Kzeroko KTM2

Particlize

把实体溶解成碎片的组合/分解特效:配置 JSON、它接受的每一个字段、继承怎么解析,以及客户端预算。

更新于 2026/9/18 10 分钟阅读 特效particlize着色器资源包

Particlize 拿实体已经渲染出来的模型,把它打散成碎片,或者反过来从碎片拼起来。具体长什么样由一份配置决定:播多久、溶解边缘什么颜色、飞出多少碎片、离远了还剩多少。配置是资源包 JSON,所以调一个特效不需要动代码。哪个时刻播哪一个,在 实体特效。

文件放在哪里

assets/<命名空间>/particlize_fx/<名称>.json

一个文件就是一份配置,它的标识符是 <命名空间>:<名称>。内置的那一套在 assets/isekaiexpansion/particlize_fx/ 下,共十二个文件,其中 base.json 是其它配置共同继承的那一份。

因为它们属于资源(assets)而不是数据(data),资源包覆盖一份配置的方式和覆盖一张贴图一样:列表里靠前的包整份文件胜出,包与包之间不会按字段合并。配置内部的继承是另一套机制,见下面。

最小示例

每个字段都有内置默认值,所以能被接受的最小文件就是 {}。而真正有用的最小文件是继承一份再改一处:

{
  "extends": "isekaiexpansion:base",
  "surface": {
    "tint": "#7FE6FFFF"
  }
}

extends 用标识符指向另一份配置。父配置的 JSON 会先和这份文件深度合并,然后才开始解析:嵌套对象逐键合并,这里出现过的键覆盖父配置的。所以上面这个例子保留了 base 里其余全部的 surface 设置。

继承可以串多少层都行,成环会被检测出来并报告,而不是卡死。

字段

字段类型默认值作用
extendsidentifier不继承要继承的配置,在解析之前深度合并。目标不存在会让这份配置失败。
timelineobject内置默认值特效什么时候开始、播多久。
surfaceobject内置默认值作用在实体自己几何体上的那一道溶解。
shardsobject内置默认值溶解过程中甩出去的散碎粒子。
lodobject内置默认值距离阈值与复杂度预算。

这四个对象互相独立。省掉哪个,哪个就整体取默认值;写了哪个,就只有你写出来的键会变,其余的仍然沿用默认值或父配置的值。

timeline

字段类型默认值作用
duration_ticksinteger32溶解的长度,夹在 1…1200。
delay_ticksinteger0开始渲染前先等多久,夹在 0…1200。用来和起手动作对齐。
hold_ticksinteger8结束状态在屏幕上再停留多久,夹在 0…1200。
easingenumsmoother_step作用在进度上的曲线:linear、smooth_step、smoother_step、quad_in、quad_out。写了认不出来的名字会静默变成 smoother_step。

surface

溶解本身——作用在实体自己几何体上的那一道着色器。

字段类型默认值作用
shapeenumplane溶解扫过时用的遮罩:plane、sphere、cylinder、box、noise。认不出来的名字变成 plane。
directionarray<number>[0.0, 1.0, 0.0]扫过的方向,加载时会归一化。长度为零的向量变成正上方。
tintcolour#B873FFFF铺在溶解表面上的颜色,格式 #RRGGBBAA。
tint_strengthnumber0.22tint 有多少真的铺到表面上,0.0…1.0。
edge_colorcolour#7629FFFF正在推进的那条溶解边缘的颜色。
edge_widthnumber0.13这条边缘的厚度,0.001…1.0。不能为零——没有厚度的边缘会渲染成一条硬接缝。
emissionnumber0.85边缘的自发光亮度,0.0…4.0。超过 1.0 会过曝,做魔法效果时正是要这个。
noise_scalenumber5.5溶解噪声的频率,0.01…128.0。越高颗粒越细。
noise_strengthnumber0.16这份噪声把边缘扰动多少,0.0…1.0。
cell_scalenumber14.0碎裂单元的大小,1.0…128.0。越高碎块越多越小。
shatternumber0.18表面按这些单元碎裂得有多厉害,0.0…1.0。
displacementnumber0.025碎块离开模型多远,0.0…2.0,单位是格。
bounds_paddingnumber0.06额外的渲染包围盒,免得飘出去的碎块被剔除,0.0…1.0。特效在边缘被切掉时调大它。

shards

溶解过程中甩出去的那些散碎粒子。

字段类型默认值作用
enabledbooleantrue填 false 就只渲染表面那一道。客户端配置也能全局关掉碎片。
colorcolour#B873FFE6碎片颜色,含透明度。
countinteger84近距离时的碎片数量,夹在 0…512。
far_countinteger24超过 lod.far_distance 之后的数量,夹在 0…count——比 count 大的值会被降到 count,反过来不会。
sizenumber0.035碎片大小,单位是格,0.002…1.0。
size_variancenumber0.55围绕 size 的随机浮动,0.0…1.0。
speednumber0.55初始向外的速度,0.0…4.0。
spreadnumber0.45初始散射的锥角宽度,0.0…4.0。
gravitynumber0.015向下的牵引,-1.0…1.0。负值会让碎片往上飘。
lifetime_ticksinteger14一个碎片活多久,1…1200。

