主题
Snowstorm 粒子系统
Mojang Bedrock Edition 粒子的镜像实现,使用 JSON 定义粒子效果。
前置知识
需要了解 特效系统总览 和 Bedrock 粒子格式。 推荐使用 Snowstorm Editor 可视化编辑粒子。
Behemiron的暴雪粒子1:1复刻了 WinterSky(JS) 的实现,支持 100% 的粒子语法,并修复了 WinterSky 中关于粒子物理计算的一些 Bug。粒子支持批量释放,支持绑定实体跟随。
Quick Start
kotlin
import com.behemiron.engine.forge.module.effect.snowstorm.SnowstormManager
// 在指定位置播放粒子
val runtime = SnowstormManager.play("snow.json", level, position)
// 绑定到实体
val runtime = SnowstormManager.play("aura.json", entity)
// 控制运行时
runtime.position = Vec3(x, y, z) // 更新位置
runtime.active = false // 暂停发射
runtime.bindToEntity(entity) // 绑定实体
// 停止
runtime.stop()
SnowstormManager.stopAll()资源放置在 Behemiron/particles/ 目录下:
Behemiron/particles/
├── snow.json
├── fire.json
└── effects/
└── explosion.jsonAPI Reference
SnowstormManager
SnowstormManager 是 Snowstorm 系统的入口单例,实现 EffectEngine<SnowstormFX, SnowstormFXRuntime>。
| 方法 | 返回值 | 说明 |
|---|---|---|
play(path, level, position) | SnowstormFXRuntime? | 在指定位置播放粒子 |
play(path, entity) | SnowstormFXRuntime? | 播放并绑定到实体 |
stop(runtime) | Unit | 停止指定运行时 |
stopAll() | Unit | 停止所有运行时 |
preload(path, onComplete?) | Unit | 异步预加载指定粒子 |
preloadAll(onComplete?) | Unit | 异步预加载所有粒子 |
reload(path) | Unit | 重载指定粒子定义 |
reloadAll() | Unit | 重载所有粒子定义 |
getDefinition(path) | SnowstormFX? | 获取粒子定义(懒加载) |
registerDefinition(path, definition) | Unit | 注册动态构建的粒子定义 |
unregisterDefinition(path) | SnowstormFX? | 移除粒子定义 |
getActiveCount() | Int | 活跃发射器数量 |
getActiveRuntimes() | List<SnowstormFXRuntime> | 所有活跃运行时(只读) |
路径格式:相对于 Behemiron/particles/ 目录,带 .json 后缀。
kotlin
// "fire.json" -> Behemiron/particles/fire.json
// "effects/explosion.json" -> Behemiron/particles/effects/explosion.jsonSnowstormFXRuntime
SnowstormFXRuntime 是粒子发射器的运行时实例。
| 属性/方法 | 类型 | 说明 |
|---|---|---|
path | String | 粒子标识符 |
position | Vec3 | 当前位置(可读写) |
age | Double | 发射器当前年龄(秒,只读) |
lifetime | Double | 发射器总生命周期(秒) |
active | Boolean | 是否正在发射粒子 |
removed | Boolean | 是否已移除(只读) |
particles | MutableList<SnowstormParticle> | 所有活跃粒子 |
definition | SnowstormFX | 粒子定义数据 |
level | Level | Minecraft 世界 |
context | SnowstormContext | MoLang 执行上下文 |
bindToEntity(entity) | Unit | 绑定到实体(跟随位置) |
unbindEntity() | Unit | 解绑实体 |
stop() / remove() | Unit | 移除发射器及所有粒子 |
spawn() | SnowstormParticle | 手动生成一个粒子 |
spawn(position, velocity) | SnowstormParticle | 在指定位置/速度生成粒子 |
isFinished() | Boolean | 是否已完成 |
getParticleCount() | Int | 活跃粒子数量 |
getEmitterCount() | Int | 发射器数量(始终为 1) |
setEntityScale(value) | Unit | 设置实体缩放因子 |
SnowstormFX
粒子定义数据类。
| 属性 | 类型 | 说明 |
|---|---|---|
description | Description | 标识符、纹理路径、材质类型 |
curves | Map<String, CurveData> | 曲线定义(用于参数动画) |
events | Map<String, EventData> | 事件定义(声音、子粒子等) |
components | JsonObject | 组件原始 JSON(延迟解析) |
JSON 格式
Snowstorm 使用 Bedrock 粒子格式(版本 1.10.0):
json
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "snowstorm:snow",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "snow.png"
}
},
"components": {
"minecraft:emitter_rate_steady": {
"spawn_rate": 80,
"max_particles": 4000
},
"minecraft:emitter_lifetime_looping": {
"active_time": 1
},
"minecraft:emitter_shape_box": {
"offset": [0, 20, 0],
"half_dimensions": [36, 0, 36],
"direction": ["Math.random(-1, 1)", "-1.2", "Math.random(-1, 1)"]
},
"minecraft:particle_initial_speed": 1,
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, -0.2, 0]
},
"minecraft:particle_appearance_billboard": {
"size": [0.1, 0.1],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 64,
"texture_height": 64,
"uv": [0, 0],
"uv_size": [64, 64]
}
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 25
}
}
}
}组件参考
发射器生命周期
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:emitter_lifetime_looping | 循环发射 | active_time, sleep_time |
minecraft:emitter_lifetime_once | 单次发射 | active_time |
minecraft:emitter_lifetime_expression | 表达式控制 | activation_expression, expiration_expression |
发射速率
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:emitter_rate_steady | 稳定速率 | spawn_rate, max_particles |
minecraft:emitter_rate_instant | 瞬间发射 | num_particles |
发射形状
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:emitter_shape_point | 点发射 | offset, direction |
minecraft:emitter_shape_box | 立方体区域 | offset, half_dimensions, direction |
minecraft:emitter_shape_sphere | 球体区域 | offset, radius, direction |
minecraft:emitter_shape_disc | 圆盘区域 | offset, radius, plane_normal, direction |
minecraft:emitter_shape_entity_aabb | 实体包围盒 | direction |
粒子初始状态
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:particle_initial_speed | 初始速度 | 数值或 MoLang 表达式 |
minecraft:particle_initial_spin | 初始旋转 | rotation, rotation_rate |
粒子运动
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:particle_motion_dynamic | 动态运动 | linear_acceleration, linear_drag_coefficient, rotation_acceleration, rotation_drag_coefficient |
minecraft:particle_motion_parametric | 参数化运动 | relative_position, direction, rotation |
minecraft:particle_motion_collision | 碰撞检测 | enabled, collision_radius, coefficient_of_restitution, collision_drag, expire_on_contact |
粒子生命周期
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:particle_lifetime_expression | 表达式生命周期 | max_lifetime, expiration_expression |
minecraft:particle_lifetime_events | 生命周期事件 | creation_event, expiration_event, timeline |
minecraft:particle_expire_if_in_blocks | 进入方块时消亡 | 方块 ID 列表 |
minecraft:particle_expire_if_not_in_blocks | 离开方块时消亡 | 方块 ID 列表 |
minecraft:particle_kill_plane | 平面边界 | 平面方程 [A, B, C, D] |
粒子外观
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:particle_appearance_billboard | 公告板渲染 | size, facing_camera_mode, uv |
minecraft:particle_appearance_tinting | 颜色着色 | color (渐变或表达式) |
minecraft:particle_appearance_lighting | 光照 | 无参数(启用即可) |
其他发射器组件
| 组件名 | 说明 | 主要参数 |
|---|---|---|
minecraft:emitter_initialization | 发射器初始化 | creation_expression, per_update_expression |
minecraft:emitter_local_space | 本地空间 | position, rotation, velocity |
MoLang 集成
Snowstorm 使用独立的 MoLang 引擎(molang-snowstorm),包含以下命名空间:
| 命名空间 | 别名 | 说明 |
|---|---|---|
math | - | 数学函数(math.sin, math.random, math.clamp 等) |
variable | v | 粒子/发射器/曲线变量 |
temp | t | 临时变量 |
发射器变量
variable.emitter_lifetime // 发射器生命周期
variable.emitter_age // 发射器年龄
variable.emitter_random_1 // 随机数 1(创建时固定)
variable.emitter_random_2 // 随机数 2
variable.emitter_random_3 // 随机数 3
variable.emitter_random_4 // 随机数 4粒子变量
variable.particle_lifetime // 粒子生命周期
variable.particle_age // 粒子年龄
variable.particle_random_1 // 随机数 1(每个粒子不同)
variable.particle_random_2 // 随机数 2
variable.particle_random_3 // 随机数 3
variable.particle_random_4 // 随机数 4曲线变量
通过 curves 定义的曲线会自动注册为 variable.<curve_name>,在组件表达式中可直接引用。
支持的曲线类型:
| 类型 | 说明 |
|---|---|
linear | 线性插值 |
bezier | 贝塞尔曲线 |
bezier_chain | 贝塞尔链(多段) |
catmull_rom | Catmull-Rom 样条 |
事件系统
事件可以在粒子/发射器的生命周期关键点触发:
| 事件类型 | 说明 |
|---|---|
sequence | 按顺序执行多个事件 |
randomize | 按权重随机选择一个事件 |
particle_effect | 生成子粒子 |
sound_effect | 播放声音 |
expression | 执行 MoLang 表达式 |
log | 输出日志 |
混合模式
| 材质名 | 混合模式 | 效果 |
|---|---|---|
particles_alpha | Alpha 混合 | 标准透明 |
particles_add | 加法混合 | 发光效果 |
particles_blend | Alpha 混合 | 标准透明(同 alpha) |
particles_opaque | 不透明 | 完全覆盖 |
particles_multiply | 正片叠底 | 阴影效果 |
完整示例
火焰粒子
json
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "behemiron:flame",
"basic_render_parameters": {
"material": "particles_add",
"texture": "flame.png"
}
},
"curves": {
"variable.flame_size": {
"type": "bezier",
"input": "variable.particle_age / variable.particle_lifetime",
"horizontal_range": 1,
"nodes": [0, 0.8, 0.5, 0]
}
},
"components": {
"minecraft:emitter_rate_steady": {
"spawn_rate": 30,
"max_particles": 200
},
"minecraft:emitter_lifetime_looping": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"offset": [0, 0, 0],
"direction": ["Math.random(-0.3, 0.3)", 1, "Math.random(-0.3, 0.3)"]
},
"minecraft:particle_initial_speed": "Math.random(1, 3)",
"minecraft:particle_lifetime_expression": {
"max_lifetime": "Math.random(0.5, 1.5)"
},
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, 2, 0],
"linear_drag_coefficient": 1
},
"minecraft:particle_appearance_billboard": {
"size": ["variable.flame_size * 0.3", "variable.flame_size * 0.3"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 64,
"texture_height": 64,
"uv": [0, 0],
"uv_size": [64, 64]
}
},
"minecraft:particle_appearance_tinting": {
"color": {
"interpolant": "variable.particle_age / variable.particle_lifetime",
"gradient": {
"0.0": "#FFFFCC00",
"0.5": "#FFFF6600",
"1.0": "#00FF3300"
}
}
}
}
}
}Kotlin 运行时控制
kotlin
import com.behemiron.engine.forge.module.effect.snowstorm.SnowstormManager
// 预加载所有粒子
SnowstormManager.preloadAll { success, failed ->
println("预加载完成: $success 成功, $failed 失败")
}
// 播放粒子
val runtime = SnowstormManager.play("flame.json", level, Vec3(100.0, 65.0, 200.0))
?: return
// 运行时控制
runtime.active = true
println("粒子数: ${runtime.getParticleCount()}")
println("年龄: ${runtime.age}s / ${runtime.lifetime}s")
// 绑定到实体并跟随
runtime.bindToEntity(player)
// 一段时间后停止
runtime.stop()Ponder 场景中使用
kotlin
scene.effects.playSnowstormFx("flame.json", Vec3(2.0, 1.0, 2.0))