粒子组件描述的是每一个粒子:它如何开始、如何移动、长什么样,以及何时消失。它们放在粒子文件的 components 块中,旁边是负责生成粒子的发射器组件。
刚接触粒子?你的第一个粒子从零开始做了一个红宝石闪光效果,用到了下面大部分组件。
每个粒子都需要什么
实际上,每个效果都需要 particle_lifetime_expression(否则粒子永远不会消失)和 particle_appearance_billboard(否则什么都看不见)。其他组件都是可选的,可以随意组合。
数字字段可以填普通数字,也可以填 Molang 字符串。在 Molang 里,你可以使用 variable.particle_age、variable.particle_lifetime 以及 variable.particle_random_1 到 _4,让每个粒子都有自己的个性。
初始状态
这些在粒子诞生时设置一次。
| 组件 | 作用 | 主要字段 |
|---|---|---|
minecraft:particle_initial_speed | 粒子开始移动时有多快 | 一个数字(每秒格数,沿发射器形状给出的方向),或者一个 [x, y, z] 速度 |
minecraft:particle_initial_spin | 图片的初始角度以及旋转的快慢 | rotation:角度(默认 0)。rotation_rate:每秒旋转的角度(默认 0) |
minecraft:particle_initialization | 为每个粒子运行你自己的 Molang,设置其他字段要读取的变量 | per_update_expression:每次更新时运行。per_render_expression:每次绘制时运行 |
速度
"minecraft:particle_initial_speed": "math.random(1, 2)"只写一个数字时,粒子会朝发射器形状给出的方向飞出。写三个数字时,由你自己设定速度,比如 [0, 2, 0] 表示笔直向上。不写这个组件的话,粒子一开始是静止的。
旋转
"minecraft:particle_initial_spin": {
"rotation": "math.random(0, 360)",
"rotation_rate": "math.random(-90, 90)"
}旋转会让平面图片像车轮一样绕中心转动。随机的初始角度是让一大群粒子看起来不像复制品的最简单方法。
你自己的变量
"minecraft:particle_initialization": {
"per_render_expression": "variable.twinkle = math.sin(variable.particle_age * 720) * 0.5 + 0.5;"
}现在 variable.twinkle 每秒在 0 和 1 之间来回摆动两次,你可以用它来控制大小或颜色。如果想让某个值在整个生命周期中按某种形状变化,使用曲线通常更简单。
运动
这些组件在粒子诞生后的每一帧移动它。
| 组件 | 作用 | 主要字段 |
|---|---|---|
minecraft:particle_motion_dynamic | 带有力的真实运动:重力、风、空气阻力、旋转加速 | linear_acceleration、linear_drag_coefficient、rotation_acceleration、rotation_drag_coefficient |
minecraft:particle_motion_parametric | 用 Molang 精确设置每一帧的位置,忽略所有的力 | relative_position、direction、rotation |
minecraft:particle_motion_collision | 让粒子能撞到方块:弹起、滑行、停下或消失 | enabled、collision_radius、coefficient_of_restitution、collision_drag、expire_on_contact、events |
动态运动
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, -9.8, 0],
"linear_drag_coefficient": 0.5,
"rotation_acceleration": 0,
"rotation_drag_coefficient": 0
}| 字段 | 作用 |
|---|---|
linear_acceleration | 每秒施加的推力,写成 [x, y, z],单位是格每二次方秒。[0, -9.8, 0] 感觉像是在下落。y 为正数时会像烟一样往上飘 |
linear_drag_coefficient | 空气阻力。粒子越快,减速越厉害。0 表示没有阻力 |
rotation_acceleration | 让旋转加速(负值则减速),单位是度每二次方秒 |
rotation_drag_coefficient | 让旋转慢下来,这样旋转的粒子最终会稳定住 |
动态运动作用于粒子的速度,所以它可以和 particle_initial_speed 配合:一开始很快,然后交给重力和阻力接管。
参数化运动
"minecraft:particle_motion_parametric": {
"relative_position": [
"math.cos(variable.particle_age * 360) * 0.8",
"variable.particle_age",
"math.sin(variable.particle_age * 360) * 0.8"
]
}这里你跳过物理计算,直接说明粒子在哪里,以发射器为原点来测量。这个例子每秒绕一圈,同时每秒上升一格:形成一道红宝石螺旋。direction 和 rotation 也可以用同样的方式设置。
参数化运动需要普通发射器
参数化运动对 emitter_rate_manual 生成的粒子无效。对于附着在生物身上的效果,只有当发射器的位置使用局部空间时它才有效。
碰撞
"minecraft:particle_motion_collision": {
"collision_radius": 0.1,
"coefficient_of_restitution": 0.4,
"collision_drag": 2,
"events": { "event": "pop", "min_speed": 2 }
}没有这个组件的话,粒子会直接穿过方块。
| 字段 | 作用 |
|---|---|
enabled | 开启或关闭碰撞。可以是 Molang,每帧检查一次。默认开启 |
collision_radius | 粒子在碰撞方块时有多大。必须不超过半格。大致与广告牌的大小保持一致 |
coefficient_of_restitution | 弹性。0 会直接停住,1 会弹回同样的高度,介于两者之间时每次弹跳都会损失一些高度 |
collision_drag | 粒子接触表面时的减速,单位是每秒格数。让它滑行一段后停下 |
expire_on_contact | 设为 true 时,粒子一碰到方块就消失。很适合做雨滴 |
events | 撞到东西时触发你的某个事件,比如溅起水花。可以是一个事件对象,也可以是一个列表。min_speed(至少每秒 2 格)会忽略轻微的触碰 |
碰撞会消耗性能
为每个粒子检测方块对游戏来说是不小的工作量。只在能看出区别的地方添加碰撞,并且让这些效果的 max_particles 保持在较低水平。
外观
这些组件决定粒子在屏幕上的样子。
| 组件 | 作用 | 主要字段 |
|---|---|---|
minecraft:particle_appearance_billboard | 把粒子绘制成一张平面图片,并决定它的大小、朝向以及显示纹理的哪一部分 | size、facing_camera_mode、direction、uv |
minecraft:particle_appearance_tinting | 给图片着色,可以是单一颜色,也可以是随时间变化的渐变 | color |
minecraft:particle_appearance_lighting | 像方块一样,让粒子在暗处变暗、在亮处变亮 | 无:写 {} 即可 |
广告牌
"minecraft:particle_appearance_billboard": {
"size": [0.1, 0.1],
"facing_camera_mode": "lookat_xyz",
"uv": {
"texture_width": 32,
"texture_height": 8,
"uv": [0, 0],
"uv_size": [8, 8]
}
}size 是 [宽度, 高度],每帧都会检查一次,所以在这里写 Molang 可以让粒子变大或变小。每个值都是从中心开始测量的,所以 0.5 会形成一个一格宽的正方形。
facing_camera_mode 决定平面图片朝向哪里:
| 模式 | 图片 |
|---|---|
lookat_xyz | 始终面向相机,但保持竖直。最常用的选择 |
rotate_xyz | 平贴在屏幕上,随相机一起转动 |
lookat_y / rotate_y | 面向相机,但只左右转动,就像立在地上的纸板人 |
lookat_direction | 面向相机,同时与粒子的方向对齐。适合做拖尾 |
direction_x / direction_y / direction_z | 让图片的 x 边、y 边或正面沿粒子的方向 |
emitter_transform_xy / _xz / _yz | 平躺在发射器的某个平面上。_xz 可以在地面上做出平贴的贴花 |
方向类模式需要知道粒子指向哪里。用 direction 来设置:
"direction": {
"mode": "derive_from_velocity",
"min_speed_threshold": 0.01
}derive_from_velocity 使用粒子的运动方向,但只在它的速度高于 min_speed_threshold 时生效。如果想自己设置,就用 "mode": "custom_direction" 配合 "custom_direction": [x, y, z]。
uv 选择要显示纹理的哪一部分。texture_width 和 texture_height 是你的 PNG 的像素尺寸,uv 是左上角,uv_size 是你想要的那部分的宽和高。上面的例子显示的是一条 32x8 图片带中的第一个 8x8 帧。
翻页动画
把几帧图片并排放在一起,广告牌就能像翻页书一样播放它们。把 uv 和 uv_size 换成 flipbook:
"uv": {
"texture_width": 32,
"texture_height": 8,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [8, 8],
"step_UV": [8, 0],
"max_frame": 4,
"stretch_to_lifetime": true,
"loop": false
}
}| 字段 | 作用 |
|---|---|
base_UV | 第一帧的左上角 |
size_UV | 一帧的宽和高 |
step_UV | 移到下一帧要移动多远。[8, 0] 表示向右移 |
max_frame | 一共有多少帧(第一帧是第 1 帧) |
frames_per_second | 播放速度 |
stretch_to_lifetime | 设为 true 时会忽略速度,在粒子的整个生命周期内把所有帧恰好播放一遍 |
loop | 设为 true 时,播完最后一帧后从第一帧重新开始 |
一个 4 帧的红宝石闪光,先张开、再闪耀、然后合上,比单张图片要生动得多。
着色
"minecraft:particle_appearance_tinting": {
"color": [1, 0.15, 0.3, 1]
}颜色会和纹理相乘,所以粒子纹理要画成白色或灰色,再让着色来添加颜色。颜色可以用以下任意一种方式书写:
| 形式 | 示例 |
|---|---|
[r, g, b] 或 [r, g, b, a],每个值 0 到 1 | [1, 0.15, 0.3, 1] |
"#RRGGBB" | "#FF2650" |
"#AARRGGBB"(alpha 在前) | "#80FF2650" 是半透明的 |
| 每个位置写 Molang | ["variable.tint.r", "variable.tint.g", "variable.tint.b", 1] |
要让颜色随时间变化,就使用渐变。各个颜色会均匀分布,interpolant 决定粒子此刻处于渐变上的哪个位置:
"minecraft:particle_appearance_tinting": {
"color": {
"gradient": {
"0.0": "#FFFFFFFF",
"0.3": "#FFFF2650",
"1.0": "#00600018"
},
"interpolant": "variable.particle_age / variable.particle_lifetime"
}
}像这样写成对象时,每个颜色都位于你指定的位置。如果写成普通列表,颜色则会从 0 到 1 均匀分布。这个闪光效果先闪一下白光,很快变成红宝石红,然后慢慢淡出。
透明度需要合适的材质
半透明颜色只有在使用 particles_blend 或 particles_add 材质时才会显示。使用 particles_alpha 时,像素要么完全显示,要么完全隐藏。
光照
"minecraft:particle_appearance_lighting": {}加上它之后,粒子在洞穴里和夜晚会显得更暗,从而与世界融为一体。对于应该在黑暗中发光的东西,比如魔法、火焰以及我们的红宝石闪光,就不要加它。
寿命
这些组件决定粒子何时消失。
| 组件 | 作用 | 主要字段 |
|---|---|---|
minecraft:particle_lifetime_expression | 主要的寿命设置:固定时间,或每帧检查一次的 Molang 条件 | max_lifetime、expiration_expression |
minecraft:particle_lifetime_events | 在每个粒子生命中的某些时刻触发你的事件 | creation_event、expiration_event、timeline |
minecraft:particle_kill_plane | 移除穿过一个看不见的平面的粒子 | [A, B, C, D] |
minecraft:particle_expire_if_in_blocks | 移除位于列出的任意方块内部的粒子 | 方块名称列表 |
minecraft:particle_expire_if_not_in_blocks | 移除离开列出的方块的粒子 | 方块名称列表 |
寿命表达式
"minecraft:particle_lifetime_expression": {
"max_lifetime": "math.random(0.8, 1.5)"
}max_lifetime是以秒为单位的寿命,在粒子诞生时确定一次。expiration_expression每帧检查一次。当它不为 0 时,粒子就会消失。例如"variable.particle_age > 1 && query.is_in_water"可以让粒子提前结束。
两者可以同时使用:哪个先满足,粒子就在那时消失。
寿命事件
"minecraft:particle_lifetime_events": {
"creation_event": "flash",
"expiration_event": "pop",
"timeline": {
"0.25": "twinkle"
}
}这些名称指向同一个文件中的 events 块,每个字段可以填一个名称或一个列表。和发射器版本不同,这些事件会为每一个粒子单独触发,所以一次爆发 12 个闪光就会触发 12 次 pop。事件能做什么,请参见事件。
消除平面
"minecraft:particle_kill_plane": [0, 1, 0, 0.5]这四个数字用数学方式描述一个平面:所有满足 A*x + B*y + C*z + D = 0 的点,以发射器为原点测量,但与世界坐标轴对齐。穿过它的粒子会消失。[0, 1, 0, 0.5] 是位于发射器下方半格处的一块地面。这是一种不用碰撞就能拦住下落粒子的低成本方法。
在方块内与不在方块内
"minecraft:particle_expire_if_not_in_blocks": [
"minecraft:water",
"minecraft:flowing_water",
"minecraft:bubble_column"
]气泡到达水面时破裂就是这样实现的:它们只有在水里时才存活。相反的情况就用 particle_expire_if_in_blocks,比如灰尘一飘进水里就消失。两者都可以与 particle_lifetime_expression 一起使用。
一个完整的粒子
把四类组件放在一起。这是一块下落的红宝石碎片的粒子部分,它会弹跳并逐渐淡出:
"minecraft:particle_initial_speed": "math.random(2, 3)",
"minecraft:particle_initial_spin": {
"rotation": "math.random(0, 360)",
"rotation_rate": 180
},
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, -9.8, 0],
"rotation_drag_coefficient": 1
},
"minecraft:particle_motion_collision": {
"collision_radius": 0.05,
"coefficient_of_restitution": 0.3,
"collision_drag": 4
},
"minecraft:particle_appearance_billboard": {
"size": [0.06, 0.06],
"facing_camera_mode": "lookat_xyz",
"uv": {
"texture_width": 8,
"texture_height": 8,
"uv": [0, 0],
"uv_size": [8, 8]
}
},
"minecraft:particle_appearance_tinting": {
"color": "#FFE0204A"
},
"minecraft:particle_appearance_lighting": {},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 2
}再从发射器组件中给它配一个发射器,比如从一个小球体中一次爆发 8 个,就能做出很棒的方块破坏效果。