lod

距离与复杂度预算。混战时还能不能玩,靠的就是这几个字段。

字段类型默认值作用
near_distancenumber24.0这个半径以内保持完整质量,1.0…512.0 格。
far_distancenumber56.0超过之后降质量;不会小于 near_distance。
max_distancenumber96.0超过之后完全不渲染;不会小于 far_distance。
complex_model_verticesinteger6000顶点数超过它就按复杂模型处理,256…1000000。
extreme_model_verticesinteger18000再超过它就走最省的路径;不会小于 complex_model_vertices。
fallback_high_subdivisionsinteger8靠近镜头时后备网格的细分级别,1…12。
fallback_medium_subdivisionsinteger5中距离级别;不会高于 fallback_high_subdivisions。
fallback_low_subdivisionsinteger3远距离级别;不会高于 fallback_medium_subdivisions。

三个距离和三个细分级别都是按顺序依次夹的,所以一份配置不可能描述出比近距离还近的远距离,也不可能让低级别比高级别更精细。按常理顺序写,这些钳制就永远不会触发。

校验

这里没有任何东西会抛到玩家脸上。

  • 格式错误或解析不了的配置会打一条警告日志、点名是哪份资源,然后只丢掉这一份。其余配置照常加载。
  • extends 指向了不存在的配置同样只让这一份失败,日志里是 Unknown inherited profile。
  • extends 成环会在解析时被检测到,并把整条路径打进日志,而不是一路递归到客户端崩溃。
  • 超范围的数值会被钳制,而不是拒绝。count 写 9999 会变成 512,游戏里不会有任何提示——日志只记加载失败,不记钳制。
  • 非有限的数值会退回到它那一项范围的下限。
  • 认不出来的枚举名会退回:shape 退到 plane,easing 退到 smoother_step。大小写和前后空格都不影响。
  • 一份配置都没加载成功时,注册表会保留一条——arcane_pulse 指向内置默认值——这样渲染器总有东西可用,而不是对着空表崩掉。

拿一个注册表里没有的标识符去问,返回的是空。这该由调用方自己处理,而不是偷偷换一份给它。

重载

ParticlizeProfileRegistry 是客户端资源重载监听器,所以 F3+T 以及任何资源包变化都会重载配置。解析在别的线程上做,解析完的映射整体原子替换,所以正在播的特效会用它开始时那份配置播完。

配置本身完全不过网络。服务端只发要播哪个配置 ID,客户端用自己的资源包去解析。两个玩家装了不同资源包,就应该看到不同的效果。

播放一份配置

服务端这边,由 ParticlizeFxBroadcaster 决定谁能看到:

方法谁能看到
playTracking(target, profileId, mode, …)所有正在追踪该实体的玩家。返回一个特效 ID。
playTrackingObservers(…)追踪者,外加一个显式的观察者过滤。
playNearby(entity, profileId, mode, seed, priority, radius)实体 radius 以内的所有人。
playFor(…)指定的某一个玩家。
cancelTracking(target, effectId)用 playTracking 返回的 ID 停掉正在播的特效。
clearFor(viewer)清掉某个玩家排队中的全部特效,用于登出和切维度。

seed 参数让一次散射可复现:同一个种子在每个客户端上产生同样的碎片分布。两个玩家对录像时这很重要,其余时候无所谓。

ParticlizeMode 有 DECOMPOSE、COMPOSE、PULSE。ParticlizePriority 有 AMBIENT、NORMAL、IMPORTANT、CRITICAL,决定客户端超预算时哪些能留下来。

客户端这边,ParticlizeFx 在本地启动一个特效,并返回一个可以用来取消它的 ParticlizeHandle。只在纯装饰、纯本地的场合用它;任何别的玩家也该看到的效果都走广播器。

客户端配置

在 config/isekaiexpansion.json 的 particlize_fx 分类下。全部是客户端设置,服务端不会强制。

选项类型默认值作用
enableParticlizeFxbooleantrue填 false 就在这台客户端上关掉全部 particlize 效果。
particlizeFxShardsbooleantrue填 false 保留表面溶解,去掉散碎粒子。
particlizeFxRenderDistanceinteger128硬上限,单位是格,8…256。叠在每份配置的 lod.max_distance 之上,取两者中更小的。
particlizeFxShaderCrowdThresholdinteger4同时有多少个走着色器路径的特效之后开始降级,1…16。
particlizeFxNativeCrowdThresholdinteger8更省的原生路径上的同一个限制,1…32。
particlizeFxMaxShardsPerFrameinteger1024一帧最多画多少碎片,128…2048。要得比这多的配置会被削,按优先级先削。

相关页面

  • 实体特效——死亡、传送、重生分别播哪一份配置。
  • Effekseer 粒子——另一套特效系统,做的是制作好的发射器,而不是实体溶解。