本篇教程由作者设定使用 CC BY-NC 协议。
看前提示:
本教程的一、二、三章改自ColorBlock模组的教程:https://www.mcbbs.net/thread-917845-1-1.html(已失效,懂得都懂),并扩展了亿些内容。
教程会不断更新迭代,添加更多的新内容和实例,更改纠正不合理处,随时都会更改,所以没有最终定稿版。后续或许会有稳定版。
一、命令和参数
所有命令均以 /particlex 为根,需要权限等级 2(OP)。命令采用树状结构,参数按顺序依次指定。
<> 为必选参数,[] 为可选参数。
紫色括号()内的为 参数的 数据类型、范围、默认值。
通用参数说明:
<粒子>:粒子类型。格式:粒子的命名空间ID。(ParticleOptions)
<坐标>:中心点坐标。格式:<x> <y> <z>,支持绝对、相对、局部坐标。(Vec3)
<颜色>:四分量颜色。格式:<红> <绿> <蓝> <透明度>。(Vector4f,0.0~1.0)
<速度>:初始速度向量。格式:<vx> <vy> <vz>。(Vec3)
[寿命]:粒子生命周期。单位:游戏刻。特殊值:0 按原版进行处理,-1 表示无限。(int,大于等于 -1,默认 0)
[速度表达式]:控制粒子生成后的运动的数学表达式。执行:每刻执行一次,次数等于粒子的 [寿命]。(String,默认 null,即 不启用)
[速度步长]:<速度表达式>中 t 的每刻递增值。(double,默认 1.0)
[组]:将粒子加入指定组。可用 | 分隔多个组名。后续可通过 group 命令管理。(String,默认 null)
1、普通粒子生成
最基础的粒子生成模式,用于在指定中心点周围随机生成一定数量的粒子。每个粒子的位置在给定范围内服从高斯(正态)分布,并可以设置颜色、速度、生命周期以及后续的运动表达式。
/particlex normal <粒子> <坐标> <颜色> <速度> <范围> <数量> [寿命] [速度表达式] [速度步长] [组]
<范围>:生成范围。格式:<dx> <dy> <dz>。粒子将在中心点加减此范围内随机分布(服从高斯分布/正态分布)。(Vec3,大于等于 0.0)
[数量]:生成的粒子数量。(int,大于等于 0 的 整数,默认 1)
2、条件粒子生成
一种基于数学表达式筛选粒子生成位置的功能。它允许你在一个三维空间范围内,按照指定步长采样每个点,只有当用户定义的布尔表达式在该点计算结果为真(非零)时,才会生成一个粒子。
/particlex conditional <粒子> <坐标> <颜色> <速度> <采样范围> <条件表达式> [采样步长] [寿命] [速度表达式] [速度步长] [组]
<采样范围>:遍历区域的范围。格式:<dx> <dy> <dz>。(Vec3,大于等于 0.0)
<条件表达式>:返回非零值(真)时在该点生成粒子。执行:单刻执行多次,次数等于等于遍历区域内的候选点数量。(String)
[采样步长]:遍历区域的步长。粒子会在 -<范围> 到 +<范围> 范围内以该步长遍历,表达式为 真 时生成。(double,大于 0.0,默认 0.1)
3、参数化粒子生成
参数化粒子生成是本模组最强大、最灵活的核心功能。它允许你通过数学表达式精确控制每个粒子的位置、颜色和运动轨迹。能够创建出各种复杂的图形和动画。
无 tick- 前缀的粒子 会在执行指令的同时全部生成,有 tick- 前缀的粒子 会逐刻生成(可做动画效果)。
无 rgba- 前缀的粒子 颜色在指令的参数 <颜色> 中指定,有 rgba- 前缀的粒子 颜色在 <参数表达式> 中指定(可随"t"变化)。
在直角坐标系(笛卡尔坐标系)中:
/particlex parameter <粒子> <坐标> <颜色> <速度> <起始> <结束> <参数表达式> [步长] [寿命] [速度表达式] [速度步长] [组]
/particlex tick-parameter <粒子> <坐标> <颜色> <速度> <起始> <结束> <参数表达式> [步长] [每刻迭代次数] [寿命] [速度表达式] [速度步长] [组]
/particlex rgba-parameter <粒子> <坐标> <速度> <起始> <结束> <参数表达式> [步长] [寿命] [速度表达式] [速度步长] [组]
/particlex rgba-tick-parameter <粒子> <坐标> <速度> <起始> <结束> <参数表达式> [步长] [每刻迭代次数] [寿命] [速度表达式] [速度步长] [组]
在极坐标系(球面坐标系)中:
/particlex polar-parameter <粒子> <坐标> <颜色> <速度> <起始> <结束> <参数表达式> [步长] [寿命] [速度表达式] [速度步长] [组]
/particlex tick-polar-parameter <粒子> <坐标> <颜色> <速度> <起始> <结束> <参数表达式> [步长] [每刻迭代次数] [寿命] [速度表达式] [速度步长] [组]
/particlex rgba-polar-parameter <粒子> <坐标> <速度> <起始> <结束> <参数表达式> [步长] [寿命] [速度表达式] [速度计算间隔] [组]
/particlex rgba-tick-polar-parameter <粒子> <坐标> <速度> <起始> <结束> <参数表达式> [步长] [每刻迭代次数] [寿命] [速度表达式] [速度步长] [组]
<起始>、<结束>:参数变量 t 的遍历范围。(double,<起始> 小于等于 <结束>)
<参数表达式>:每步计算的表达式。执行:无 tick- 前缀:单刻执行多次,次数等于从 <起始> 到 <结束> 以 <步长> 迭代的总步数;有 tick- 前缀: 每刻执行 [每刻迭代次数] 次。(String)
[步长]:每次执行<参数表达式>时 t 的递增值。(double,大于 0.0,默认 0.1)
[每刻迭代次数]:每游戏刻内执行<参数表达式>的次数。(int,大于 0,默认 10)
4、根据图片、视频生成粒子
将图片和视频转换为由粒子构成的动态视觉效果。
-matrix后缀 为矩阵变换模式。
若客户端缺少 JavaCV 库,视频功能将被禁用并提示下载。(存放在.javacv)
图像模式:将图片的每个像素转换为粒子。支持:.png、.jpg、.gif(只有第一帧)等。
/particlex image <粒子> <坐标> <图片路径> [缩放] [x旋转] [y旋转] [z旋转] [翻转] [粒子间距] [速度] [寿命] [速度表达式] [速度步长] [组]
/particlex image-matrix <粒子> <坐标> <path> [缩放] [矩阵] [粒子间距] [速度] [寿命] [速度表达式] [速度步长] [组]
视频模式:将视频逐帧转换为粒子动画(需 JavaCV 支持)。支持:.mp4、.mkv、.gif 等。
/particlex video <粒子> <坐标> <视频路径> [比例] [x旋转] [y旋转] [z旋转] [翻转] [粒子间距] [速度] [寿命] [速度表达式] [速度步长] [组]
/particlex video-matrix <粒子> <坐标> <视频路径> [比例] [矩阵] [粒子间距] [速度] [寿命] [速度表达式] [速度步长] [组]
<图片路径>:在 ./particleImages 目录下的文件名。(String)
<视频路径>:在 ./particleVideos 目录下的文件名。(String)
[缩放]:缩放倍数。(double,大于等于 0.0。默认:0.1)
[X旋转]、[Y旋转]、[Z旋转]:绕 X、Y、Z 轴旋转的角度。(int,90 的整数倍,用 0, 90, 180, 270 表示,默认 0、0、0)
[翻转]:控制图像或视频的镜像翻转。可选:not(无翻转)、horizontally(水平翻转)、vertical(垂直翻转)。(enum,默认 not)
[矩阵]:4x4 变换矩阵。自定义矩阵格式:"(a11,a12,a13,a14,,a21,a22,a23,a24,,a31,a32,a33,a34,,a41,a42,a43,a44)"。预定义:E3(3x3单位矩阵)、E4(4x4单位矩阵)。(String,默认 E3)
[粒子间距]:每个像素对应粒子的密度。值越大,粒子间距越小。(double,大于0.0,默认 10.0)
5、组管理
用于批量控制粒子的重要功能。通过为粒子指定组名,可以将大量粒子归入逻辑组,然后使用专门的组管理命令一次性移除或修改这些粒子的行为,而无需重新生成。在创建复杂特效、动画和交互时非常有用。
移除组内粒子:
/particlex group remove <组> [组移除条件表达式] [组坐标]
修改组内位置表达式的属性:
/particlex group change parameter <组> <组参数表达式> [组改变条件表达式] [组坐标]
修改组内速度表达式的属性:
/particlex group change speedexpression <组> <速度表达式> [组改变条件表达式] [组坐标]
[组移除条件表达式]:只有满足条件(返回非零值)的粒子会被移除。执行:单刻执行多次,次数为组内“所有”粒子数(包括寿命结束的粒子)。(String,默认 null)
<组参数表达式>:新的参数表达式。执行:单刻执行多次,次数为组内“所有”粒子数。(String)
<速度表达式>:新的速度表达式。执行:每刻执行一次,次数为组内粒子的剩余寿命。注:因和 [速度表达式] 一模一样,所以下文将两者统称为 [<速度表达式>]。(String)
[组改变条件表达式]:只有满足条件的粒子会被修改。执行:单刻执行多次,次数为组内“所有”粒子数。(String,默认 null)
[组坐标]:指定参考点。格式:<x> <y> <z>,支持绝对、相对、局部坐标。用于 [组移除条件表达式]、[组改变条件表达式] 中的相对坐标计算。(Vec3,默认 null)
6、辅助命令
不直接生成粒子,但提供工具和辅助功能的一组命令。它们主要用于信息查询、全局清理和性能优化。
列出可用数学函数:显示所有可在表达式中使用的数学函数
/particlex functions
清除所有粒子:立即清除当前维度所有生成的粒子,并清空所有粒子组。
/particlex clear-particle
清除图像缓存:清除图片缓存,释放内存。
/particlex clear-cache
二、表达式可用符号
书写位置:<条件表达式>、<参数表达式>、[<速度表达式>]、<组参数表达式>、[组改变条件表达式]、[组移除条件表达式]。
1、变量
用于控制粒子的位置、颜色、速度等属性。所有变量的值均为 double 类型。
(1)介绍:
| 变量名 | 描述 | 补充 | 默认值 |
|---|---|---|---|
| t | 参数模式下的迭代变量,或自定义运动的时间累积 | <参数表达式> 中:<begin> 开始,<end> 结束,[步长] 每次递增值。 [速度表达式] 中:0.0 开始,清除粒子时 结束,[速度步长] 每次递增值 | 0.0 |
| x、y、z | 粒子相对于中心点的 笛卡尔偏移量 | 极坐标模式下由 s1、s2、dis 转换而来 | 0.0 |
| s1、s2、dis | 粒子相对于中心点的 球坐标偏移量 | 方位角、俯仰角、距离, 其中 角 为 弧度制 | 0.0 |
| vx、vy、vz | 速度分量 | 0.0 | |
cr、cg、cb、 alpha | 红、绿、蓝 颜色分量 和 透明度 | 范围 0.0~1.0,可超范围。 | 1.0 |
| cx、cy、cz | 中心点坐标 | 通常为命令中指定的 <坐标> | 0.0 |
| dx、dy、dz | 粒子相对于中心点的 笛卡尔偏移量的 只读副本 | 每帧开始时,它们被设置为与 x, y, z 相同的值 | 0.0 |
| ds1、ds2、ddis | 粒子相对于中心点的 球坐标偏移量的 只读副本 | 每帧与 s1, s2, dis 同步 | 0.0 |
| age | 粒子年龄 | 粒子已存在的 刻数 | 0.0 |
| destroy | 销毁粒子 | 若设为 非零值(destory != 0.0),粒子将在 下一 tick 被移除 | 0.0 |
| PI | 圆周率 π | 3.141592653589793 | 0.0 |
| E | 自然常数 e | 2.718281828459045 | 0.0 |
| 自定义变量 | 通过赋值语句可以创建任意的自定义变量, 可为整数/浮点数变量、矩阵变量。 | 允许字符:字母(a-z、A-Z)、数字(0-9)、下划线(_)。 数字开头会被忽略。大小写敏感。 |
内置变量:上表除了 自定义变量 外的变量,可以 跨刻读取和修改。
自定义变量:通过赋值引入,仅用于临时计算,每次表达式执行时都会重新初始化。
定义变量后,可在后续表达式中直接引用变量。名:"radius=5;x=radius*cos(angle);y=radius*sin(angle)"。
变量可以被重新赋值:"a=0; a=a+1"。
(2)内置变量分类:
| 位置 | 因变量 | 自变量 | 无效变量 | 常量 |
|---|---|---|---|---|
<条件表达式> | x, y, z s1, s2, dis | t vx, vy, vz cx, cy, cz dx, dy, dz ds1, ds2, ddis age, destroy | PI E | |
<参数表达式> | 直角坐标系:x, y, z 极坐标系:s1, s2, dis rgba-命令:cr, cg, cb, alpha | t | vx, vy, vz cx, cy, cz dx, dy, dz ds1, ds2, ddis age, destroy | |
[<速度表达式>] | vx, vy, vz cr, cg, cb, alpha destroy | t x, y, z s1, s2, dis cr, cg, cb, alpha cx, cy, cz dx, dy, dz ds1, ds2, ddis | age | |
<组参数表达式> | x, y, z vx, vy, vz cr, cg, cb, alpha cx, cy, cz | x, y, z vx, vy, vz cr, cg, cb, alpha cx, cy, cz | t s1, s2, dis dx, dy, dz ds1, ds2, ddis age, destroy | |
[组改变条件表达式] | x, y, z s1, s2, dis | t vx, vy, vz cx, cy, cz dx, dy, dz ds1, ds2, ddis age, destroy | ||
[组移除条件表达式] | x, y, z s1, s2, dis age | t vx, vy, vz cx, cy, cz dx, dy, dz ds1, ds2, ddis destroy |
自变量:表达式执行前被赋予有意义的值,作为输入(反映当前粒子状态);可读写。
因变量:该变量的赋值会实际影响粒子的行为(生成、运动或筛选结果等);可读写。
无效变量:其值 不反映当前粒子状态、不影响粒子的行为,可能为默认值;可读写。
常量:固定值;只读。
2、运算符
| 类型 | 符号 | 补充 | 示例 | |
|---|---|---|---|---|
算 术 运 算 符 | 加法 | + | 数值 或 矩阵 加法 | a + b |
| 减法、取负 | - | 数值 或 矩阵 减法/取负 | a - b 或 -a | |
| 乘法 | * | 数值 或 矩阵 乘法 | a * b | |
| 除法 | / | 数值 或 矩阵与标量 除法 | a / b | |
| 幂运算 | ^ | 数值 或 矩阵与整数指数 幂 | a ^ b | |
| 取余 | % | 数值 或 矩阵与标量 取余。 余数的符号与被除数相同 | a % b | |
比 较 运 算 符 | 小于 | < | 返回 1(真)或 0(假) | x < 1 |
| 小于等于 | <= | 返回 1(真)或 0(假) | x <= 1 | |
| 大于 | > | 返回 1(真)或 0(假) | x > 4 | |
| 大于等于 | >= | 返回 1(真)或 0(假) | x >= 5 | |
| 等于 | == | 返回 1(真)或 0(假) | x == 1 | |
| 不等于 | != | 返回 1(真)或 0(假) | x != 4 | |
逻辑 运算符 | 逻辑与 | & | cond1 & cond2。短路求值。 两边都非零则返回 1(真),否则 0(假) | 1<t & t<4 |
| 逻辑或 | | | cond1 | cond2。短路求值。 至少一边非零则结果为 1(真),否则 0(假) | t<1 | 4<t | |
| 逻辑非 | ! | !cond。 数值非零转为 0(真),零转为 1(假) | ! t==1 | |
| 赋值运算符 | 赋值 | = | 将 右侧表达式的值 赋给 左侧变量 | a=19、b=1.9、 c=(8,1,0,,5,2,0) |
优先级从高到低:
“括号 ()、函数调用、矩阵字面量”→“一元运算符 -(取负)!”→“幂运算 ^(右结合)”→“乘除取余 * / %”→“加减 + -”→“比较 < <= > >= == !=”→“逻辑与 &”“逻辑或 |”→“赋值 =”。
算术运算符:支持数值(整数/浮点数)和矩阵运算。
当两侧都是整数时,结果为整数(幂运算除外,幂运算返回浮点数)。
若一侧为浮点数,则结果为浮点数。
/ 和 % 除数为 0 时:两侧都是整数 → 抛出异常;一侧为浮点数 → 特殊值。
矩阵运算时,支持矩阵与标量、矩阵与矩阵的加、减、乘、除、取模、幂(仅整数幂)。矩阵乘法遵循线性代数规则。
3、其他语法、字符
| 类型 | 字符 | 用法 | 描述 | 例子 |
|---|---|---|---|---|
语句 分隔 | ; | 表达式1 ; 表达式2 | 分隔 多个独立的表达式语句,构成 一个表达式块。 整个块会按顺序执行,最后一个表达式的值作为整个块的返回值 | a=1; b=2; c=a+b; d=sin(c) |
列表 分隔 | , | 元素1 , 元素2 | 分隔 同一语句中的多个元素 | 多变量赋值:x, y, z = 1, 2, cos(t) 函数参数列表:pow(2, 3) 矩阵字面量中的列分隔:(1, 1, 4,, 5, 1, 4) |
矩阵 字面量 | () | (a1,a2,...,,b1,b2,...,,...) | 用圆括号 () 包围, 行之间用 双逗号 ,, 分隔, 列之间用 单逗号 , 分隔 | (1,2,3,,4,5,6) 或 (sin(t), cos(t),, t^2, log(t)) |
| ,, | ||||
| , | ||||
| 括号 | () | ( 表达式/矩阵 ) | 改变 运算优先级, 也用于 矩阵字面量和解构 | a*(114+b)、(x<=1|dis<1)&(s2>0)、 (1,1,4,,5,1,4) |
函数 调用 | 函数标识符(参数1,参数2,...) | 调用 数学函数。 参数可以是任意表达式或一个值, 参数数量需匹配函数定义 | sin(t) | |
| 数 | 0-9 | 数字 | 用于 整数/浮点数、变量名 | 整数:11、4 浮点数:51.4 变量名:a1 |
| . | 小数点 | 用于 浮点数 | ||
| 空白字符 | 半角空格 | 空白字符在解析时会被 忽略,仅用于 分隔语法元素。 | x=t; y=t^2 | |
4、内置函数
表格中的“函数及数据类型”就是 /particlex functions 命令 输出的内容。
其中“double”之类的是数据类型,仅作提示,在命令中不能写出。在函数前面的是“输出/返回数据类型”,在括号里的是“输入数据类型”。
(1)常用数学函数:
来自Java标准库Math类中的函数,支持常见的数学运算。
| 概述 | 函数 | 解释 | 补充 | 函数及数据类型 | |
|---|---|---|---|---|---|
| 基本三角函数 | 正弦 | sin(a) | 计算角度 a 的正弦值 | 输入 弧度 | double sin(double a) |
| 余弦 | cos(a) | 计算角度 a 的余弦值 | 输入 弧度 | double cos(double a) | |
| 正切 | tan(a) | 计算角度 a 的正切值 | 输入 弧度 | double tan(double a) | |
| 反三角函数 | 反正弦 | asin(a) | 计算数值 a 的反正弦值 | 返回 弧度,范围[-π/2, π/2] | double asin(double a) |
| 反余弦 | acos(a) | 计算数值 a 的反余弦值 | 返回 弧度,范围[0, π] | double acos(double a) | |
| 反正切 | atan(a) | 计算数值 a 的反正切值 | 返回 弧度,范围[-π/2, π/2] | double atan(double a) | |
| atan2(a, b) | 计算 a÷b 的反正切值, 得出极坐标的相角theta。 | 返回 弧度,范围 [-π, π] | double atan2(double a, double b) | ||
| 角度转换 | 转 弧度 | toRadians(a) | 将 角度a 转换为 弧度 | double toRadians(double a) | |
| 转 角度 | toDegrees(a) | 将 弧度a 转换为 角度 | double toDegrees(double a) | ||
| 指数与幂 | ab | pow(a, b) | 计算 a 的 b 次幂 | double pow(double a, double b) | |
| ea | exp(a) | 计算 e 的 a 次幂 | double exp(double a) | ||
| ea−1 | expm1(a) | 计算 e 的 a 次幂减 1 | 对接近 0 的输入,返回更精确 | double expm1(double a) | |
| a×2b | scalb(a,b) | 计算 a 乘 2 的 b 次幂 | double scalb(double a, int b) | ||
| 无偏指数 | getExponent(a) | 计算 a 的无偏指数 | int getExponent(double a) | ||
| 对数 | ln(a) | log(a) | 计算 a 的自然对数,即 ln(a) | 底为 e | double log(double a) |
| lg(a) | log10(a) | 计算 a 的常用对数,即 lg(a) | 底为 10 | double log10(double a) | |
| ln(1+a) | log1p(a) | 计算 1+a 的自然对数 | 对接近 0 的输入,返回更精确 | double log1p(double a) | |
| 根号 | √a | sqrt(a) | 计算 a 的平方根 | double sqrt(double a) | |
| 3√a | cbrt(a) | 计算 a 的立方根 | double cbrt(double a) | ||
| √(x²+y²) | hypot(a, b) | 计算 x²+y² 的平方根 | 避免中间结果溢出 | double hypot(double a, double b) | |
| 双曲函数 | 双曲正弦 | sinh(a) | 计算 a 的双曲正弦值 | double sinh(double a) | |
| 双曲余弦 | cosh(a) | 计算 a 的双曲余弦值 | double cosh(double a) | ||
| 双曲正切 | tanh(a) | 计算 a 的双曲正切值 | 返回 范围 (-1, 1) | double tanh(double a) | |
| 极值 | 较小值 | min(a, b) | 比较 a 和 b,返回较小值 | double min(double a, double b) | |
| long min(long a, long b) | |||||
| int min(int a, int b) | |||||
| 较大值 | max(a, b) | 比较 a 和 b,返回较大值 | double max(double a, double b) | ||
| long max(long a, long b) | |||||
| int max(int a, int b) | |||||
| 取整 | 向下取整 | floor(a) | 返回不大于 a 的最大整数 | double floor(double a) | |
| 向上取整 | ceil(a) | 返回不小于 a 的最小整数 | double ceil(double a) | ||
| 四舍五入 | rint(a) | 返回最接近 a 的整数的 double 值 | double rint(double a) | ||
| round(a) | 返回最接近 a 的 long 值 | long round(double a) | |||
| 绝对值 | abs(a) | 返回 a 的绝对值 | double abs(double a) | ||
| long abs(long a) | |||||
| int abs(int a) | |||||
| absExact(a) | 溢出抛出异常(防止溢出) | long absExact(long a) | |||
| int absExact(int a) | |||||
| 符号函数 | signum(a) | 返回 a 的符号 | double signum(double a) | ||
| copySign(a, b) | 返回 a 的绝对值与 b 的符号结合后的值 | double copySign(double a, double b) | |||
| 随机数 | random() | 生成 [0.0, 1.0) 范围内的随机数 | double random() | ||
| 数值范围限制 | clamp(a, b, c) | 将 a 限制在 [b, c] 范围内 | a 在区间内 返回 a, 超过 c 返回 c,小于 b 返回 b | double clamp(double a, double b, double c) | |
| long clamp(long a, long b, long c) | |||||
| int clamp(long a, int b, int c) | |||||
精 确 算 数 运 算 ︵ 多 为 “ 整 数 类 型 ” ︶ | 加法 | addExact(a, b) | 计算 a+b | 溢出抛出异常(防止溢出) | long addExact(long a, long b) |
| int addExact(int a, int b) | |||||
| 递增 | incrementExact(a) | 返回 a+1 | long incrementExact(long a) | ||
| int incrementExact(int a) | |||||
| 减法 | subtractExact(a, b) | 计算 a−b | long subtractExact(long a, long b) | ||
| int subtractExact(int a, int b) | |||||
| 递减 | decrementExact(a) | 返回 a−1 | long decrementExact(long a) | ||
| int decrementExact(int a) | |||||
| 乘法 | multiplyExact(a, b) | 计算 a×b | long multiplyExact(long a, long b) | ||
| long multiplyExact(long a, int b) | |||||
| int multiplyExact(int a, int b) | |||||
| multiplyFull(a, b) | long multiplyFull(int a, int b) | ||||
| multiplyHigh(a, b) | 返回 a×b 的 高位 64 位 | 将 输入 视为 有符号数 | long multiplyHigh(long a, long b) | ||
| unsignedMultiplyHigh(a, b) | 将 输入 视为 无符号数 | long unsignedMultiplyHigh(long a, long b) | |||
| 融合乘加运算 | fma(a, b, c) | 计算 a×b+c | double fma(double a, double b, double c) | ||
| 除法 | divideExact(a, b) | 计算 a÷b | 溢出抛出异常(防止溢出) | long divideExact(long a, long b) | |
| int divideExact(int a, int b) | |||||
| floorDiv(a, b) | 计算 a÷b 的向下取整值, 即 整数商 | long floorDiv(long a, long b) | |||
| long floorDiv(long a, int b) | |||||
| int floorDiv(int a, int b) | |||||
| floorDivExact(a, b) | 溢出抛出异常(防止溢出) | long floorDivExact(long a, long b) | |||
| int floorDivExact(int a, int b) | |||||
| ceilDiv(a, b) | 计算 a÷b 的向上取整值, 即 整数商加 1 | long ceilDiv(long a, long b) | |||
| long ceilDiv(long a, int b) | |||||
| int ceilDiv(int a, int b) | |||||
| ceilDivExact(a, b) | 溢出抛出异常(防止溢出) | long ceilDivExact(long a, long b) | |||
| int ceilDivExact(int a, int b) | |||||
| 取模 | floorMod(a, b) | 计算 a÷b 的 余数, 向负无穷取整,符号与除数 b 一致 | long floorMod(long a, long b) | ||
| int floorMod(long a, int b) | |||||
| int floorMod(int a, int b) | |||||
| ceilMod(a, b) | 计算 a÷b 的 余数, 向正无穷取整,符号与除数 b 相反 | long ceilMod(long a, long b) | |||
| int ceilMod(long a, int b) | |||||
| int ceilMod(int a, int b) | |||||
| IEEE标准余数 | IEEEremainder(a, b) | 计算 a÷b 的 IEEE 754 标准余数。 结果满足 |r| ≤ |b|/2 | a - b * n,其中 n 是最接近 a/b 的整数 (如果恰好有两个整数同样接近,则取偶数) | double IEEEremainder(double a, double b) | |
| 相反数 | negateExact(a) | 计算 a 的 相反数值 | 溢出抛出异常(防止溢出) | int negateExact(int a) | |
| long negateExact(long a) | |||||
| 转 int | toIntExact(a) | 将 long 值安全转换为 int 值 | 输入 超出 int 范围,抛出异常 | int toIntExact(long a) | |
| 最小精度单位 | ulp(a) | 返回 a 的 最小精度单位 | 该浮点数在当前位置的最小增减量 | double ulp(double a) | |
| 邻近浮点数 | a 向 b 方向 | nextAfter(a, b) | 返回从 a 向 b 方向的下一个邻近浮点数 | double nextAfter(double a, double b) | |
| 向上 | nextUp(a) | 返回比 a 大 的最小浮点数 | 正方向的下一个值 | double nextUp(double a) | |
| 向下 | nextDown(a) | 返回比 a 小 的最大浮点数 | 负方向的下一个值 | double nextDown(double a) | |
(2)扩展函数:
这些是本模组自定义的扩展函数,用于复杂粒子变换。
| 概述 | 函数 | 解释 | 补充 | 函数及数据类型 | |
|---|---|---|---|---|---|
| 线性插值 | lerp(delta, start, end) | 在 start 和 end 之间按比例 delta 线性插值 | 公式“start+delta×(end-start)” | double lerp(double delta, double start, double end) | |
| lerpInt(delta, start, end) | 整数版本的线性插值,结果向下取整 | int lerpInt(double delta, int start, int end) | |||
矩阵变换 (4x4 齐次坐标) (行向量) | 平移矩阵 | translate(x, y, z) | 生成 3D 平移矩阵,沿 X、Y、Z 轴平移 x,y,z 个单位 | 矩阵“(1,0,0,0,,0,1,0,0,,0,0,1,0,,x,y,z,1)” | double[][] translate(double x, double y, double z) |
| 缩放矩阵 | scale(x, y, z) | 生成 3D 缩放矩阵,沿 X、Y、Z 轴分别缩放 x,y,z 倍 | 矩阵“(x,0,0,0,,0,y,0,0,,0,0,z,0,,0,0,0,1)” | double[][] scale(double x, double y, double z) | |
| 旋转矩阵 | rotate(pitch, yaw, roll) | 生成绕 X(pitch)、Y(yaw)、Z(roll)轴 旋转的 3D 复合旋转矩阵 | 输入 弧度 | double[][] rotate(double pitch, double yaw, double roll) | |
| rotateDeg(pitch, yaw, roll) | 输入 角度 | double[][] rotateDeg(double pitch, double yaw, double roll) | |||
| 矩阵操作 | 转置矩阵 | transpose(matrix) | 计算 矩阵matrix 的 转置矩阵 | 行列互换 | double[][] transpose(double[][] matrix) |
| int[][] transpose(int[][] matrix) | |||||
| 逆矩阵 | invert(matrix) | 计算 矩阵matrix 的 逆矩阵 | 仅支持可逆方阵(使用高斯-约当消元法求方阵的逆矩阵) | double[][] invert(double[][] matrix) | |
三、技巧
1、变换
想要搞懂本部分需了解“矩阵”的相关知识,特别是 矩阵的运算、3D 齐次坐标下的 线性变换矩阵。(当然你也可以直接从下面套用)
(1)静态变换
Ⅰ、直角坐标系:
书写位置:不带“polar-”前缀指令的 <参数表达式>、<组参数表达式> 。
<参数表达式> 格式:"...表达式部分; 变换部分..."。<组参数表达式> 格式:"...变换部分..."
变换部分矩阵格式:"(x,y,z)=(x,y,z,1)*变换矩阵"(单一变换)、"(x,y,z)=(x,y,z,1)*变换矩阵1*变换矩阵2*..."(复合变换)。
| 变换类型 | 变换矩阵(行向量) | 变换部分(分解格式) | 数值 | 数值大小 | |
|---|---|---|---|---|---|
平移 变换 | translate(tx, ty, tz) | x, y, z=x+tx, y+ty, z+tz | tx、ty、tz: X 轴平移距离、Y 轴平移距离、Z 轴平移距离 | 0 为不平移; 大于 0 沿正向轴平移; 小于 0 沿负向轴平移 | |
缩放 变换 | scale(sx, sy, sz) | x, y, z=x*sx, y*sy, z*sz | sx、sy、sz: X 轴缩放大小、Y 轴缩放大小、Z 轴缩放大小 | 1 为不缩放;0 为二向箔; 大于 1 放大,小于 1 缩小; 小于 0 沿中心点镜像反转 | |
旋 转 变 换 | X 轴 | rotate(rx, ry, rz) rotateDeg(rx, ry, rz) | y=y*cos(rx)-z*sin(rx); z=y*sin(rx)+z*cos(rx) | rx:绕 X 轴旋转角度 | 大于 0 时,旋转方向为 +Z → +Y;0 为不旋转 |
| Y 轴 | x=x*cos(ry)+z*sin(ry); z=-x*sin(ry)+z*cos(ry) | ry:绕 Y 轴旋转角度 | 大于 0 时,旋转方向为 +X → +Z;0 为不旋转 | ||
| Z 轴 | x=x*cos(rz)-y*sin(rz); y=x*sin(rz)+y*cos(rz) | rz:绕 Z 轴旋转角度 | 大于 0 时,旋转方向为 +Y → +X;0 为不旋转 | ||
示例:
平移变换:
将 "x=t" 粒子图像 X轴平移-5格、Y轴平移2格,设定<参数表达式>为 "x=t; (x,y,z)=(x,y,z,1)*translate(-5,2,0)",或 "x=t; x,y=x-5,y+2"。
将组为 a 的粒子图像 沿X轴平移-5格、Y轴平移2格,设定<组参数表达式>为 "(x,y,z)=(x,y,z,1)*translate(-5,2,0)"。
缩放变换:
将 "x,y=t,t^2" 粒子图像 沿Y轴缩小到原来的0.5倍,设定<参数表达式>为 "x,y=t,t^2; (x,y,z)=(x,y,z,1)*scale(1,0.5,1)"。
旋转变换:
将 "x,y=t,t^2" 粒子图像 沿X轴旋转45°,设定<参数表达式>为 "x,y,z=t,t^2,0; (x,y,z)=(x,y,z,1)*rotateDeg(45,0,0)",或 "x,y,z=t,t^2,0; (x,y,z)=(x,y,z,1)*rotate(PI/4,0,0)"。
复合变换:
将 "x,y=t,t^2" 粒子图像 沿X轴旋转−45°、Y轴放大到原来的5倍,设定<参数表达式>为 "x,y,z=t,t^2,0; (x,y,z)=(x,y,z,1)*rotateDeg(-45,0,0)*scale(1,5,1)"。
Ⅱ、极坐标系:
书写位置:带“polar-”前缀指令的 <参数表达式>。
<表达式>格式:"...表达式部分; 变换部分...",或者两者结合。
| 变换类型 | 变换部分 | 数值 | 数值大小 |
|---|---|---|---|
| 缩放变换 | dis=dis+sd | sd: 整体缩放大小 | 0 为不缩放;大于 0 放大,小于 0 缩小 |
| dis=dis*sd | 1 为不缩放;大于 1 放大,小于 1 缩小; 小于 0 沿原点镜像反转 | ||
| 旋转变换 | s1=s1+phi | phi:水平旋转角度 | 大于 0 时,旋转方向为 左 → 右;0 为不旋转 |
s2=s2+theta | theta:垂直旋转角度 | 大于 0 时,旋转方向为 下 → 上;0 为不旋转 |
示例:
以 "s2,dis=PI*2*t,2" 粒子图像为例。
缩放变换:
将粒子图像 放大2格,设定<参数表达式>为 "s2,dis=PI*2*t,2; dis=dis+2"。
将粒子图像 放大到原来的2倍,设定<参数表达式>为 "s2,dis=PI*2*t,2; dis=dis*2"。
旋转变换:
将粒子图像 水平旋转45°,设定<参数表达式>为 "s1,s2,dis=0,PI*2*t,2; s1=s1+PI/4"。
将粒子图像 垂直旋转45°,设定<参数表达式>为 "s1,s2,dis=0,PI*2*t,2; s2=s2+PI/4"。
(2)动态变换
书写位置:[<速度表达式>]。
格式:"...变换部分..."
变换部分矩阵格式:
"(vx,vy,vz)=(x,y,z,1)*(变换矩阵-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))"(单一变换)
"(vx,vy,vz)=(x,y,z,1)*(变换矩阵1*变换矩阵2*...-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))"(复合变换)。
| 变换类型 | 变换矩阵(行向量) | 变换部分(分解格式) | 数值 | 数值大小 | |
|---|---|---|---|---|---|
平移变换 | translate(tx, ty, tz) | vx, vy, vz=tx, ty, tz | tx、ty、tz: X 轴平移速度、Y 轴平移速度、Z 轴平移速度 | 0 为不平移; 大于 0 沿正向轴移动; 小于 0 沿负向轴移动 | |
缩放变换 | scale(1+sx, 1+sy, 1+sz) | vx, vy, vz= x*sx, y*sy, z*sz | sx、sy、sz: X 轴缩放速度、Y 轴缩放速度、Z 轴缩放速度 | 0 为不缩放; 大于 0 放大, 小于 0 缩小 | |
旋 转 变 换 | X 轴 | rotate(rx, ry, rz) rotateDeg(rx, ry, rz) | vy=y*cos(rx)-z*sin(rx) - y ; vz=y*sin(rx)+z*cos(rx) - z | rx:绕 X 轴旋转速度 | 大于 0 时,旋转方向为 +Z → +Y;0 为不旋转 |
| Y 轴 | vx=x*cos(ry)+z*sin(ry) - x ; vz=-x*sin(ry)+z*cos(ry) - z | ry:绕 Y 轴旋转速度 | 大于 0 时,旋转方向为 +X → +Z;0 为不旋转 | ||
| Z 轴 | vx=x*cos(rz)-y*sin(rz) - x ; vy=x*sin(rz)+y*cos(rz) - y | rz:绕 Z 轴旋转速度 | 大于 0 时,旋转方向为 +Y → +X;0 为不旋转 | ||
原理:离散时间步长更新下(Δt=游戏刻=1),计算三维空间中一点经过变换后的速度分量,v = ( r' − r ) / Δt = r' − r = T • r − I4 • r =( T − I4 ) r
其中,变换后的速度分量 v=(vx,vy,vz),变换前的坐标向量 r=(x,y,z),变换后的坐标向量 r'=(x',y',z'),
T 是作用于齐次坐标 (x,y,z,1) 的 4×4 变换矩阵,I4 是 4×4 单位矩阵。
示例:
平移变换:
将粒子图像 沿X轴平移0.2速度,设定 [<速度表达式>] 为
"(vx,vy,vz)=(x,y,z,1)*(translate(0.2,0,0)-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))",或 "vx=0.2"。
缩放变换:
将粒子图像 沿Z轴缩小-0.1速度,设定 [<速度表达式>] 为
"(vx,vy,vz)=(x,y,z,1)*(scale(1,1-0.1,1)-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))","vz=-0.1*z"。
将粒子图像 整体放大0.1速度,设定 [<速度表达式>] 为
"(vx,vy,vz)=(x,y,z,1)*(scale(1+0.1,1+0.1,1+0.1)-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))",或 "(vx,vy,vz)=(x,y,z)*0.1"。
旋转变换:
将粒子图像 沿Y轴旋转PI/32速度,设定 [<速度表达式>] 为
"(vx,vy,vz)=(x,y,z,1)*(rotate(0,PI/32,0)-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))",或 "(vx,vy,vz)=(x,y,z,1)*(rotateDeg(0,5.625,0)-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))"。
复合变换:
将粒子图像 沿X轴平移0.05速度、沿Y轴放大0.01速度、整体放大0.02速度、沿Y轴旋转PI/16速度,设定 [<速度表达式>] 为
"(vx,vy,vz)=(x,y,z,1)*(translate(0.05,0,0)*scale(1,1+0.01,1)*scale(1+0.02,1+0.02,1+0.02)*rotate(0,PI/16,0)-(1,0,0,0,,0,1,0,0,,0,0,1,0,,0,0,0,1))"。
2、分段表达式
在 表达式 中应用“逻辑运算符”“关系运算符”的方法,类似分段函数。
虽然 表达式 没有 三元运算符,但可以用下面的方法来实现相同的效果。
(1)利用关系运算符的返回值
原理:逻辑运算的结果(返回值)被规范化为整数:0 代表“假”,1 代表“真”。
格式:"temp=计算部分*(条件部分) + 计算部分*(条件部分)+..."。(不分顺序。temp 为你想设的变量)
(2)利用逻辑运算符的短路特性(自定义变量 可能没用)
原理:
对于 &,只有当左侧操作数为“真”(非零)时才会计算右侧操作数;若左侧为“假”时则右侧被跳过。
对于 |,只有当左侧操作数为“假”时才计算右侧操作数;若左侧为“真”时则右侧被跳过。
格式:"(条件部分&表达式部分)|(条件部分&表达式部分)|…",或 "条件部分&表达式部分; 条件部分&表达式部分;…"。(先条件后表达式)
注意“表达式部分”内部不能包含 ; 。
示例:
例1:t<0 时粒子图像为 y=sin(x),t>=0 时粒子图像为 y=0.2*x^2,设定 <参数表达式> 为
利用返回值:"x, y = t, sin(t)*(t<0) + 0.2*t^2*(t>=0)"。
利用短路特性: "( t<0&y=sin(t) )|( t>=0&y=0.2*t^2 )",或 "t<0&y=sin(t); t>=0&y=0.2*t^2"。
例2:将粒子图像在 t<20 时沿 +y 移动 2 格,在 20<=t<40 时沿 +x 移动 3 格,设定 [<速度表达式>] 为
利用返回值:"vy=2/20.0*(t<20); vx=3/20.0*(20<=t&t<40)"。
利用短路特性:( t<20&vy=2/20.0 )|( 20<=t&t<40&vx=3/20.0 )',或 't<20&vy=2/20.0; 20<=t&t<40&vx=3/20.0'。
例3:将粒子图像中 t>30、颜色值小于 (0.5,0.5,0.5) 的粒子 消失,设定 [<速度表达式>] 为
利用返回值:"destroy = t>30 & cr%1<.5 & cg%1<.5 & cb%1<.5" ( *1 省略)。
利用短路特性:"t>30 & cr%1<.5 & cg%1<.5 & cb%1<.5 & destroy=1"。
3、缓动运动
书写位置:一般为 [<速度表达式>]。
本来想用 cubic-bezier(x1, y1, x2, y2) 的,结果想要实现很麻烦(或者压根不能实现),就不用了。
以下均在 [速度步长] 为 1.0 的情况下为准,若改变,则要让 T 里的 t 除以 [速度步长]。
首先归一化时间 t :
表达式:"t = t/Step ; t = (0<=t&t<Age)*(t/Age) + (t>=Age)*1"。(需放在下面的表达式前面)
自定义变量:Age 为 缓动运动的时间(需填值),Step 为 [速度步长] 的值(需填值)。
若 [速度步长] 的值为 1.0,则“ t = t/Step ; ”就不用写了。
常用缓动函数表达式:相对于位移而言。
| 缓动类型 | 表达式 | 补充 | |
|---|---|---|---|
| 线性 | V=S/Age * 1 | ||
二次 | 缓入 | V=S/Age * 2*t | |
| 缓出 | V=S/Age * 2*(1-t) | ||
| 缓入缓出 | V=S/Age * ((t<0.5)*(4*t) + (t>=0.5)*(4*(1-t))) | ||
三次 | 缓入 | V=S/Age * 3*t^2 | |
| 缓出 | V=S/Age * 3*(1-t)^2 | ||
| 缓入缓出 | V=S/Age * ((t<0.5)*(12*t^2) + (t>=0.5)*(12*(1-t)^2)) | ||
N次 | 缓入 | V=S/Age * N*t^N | N:阶次(需填值)。 N1:临时变量。 |
| 缓出 | V=S/Age * N*(1-t)^N | ||
| 缓入缓出 | N=5 ; N1=N*2^(N-1) ; V=S/Age * ((t<0.5)*(N1*t^N) + (t>=0.5)*(N1*(1-t)^N)) | ||
指数 | 缓入 | V=S/Age * 10*log(2)*pow(2,10*(t-1)) | |
| 缓出 | V=S/Age * 10*log(2)*pow(2,-10*t) | ||
| 缓入缓出 | V=S/Age * ((t<0.5)*(10*log(2)*pow(2,20*t-10)) + (t>=0.5)*(10*log(2)*pow(2,-20*t+10))) | ||
| 正弦 | 缓入 | V=S/Age * (PI/2)*sin(PI/2*t) | |
| 缓出 | V=S/Age * (PI/2)*cos(PI/2*t) | ||
| 缓入缓出 | V=S/Age * (PI/2)*sin(PI*t) | ||
| 圆形 | 缓入 | V=S/Age * t/sqrt(1-t^2) | |
| 缓出 | t=t+(t==0) ; V=S/Age * (1-t)/sqrt(1-(1-t)^2) | ||
回退 | 缓入 | Bc=1.70158 ; V=S/Age * (3*(Bc+1)*t^2-2*Bc*t) | Bc:过冲系数(典型值 1.70158) |
V=S/Age * (3*t^2-(sin(PI*t)+PI*t*cos(PI*t))) | |||
| 缓出 | Bc=1.70158 ; V=S/Age * (3*(Bc+1)*(1-t)^2-2*Bc*(1-t)) | ||
V=S/Age * (3*(1-t)^2-(sin(PI*(1-t))+PI*(1-t)*cos(PI*(1-t)))) | |||
| 缓入缓出 | Bc=1.70158 ; V=S/Age * ((t<0.5)*(12*(Bc+1)*t^2-4*Bc*t ) + (t>=0.5)*(12*(Bc+1)*(1-t)^2-4*Bc*(1-t))) | ||
弹性 | 缓入 | V=S/Age * (pow(2,-10*(1-t))*((-10*log(2))*sin(((1-t)-0.075)*2*PI/0.3 ) + (2*PI/0.3)*cos(((1-t)-0.075)*2*PI/0.3))) | 基于 Penner 弹性缓动函数推导。 可以根据需要调整内部常数(如 0.075 和 0.3) 以获得不同的振荡效果 |
| 缓出 | V=S/Age * (pow(2,-10*t)*(-10*log(2)*sin((t-0.075)*(2*PI)/0.3) + (2*PI/0.3)*cos((t-0.075)*(2*PI)/0.3))) | ||
| 缓入缓出 | V=S/Age * ((t<0.5)*(pow(2,-10*(2*t))*((-10*log(2))*sin(((2*t)-0.075)*2*PI/0.3)+(2*PI/0.3)*cos(((2*t)-0.075)*2*PI/0.3))) + (t>=0.5)*(pow(2,-10*(2*t-1))*((-10*log(2))*sin(((2*t-1)-0.075)*2*PI/0.3)+(2*PI/0.3)*cos(((2*t-1)-0.075)*2*PI/0.3)))) | ||
| 弹跳 | 缓入 | V=S/Age * (((1-t)<1/2.75)*(15.125*(1-t))+((1-t)>=1/2.75&(1-t)<2/2.75)*(15.125*((1-t)-1.5/2.75)) +((1-t)>=2/2.75&(1-t)<2.5/2.75)*(15.125*((1-t)-2.25/2.75))+((1-t)>=2.5/2.75)*(15.125*((1-t)-2.625/2.75))) | |
| 缓出 | V=S/Age * ((t<1/2.75)*(15.125*t) + (t>=1/2.75&t<2/2.75)*(15.125*(t-1.5/2.75)) +(t>=2/2.75&t<2.5/2.75)*(15.125*(t-2.25/2.75)) + (t >= 2.5/2.75)*(15.125*(t-2.625/2.75))) | ||
| 缓入缓出 | V=S/Age * ((t<0.5)*(((1-2*t)<1/2.75)*(15.125*(1-2*t))+((1-2*t)>=1/2.75&(1-2*t)<2/2.75)*(15.125*((1-2*t)-1.5/2.75)) +((1-2*t)>=2/2.75&(1-2*t)<2.5/2.75)*(15.125*((1-2*t)-2.25/2.75))+((1-2*t)>=2.5/2.75)*(15.125*((1-2*t)-2.625/2.75))) + (t>=0.5)*(((2*t-1)<1/2.75)*(15.125*(2*t-1))+((2*t-1)>=1/2.75&(2*t-1)<2/2.75)*(15.125*((2*t-1)-1.5/2.75)) +((2*t-1)>=2/2.75&(2*t-1)<2.5/2.75)*(15.125*((2*t-1)-2.25/2.75))+((2*t-1)>=2.5/2.75)*(15.125*((2*t-1)-2.625/2.75)))) | ||
| 贝塞尔 | 二次 | V=S/Age * (2*(1-t)*y1+2*t*(1-y1)) | y1:控制点纵坐标(需填值)。 起点 (0,0),终点 (1,1)。 |
| 三次 | V=S/Age * (3*(1-t)^2*y1+6*(1-t)*t*(y2-y1)+3*t^2*(1 - y2)) | y1、y2:贝塞尔曲线的控制点(需填值)。 起点 (0,0),终点 (1,1)。 | |
自定义变量:V 为 实际速度(可赋给 vx、vy、vz 等),S 为移动距离(需填值)。
某些缓动后停不下来怎么办,可以限制 V,t>1 时让 V 为 0,例如:vy=V*(t<1)。
以上缓动方法并不是全部,你也可以用表达式设个想要的缓动运动。V 也并不一定只赋给 速度变量,其他变量也可赋。
四、小知识
1、客户端-服务端同步
命令在服务端执行,通过 NeoForge 的网络系统,将命令参数从服务端发送到所有在线的客户端,由客户端负责最终的粒子生成。
2、条件表达式生成粒子的大致原理
空间遍历:根据 <采样范围> 和 [采样步长],在三维空间中进行三重循环(从 -dx 到 +dx,以 [采样步长] 递增)。对每个候选点:
将 <条件表达式> 中的 x, y, z 设置为当前点相对于中心的位置。
自动计算球坐标 s1、s2 和 dis,以便表达式可以直接使用。
执行 <条件表达式>。表达式的结果是一个整数值(0 表示假,非 0 表示真)。
粒子生成:如果表达式返回值 非 0,则在该绝对位置(中心位置 + 偏移)生成一个粒子,并应用命令中指定的颜色、速度、生命周期等属性。
3、颜色
在 Minecraft 的渲染管线中,颜色值的处理过程如下:
浮点数到整数的转换:将 浮点数 乘以 255 并强制转换为 int。(VertexConsumer)
整数到字节的写入:直接将 int 强制转换为 byte,发生溢出截断(保留低 8 位)。(BufferBuilder.setColor)
OpenGL 归一化:将内存中的 字节值 byte 当作 无符号整数(范围 0~255),然后除以 255.0 映射到 [0.0, 1.0]。(VertexFormatElement.Usage.COLOR)
最终效果:(cr, cg ,cb 都为这个数)
2.0 → (int)(2.0*255) = 510 → (byte)510 = -2 → 254 / 255 ≈ 0.996(接近白色)。
3.5 → (int)(3.5*255) = 892 → (byte)892 = 124 → 124 / 255 ≈ 0.486(接近中等灰度)。
-1.0 → (int)(-1.0*255) = -255 → (byte)-255 = 1 → 1 / 255 ≈ 0.004(接近黑色)。
现象本质:整数溢出环绕,它解释了为什么超出 [0,1] 的值不会直接变成 1 或 0,而是循环到另一端的值。
4、特殊值
(1)介绍:
数学表达式中,浮点运算遵循 IEEE 754 浮点标准,因此可能产生两个特殊值:NaN(Not a Number)和 Infinity(无穷大)。这些值通常由非法或未定义的数学操作产生,并会影响粒子的行为和渲染。
NaN(Not a Number):表示未定义或不可表示的数值结果。NaN 具有传染性:任何包含 NaN 的算术运算结果仍为 NaN。NaN 与任何值(包括自身)的比较结果均为 假。
+Infinity(正无穷大):表示超出 double 上限的值。
-Infinity(负无穷大):表示超出 double 下限的值。
(2)产生
NaN:
零除以零:0.0 / 0.0。
取余中除数为零:1 % 0.0。
负数开平方:sqrt(-1)。
负数取对数:log(-1)。
负数的小数次幂:pow(-2, 0.5)。
反三角函数的参数超出定义域 [-1, 1]:asin(2)、acos(3)。
其他:任何包含 NaN 的运算、Infinity - Infinity、Infinity / Infinity、0 × Infinity。
Infinity:
正数除以零:1.0 / 0.0 → +Infinity。
负数除以零:-1.0 / 0.0 → -Infinity。
对零取对数:log(0) → -Infinity。
零的负数次幂:pow(0, -2) → +Infinity。
超出范围的指数:exp(1000) → +Infinity。
其他:包含 Infinity 的部分运算。
无穷大参与比较运算时行为正常:+Infinity > 任何有限数 为 真,-Infinity < 任何有限数 为 真。但与 NaN 的比较的永远为 假。
(3)对粒子的影响
速度变量(vx, vy, vz):NaN 将转换为 0.0。Infinity 等不会被转换,以“无穷大”速度移动,瞬间超出世界边界(通常会被移除或卡在边界)。
位置变量(x, y, z):NaN 可能无法渲染该粒子。Infinity 等粒子会被瞬间推到无限远处。
颜色变量(cr, cg, cb, alpha):NaN 在渲染管道被视为未定义行为(可能导致不可见或显示为黑/白色)。Infinity 等渲染时可能被钳位到 1.0 或 0.0。
销毁标志(destroy):销毁。
5、数据类型
参数、变量、函数等所用到的 数据类型 看下表。
| 数据类型 | 说明 | 占用空间 | 取值范围 | ||
|---|---|---|---|---|---|
基 本 数 据 类 型 | 整数类型 | byte | 整数,禁用小数点 | 1 字节 | -27 ~ 27−1 |
| short | 2 字节 | -215 ~ 215−1 | |||
| int | 4 字节 | -231 ~ 231−1 | |||
| long | 8 字节 | -263 ~ 263−1 | |||
| 浮点类型 | float | 即可表示 整数 又可表示 小数,建议加上小数点。 单精度 float,双精度 double | 4 字节 | 约-3.403E38 ~ 3.403E38 | |
| double | 8 字节 | 约-1.798E308 ~ 1.798E308 | |||
| 字符类型 | char | 2 字节 | '\u0000' ~ '\uFFFF' | ||
| 布尔类型 | boolean | 表示逻辑上的 真(true)和 假(false) | 1 字节 | true、false | |
其 他 数 据 类 型 | 粒子类型 | ParticleOptions | 原版粒子类型 ID,包含可选参数 | ||
| 字符串 | String | 无 引号 可用字符:“a~z、A~Z”、“0~9”、“-_.+”; 加 引号 可用字符:任意字符串,中文也可以,若字符串里有引号需用 \ 转译。 建议加上 引号(英文半角下的 单/双引号 " "、' ' ) | |||
| 向量 | Vec3 | 三维向量,即 三个 double | |||
| Vector4f | 四分量,即 四个 float | ||||
| 数组 | double[][] | 二维 双精度 数组,用于表示 矩阵 | |||
| int[][] | 二维 整型 数组,用于表示 矩阵 | ||||
| 枚举 | enum | 为 常量 赋予 有意义的名称 | |||
6、配置文件
配置文件名为“exparticle.json”,存放在“.minecraft或 版本名称”下,而不是 config 里。
"maxParticleCount": 65536,设置最大粒子数量上限,超出限制强制删除老的粒子(int);
"maxParticleTickMillis": 1000,设定每个粒子的单次更新操作耗时上限(int);
"ParallelParticleUpdate": false,控制是否启用多线程并行更新粒子(boolean)。
五、出现的问题与解决方法
1、条件表达式无法正确生成粒子
问题:设<采样范围>: 0.5 0.5 0.5,<条件表达式>:"y==-0.1",[采样步长]:0.4 …"。结果粒子无法在其位置生成。把<条件表达式>改为"y<-0.0999&y>-0.1001",结果能生成。
原因:浮点数精度导致。
由于 -0.1、0.4 无法用二进制精确表示,实际存储的值与 -0.1 的精确值 不相等,导致 y == -0.1 为假。
解决方案:
使用容差判断:例如 abs(y - (-0.1)) < 10^-6,这表示 y 接近 -0.1 的误差在 10^-6 以内。
改用区间判断:例如 y > -0.1001 & y < -0.0999。
调整步长使迭代值更准确:例如步长 0.125(1/8)在二进制中可以精确表示。但还是建议在编写条件表达式时,避免直接相等判断。
2、组坐标与执行位置
问题:在命令方块中输入组管理指令,若组坐标没有时,执行位置在玩家坐标而不是命令方块位置。
原因:
网络包设计:当命令中未提供坐标时,服务端构造的网络包会将 hasPos 标记设为 false,并且不发送任何坐标数据。
客户端处理:客户端收到网络包后,如果 hasPos 为 false,就会直接使用本地玩家(即当前客户端控制的玩家)的坐标作为默认参考点。
解决方案:
手动指定坐标:在命令中显式添加 [组坐标] 参数,例如 ~ ~ ~(相对命令方块自身)或具体的绝对坐标。
3、与 /tick 指令
问题:/tick freeze、/tick unfreeze、/tick step 可以正常冻结解冻粒子。而 /tick rate 命令设置低于 20 时会减慢,但高于 20 时和 20 一样的效果。(单机)
原因:
客户端 tick 速率硬上限:客户端的 tick 循环中,tick 间隔由 DeltaTracker.Timer 管理,会取「默认 20 TPS 对应的间隔」与「服务端同步的 tick 间隔」两者中的较大值。即使服务端要求更高的 tick 速率(间隔更短),客户端仍会采用 50ms 的最小间隔,导致实际速率无法超过 20 TPS。(Minecraft)
粒子动画依赖客户端 tick:粒子的更新(包括自定义的 customTick 和 t 的递增)均在客户端主 tick 循环的 particleEngine.tick() 中执行。由于客户端 tick 速率被锁定在 ≤20 TPS,粒子动画自然无法获得高于 20 TPS 的更新频率。
其他 tick 命令的机制:/tick freeze 通过冻结游戏逻辑让所有 tick 停止,粒子自然静止;/tick step 则在冻结状态下强制执行指定数量的 tick,从而逐帧推进动画。这些命令不改变 tick 速率,只控制 tick 的执行与否,因此能正常生效。(TickRateManager)
解决方案:
若需加速粒子效果,可在自定义表达式中增大 t 的步长(如将 [速度步长] 设为更大的值),使单次 tick 内 t 的变化量增加,从而在相同 tick 数下达到更快的整体运动。
六、实用示例
工具、资源网站:MC 指令生成器 | OCEAN、Particlex 1.16.5(网站中使用的是 本模组前身 ColorBlock 中的指令,需自行替换对应指令)。
再弄几个我自己做的小指令吧:
下面代码适用范围 以 数据包中的函数 为准,若想在 命令方块 中使用,需删除 反斜杠“\”。批量替换完 \ 直接粘贴到命令方块里就行,不用管换行。
“\”在数据包函数中的功能:单行命令的拆分和续接。
1、参数化粒子指令
(1)方块边框
particlex rgba-parameter minecraft:end_rod ~ ~2 ~ 0 0 0 0 12.0 \
"( t<1 & x,y,z=t-0.5,-0.5,-0.5 )| \
( 1<=t&t<2 & x,z=0.5,t-1.5 )| \
( 2<=t&t<3 & x,z=2.5-t,0.5 )| \
( 3<=t&t<4 & x,z=-0.5,3.5-t )| \
( 4<=t&t<5 & x,y,z=-0.5,t-4.5,-0.5 )| \
( 5<=t&t<6 & x,y,z=0.5,5.5-t,-0.5 )| \
( 6<=t&t<7 & x,y,z=0.5,t-6.5,0.5 )| \
( 7<=t&t<8 & x,y,z=-0.5,7.5-t,0.5 )| \
( 8<=t&t<9 & x,y,z=-0.5,0.5,t-8.5 )| \
( 9<=t&t<10 & x,z=t-9.5,0.5 )| \
( 10<=t&t<11 & x,z=0.5,10.5-t )| \
( 11<=t&t<12 & x,z=11.5-t,-0.5 ); \
cr,cg,cb,alpha=0,t,1,1.0" \
0.125 0
(2)沙漏形状
particlex tick-parameter minecraft:end_rod ~ ~10 ~ 0 1 1 1 0 0 0 0 200 \
's,a=5,PI/2; v,u=t*5,t*PI/100; \
(x,y,z)=(sin(v)*cos(u)^2,sin(u-a),cos(v)*cos(u)^2)*s' \
0.05 50 -1
如有错误,欢迎指出。


