|
| 1 | +# 基本用法 |
1 | 2 |
|
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