Skip to content

Commit 8ec500c

Browse files
committed
更新readme
1 parent 568a573 commit 8ec500c

File tree

1 file changed

+219
-24
lines changed

1 file changed

+219
-24
lines changed

README.md

Lines changed: 219 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,220 @@
1+
# 基本用法
12

2-
* API创建粒子组合使用流程
3-
* 1. 创建自定义粒子: 自定义粒子需要继承 ControlableParticle
4-
* 2. 自行创建ParticleEffect 要求ParticleEffect必须包含UUID 这个UUID会在构造group时输入 并且使用Fabric API正常注册
5-
* 3. 继承 ControlableParticleGroup 并且实现粒子组合的位置关系 其中
6-
* loadParticleLocations 的ParticleRelativeData 的第一个参数要求输入 ParticleEffect构造器
7-
* 改构造器只会提供一个参数即粒子UUID 你必须使用这个参数作为该Effect的UUID否则会产生NullPointerException (获取控制器失败)
8-
* 4. 在进行ControlableParticle的Factory构建时, 要求输入的uuid来源一定是effect的uuid 并且effect遵从上面的要求
9-
* 5. 在ClientParticleGroupManager注册该ControlableParticleGroup 并且提供ControlableParticleGroup的构建器
10-
* @see ClientParticleGroupManager.register(你的客户端粒子组class, 构建器构造器)
11-
* 构建器构造器lambda要求返回 ControlableParticleGroupProvider 类型 这个类型的createGroup方法的第一个参数是该group的UUID
12-
* 在new 你的Group时 一定要将该uuid作为参数输入0
13-
* 第二个参数是ServerParticleGroup 传输给客户端的参数 用于同步服务器信息
14-
* 6. 创建一个对标的ServerParticleGroup 并且重写tick / otherPacketArgs 方法
15-
* otherPacketArgs方法的返回结果会作为参数传输到客户端ControlableParticleGroup类中
16-
* 创建后使用 ServerParticleGroupManager 添加该粒子组合
17-
* 此时客户端就可以同步收到该粒子组合并且按照ControlableParticleGroup设定的tick方法变化
18-
*
19-
* 需要继承的类
20-
* @see ControlableParticleGroup 客户端渲染粒子行为
21-
* @see ControlableParticleGroupProvider 服务端发送数据包后 解析成ControlableParticleGroup的构造器
22-
* @see ServerParticleGroup 用于同步给其他客户端的服务器粒子组控制器对象
23-
* @see ControlableParticle 能够被控制的粒子 (原版addParticle是不提供粒子对象的)
24-
* @see ParticleEffect 对应所需要的粒子效果
25-
*
3+
创建一个粒子类并且继承 ControlableParticle
4+
5+
### Example Particle
6+
7+
```kotlin
8+
class TestEndRodParticle(
9+
// Particle粒子需要的参数
10+
world: ClientWorld,
11+
pos: Vec3d,
12+
velocity: Vec3d,
13+
// 用于获取ParticleControler的粒子唯一标识符
14+
controlUUID: UUID,
15+
val provider: SpriteProvider
16+
) :
17+
// 必须继承 ControlableParticle类
18+
ControlableParticle(world, pos, velocity, controlUUID) {
19+
override fun getType(): ParticleTextureSheet {
20+
return ParticleTextureSheet.PARTICLE_SHEET_OPAQUE
21+
}
22+
23+
init {
24+
setSprite(provider.getSprite(0, 120))
25+
// 由于ControlableParticle 禁止重写 tick方法
26+
// 使用此方法代替
27+
controler.addPreTickAction {
28+
setSpriteForAge(provider)
29+
}
30+
}
31+
32+
// 基本粒子注册
33+
class Factory(val provider: SpriteProvider) : ParticleFactory<TestEndRodEffect> {
34+
override fun createParticle(
35+
parameters: TestEndRodEffect,
36+
world: ClientWorld,
37+
x: Double,
38+
y: Double,
39+
z: Double,
40+
velocityX: Double,
41+
velocityY: Double,
42+
velocityZ: Double
43+
): Particle {
44+
return TestEndRodParticle(
45+
world,
46+
Vec3d(x, y, z),
47+
Vec3d(velocityX, velocityY, velocityZ),
48+
parameters.controlUUID,
49+
provider
50+
)
51+
}
52+
}
53+
}
54+
```
55+
56+
为了能够获取到对应的 UUID 所以你的ParticleEffect也要有uuid
57+
58+
```kotlin
59+
// 作为构造参数
60+
class TestEndRodEffect(val controlUUID: UUID) : ParticleEffect {
61+
companion object {
62+
@JvmStatic
63+
val codec: MapCodec<TestEndRodEffect> = RecordCodecBuilder.mapCodec {
64+
return@mapCodec it.group(
65+
Codec.BYTE_BUFFER.fieldOf("uuid").forGetter { effect ->
66+
val toString = effect.controlUUID.toString()
67+
val buffer = Unpooled.buffer()
68+
buffer.writeBytes(toString.toByteArray())
69+
buffer.nioBuffer()
70+
}
71+
).apply(it) { buf ->
72+
TestEndRodEffect(
73+
UUID.fromString(
74+
String(buf.array())
75+
)
76+
)
77+
}
78+
}
79+
80+
@JvmStatic
81+
val packetCode: PacketCodec<RegistryByteBuf, TestEndRodEffect> = PacketCodec.of(
82+
{ effect, buf ->
83+
buf.writeUuid(effect.controlUUID)
84+
}, {
85+
TestEndRodEffect(it.readUuid())
86+
}
87+
)
88+
89+
}
90+
91+
override fun getType(): ParticleType<*> {
92+
return ModParticles.testEndRod
93+
}
94+
}
95+
```
96+
97+
使用Fabric API 在客户端处注册此粒子后
98+
接下来进行粒子组合 (ControlableParticleGroup) 的构建
99+
100+
### 构建 ControlableParticleGroup
101+
102+
ControlableParticleGroup的作用是在玩家客户端处渲染粒子组合
103+
104+
构建一个基本的ControlableParticleGroup代码示例:
105+
一个在玩家视野正中心 每tick旋转10度的魔法阵
106+
107+
```kotlin
108+
class TestGroupClient(uuid: UUID, val bindPlayer: UUID) : ControlableParticleGroup(uuid) {
109+
110+
// 为了让服务器能够正常的将ParticleGroup数据转发给每一个玩家
111+
// 服务器会发 PacketParticleGroupS2C 数据包
112+
// 这里是解码
113+
class Provider : ControlableParticleGroupProvider {
114+
override fun createGroup(
115+
uuid: UUID,
116+
// 这里的 args是 服务器同步给客户端用的参数
117+
// 可以查看 cn.coostack.network.packet.PacketParticleGroupS2C 类注释的字段不建议覆盖也无需处理(已经处理好了)
118+
args: Map<String, ParticleControlerDataBuffer<*>>
119+
): ControlableParticleGroup {
120+
// 绑定到的玩家
121+
val bindUUID = args["bindUUID"]!!.loadedValue as UUID
122+
return TestGroupClient(uuid, bindUUID)
123+
}
124+
}
125+
126+
// 魔法阵粒子组合
127+
override fun loadParticleLocations(): Map<RelativeLocation, ParticleRelativeData> {
128+
// 在XZ平面的魔法阵
129+
val list = Math3DUtil.getCycloidGraphic(3.0, 5.0, 2, -3, 360, 0.2).onEach { it.y += 6 }
130+
//
131+
return list.associateWith {
132+
withEffect({
133+
// 提供ParticleEffect (在display方法中 world.addParticle)使用
134+
// it类型为UUID
135+
TestEndRodEffect(it)
136+
}) {
137+
// this is ControlableParticle
138+
// 用于初始化粒子信息
139+
color = Vector3f(230 / 255f, 130 / 255f, 60 / 255f)
140+
this.maxAliveTick = this.maxAliveTick
141+
}
142+
}
143+
}
144+
145+
146+
/**
147+
* 当粒子第一次渲染在玩家视角的时候
148+
* 玩家超出渲染范围后又回归渲染范围任然会调用一次
149+
* 可以理解为粒子组初始化
150+
*/
151+
override fun onGroupDisplay() {
152+
MinecraftClient.getInstance().player?.sendMessage(Text.of("发送粒子: ${this::class.java.name} 成功"))
153+
addPreTickAction {
154+
// 当玩家能够看到粒子的时候 (这个类会被构造)
155+
val bindPlayerEntity = world!!.getPlayerByUuid(bindPlayer) ?: let {
156+
return@addPreTickAction
157+
}
158+
teleportGroupTo(bindPlayerEntity.eyePos)
159+
rotateToWithAngle(
160+
RelativeLocation.of(bindPlayerEntity.rotationVector),
161+
Math.toRadians(10.0)
162+
)
163+
}
164+
}
165+
}
166+
```
167+
168+
创建好ControlableParticleGroup后, 需要在客户端进行注册
169+
170+
```kotlin
171+
ClientParticleGroupManager.register(
172+
TestGroupClient::class.java, TestGroupClient.Provider()
173+
)
174+
```
175+
176+
当你完成上述操作后, 为了让其他玩家也能同步操作, 需要设置一个服务器向的ControlableParticleGroup
177+
示例:
178+
179+
```kotlin
180+
/**
181+
* 构造参数无要求
182+
*/
183+
class TestParticleGroup(private val bindPlayer: ServerPlayerEntity) :
184+
// 第一个参数是 ParticleGroup的唯一标识符
185+
// 这个内容会同步到客户端
186+
// 第二个参数是粒子的可见范围
187+
// 当玩家超出这个范围时会发送删除粒子组包(对该玩家不可见)
188+
ServerParticleGroup(UUID.randomUUID(), 16.0) {
189+
override fun tick() {
190+
withPlayerStats(bindPlayer)
191+
setPosOnServer(bindPlayer.eyePos)
192+
}
193+
194+
/**
195+
* 这个是你想发送给客户端用于构建ControlableParticleGroup的参数
196+
* 最终会传入 ControlableParticleGroupProvider.createGroup()
197+
*/
198+
override fun otherPacketArgs(): Map<String, ParticleControlerDataBuffer<out Any>> {
199+
return mapOf(
200+
"bindUUID" to ParticleControlerDataBuffers.uuid(bindPlayer.uuid)
201+
)
202+
}
203+
}
204+
```
205+
206+
完成上述构建后,只需要在服务器中添加粒子
207+
208+
```kotlin
209+
val serverGroup = TestParticleGroup(user as ServerPlayerEntity)
210+
ServerParticleGroupManager.addParticleGroup(
211+
TestGroupClient::class.java,
212+
// world必须是ServerWorld
213+
serverGroup, user.pos, world as ServerWorld
214+
)
215+
```
216+
217+
其余特殊用法可以查看
218+
cn.coostack.particles.control.group.ControlableParticleGroup 与
219+
220+
cn.coostack.network.particle.ServerParticleGroup

0 commit comments

Comments
 (0)