粒子
粒子是游戏中的视觉效果,用于润色并增强沉浸感。由于其创建和引用方式,使用时也需要谨慎。
原创说明图,依据 Forge 1.21.x 官方文档 绘制。
创建粒子
粒子分为用于显示粒子的仅客户端实现,以及用于引用粒子或从服务器同步数据的通用实现。
| 类 | 端 | 说明 |
|---|---|---|
ParticleType |
双端 | 粒子类型定义的注册对象,用于在任一端引用粒子。 |
ParticleOptions |
双端 | 数据容器,将网络或命令中的信息同步到关联客户端。 |
ParticleProvider |
客户端 | 按 ParticleType 注册的工厂,根据关联的 ParticleOptions 构造 Particle。 |
Particle |
客户端 | 在关联客户端显示粒子的可渲染逻辑。 |
ParticleType
ParticleType 是定义特定粒子类型的注册对象,可在两端引用该粒子。每个 ParticleType 都必须注册。
它接收两个参数:overrideLimiter 决定粒子是否不受距离限制地渲染;ParticleOptions$Deserializer 用于在客户端读取收到的 ParticleOptions。基类 ParticleType 是抽象类,需要实现 #codec,描述如何编码和解码该类型关联的 ParticleOptions。
注意:原版实现中,
ParticleType#codec仅用于生物群系 codec。
多数情况下无需向客户端发送粒子数据,此时使用 SimpleParticleType 更方便。它同时实现 ParticleType 和 ParticleOptions,除类型外不发送自定义数据。除用于着色的红石粉粒子和依赖方块或物品的粒子外,大多数原版实现都使用它。
重要:如果粒子仅在客户端引用,生成粒子不需要
ParticleType;但使用ParticleEngine中的预构建逻辑或从服务端生成粒子时需要它。
ParticleOptions
ParticleOptions 表示每个粒子接收的数据,也用于发送从服务端生成的粒子的数据。所有粒子生成方法都接收 ParticleOptions,从而得知粒子类型和生成时关联的数据。
| 方法 | 说明 |
|---|---|
getType |
获取粒子的类型定义,即 ParticleType。 |
writeToNetwork |
在服务端将粒子数据写入缓冲区以发送到客户端。 |
writeToString |
将粒子数据写入字符串。 |
这些对象要么按需创建,要么作为 SimpleParticleType 的单例存在。
ParticleOptions$Deserializer
为了在客户端接收 ParticleOptions 或在命令中引用数据,需要通过 ParticleOptions$Deserializer 反序列化粒子数据。其每个方法都与 ParticleOptions 中的编码方法对应:
| 方法 | 对应编码方法 | 说明 |
|---|---|---|
fromCommand |
writeToString |
从字符串解码粒子数据,通常来自命令。 |
fromNetwork |
writeToNetwork |
在客户端从缓冲区解码粒子数据。 |
需要发送自定义粒子数据时,将此对象传入 ParticleType 构造函数。
Particle
Particle 提供将数据绘制到屏幕上所需的渲染逻辑。创建粒子必须实现两个方法:render 将粒子渲染到屏幕;getRenderType 获取其渲染类型。
渲染纹理时常用的子类是 TextureSheetParticle。仍需实现 #getRenderType,设置的纹理精灵会在粒子位置渲染。
ParticleRenderType
ParticleRenderType 是 RenderType 的变体,为该类型的每个粒子构建开始和结束阶段,然后通过 Tesselator 一次渲染所有粒子。共有六种类型:
| 渲染类型 | 说明 |
|---|---|
TERRAIN_SHEET |
渲染纹理位于可用方块中的粒子。 |
PARTICLE_SHEET_OPAQUE |
渲染纹理不透明且位于可用粒子中的粒子。 |
PARTICLE_SHEET_TRANSLUCENT |
渲染纹理半透明且位于可用粒子中的粒子。 |
PARTICLE_SHEET_LIT |
与 PARTICLE_SHEET_OPAQUE 相同,但不使用粒子着色器。 |
CUSTOM |
提供混合和深度遮罩设置,但不提供渲染功能;渲染应在 Particle#render 中实现。 |
NO_RENDER |
粒子永不渲染。 |
自定义渲染类型的实现留给读者练习。
ParticleProvider
粒子通常由 ParticleProvider 创建。工厂的 #createParticle 方法根据粒子数据、客户端世界、位置和运动增量创建粒子。Particle 不绑定特定 ParticleType,因此可按需在不同工厂中复用。
通过订阅 Mod 事件总线上的 RegisterParticleProvidersEvent 注册 ParticleProvider。在事件中,向 #registerSpecial 传入工厂实例即可注册。
重要:
RegisterParticleProvidersEvent仅应在客户端调用,因此应将其隔离在客户端类中,并通过DistExecutor或@EventBusSubscriber引用。
ParticleDescription、SpriteSet 和 SpriteParticleRegistration
PARTICLE_SHEET_OPAQUE、PARTICLE_SHEET_TRANSLUCENT 和 PARTICLE_SHEET_LIT 不能使用上述注册方式,因为它们都使用由 ParticleEngine 直接加载的精灵集,需要用其他方式获取和注册纹理。以下假设粒子是 TextureSheetParticle 的子类,因为这是原版对该逻辑的唯一实现。
要添加纹理,需在 assets/<modid>/particles 中添加 JSON 文件,称为 ParticleDescription。文件名表示工厂所关联的 ParticleType 注册表名称。每个粒子 JSON 是对象,唯一的 textures 键保存 ResourceLocation 数组。其中 <modid>:<path> 指向 assets/<modid>/textures/particle/<path>.png。
下面保留原文中的注释以解释纹理路径;实际 JSON 文件不支持 // 注释,使用时应删除注释。
{
"textures": [
// 指向 assets/mymod/textures/particle/particle_texture.png
"mymod:particle_texture",
// 纹理应按绘制顺序排列,例如先渲染 particle_texture,
// 一段时间后再渲染 particle_texture2
"mymod:particle_texture2"
]
}
要引用纹理,TextureSheetParticle 子类应接收 SpriteSet 或从 SpriteSet 获取的 TextureAtlasSprite。SpriteSet 保存对应 ParticleDescription 中精灵的纹理列表,提供两种获取精灵的方法:第一种接收两个整数,底层实现可随粒子年龄改变纹理;第二种接收 Random 实例,随机选择纹理。可通过 TextureSheetParticle 中接收 SpriteSet 的辅助方法设置精灵:#pickSprite 随机选取,#setSpriteFromAge 根据两个整数表示的比例选取。
要注册这些纹理,向 RegisterParticleProvidersEvent#registerSpriteSet 提供 SpriteParticleRegistration。该方法接收保存粒子关联精灵集的 SpriteSet,并创建用于生成粒子的 ParticleProvider。最简单的实现方式是在类上实现 ParticleProvider,让构造函数接收 SpriteSet,再正常传给粒子。
注意:如果注册的
TextureSheetParticle子类只有一张纹理,也可以向#registerSprite提供ParticleProvider$Sprite;它的函数式接口方法与ParticleProvider基本相同。
生成粒子
两种世界实例都可以生成粒子,但方法不同。在 ClientLevel 上,调用 #addParticle 生成粒子,或调用 #addAlwaysVisibleParticle 生成任意距离都可见的粒子。在 ServerLevel 上,调用 #sendParticles 向客户端发送生成粒子的数据包。在服务器上调用两个 ClientLevel 方法不会产生效果。
原文:Particles。