粒子

粒子

粒子是游戏中的视觉效果,用于润色并增强沉浸感。由于其创建和引用方式,使用时也需要谨慎。

ParticleOptions 指定粒子类型和数据,客户端按类型注册的 ParticleProvider 根据参数创建 Particle,Particle 负责渲染。

原创说明图,依据 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。