Kzeroko KTM2

武器斩击特效

Better Combat 攻击轨迹、完整资源包配置档、几何、材质与客户端事件。

更新于 2026/9/26 19 分钟阅读

武器斩击特效根据实际渲染的武器运动生成双通道轨迹。它观察玩家与生物的 Better Combat 攻击动画,分类后只绘制符合条件的斩击;即使关闭内置轨迹,开始事件仍可供扩展使用。

它做什么

实现由 client.fx.weaponslash 持有,仅负责客户端呈现,不计算伤害,也不持有服务端游戏状态或存档数据。配置档位于客户端资源 assets/<namespace>/weapon_slash_fx/<path>.json,对应 ID 为 <namespace>:<path>。纹理是普通资源包资源。

公开事件为 api.client.fx.WeaponAttackFxEvents.ATTACK_ANIMATION_START。WeaponSlashFx、WeaponSlashPoseCapture 与注册表是实现类,不是公共 API 门面。Better Combat 提供玩家攻击动画数据,Better Mob Combat 桥接为生物调用同一路径。当前仓库使用 Better Mob Combat 1.3.1 与 Satin 1.14.0。

运行时模型

攻击与渲染生命周期

每次动画开始时,先解析攻击手物品、Better Combat 类别、配置档与视觉类型,再触发事件。时长为正有限 tick 值,否则为 0;前摇为有限值时限制到 0–1,否则为 0。事件先于实体启用、时长、排除项和斩击类型检查执行。每次攻击只触发一次,不会为第二只手的轨迹再次触发。监听器异常不会被本地捕获,可能打断后续监听器与轨迹设置。

新攻击会停止该实体旧轨迹的采集,保留已有采样继续淡出。生成轨迹要求有匹配配置档、类型为 SLASH、武器不在排除项内、玩家/生物开关启用且时长为正。实际采集终点必须大于起点。若动画同时启用 leftItem 与 rightItem、攻击不是 TWO_HANDED,且双手武器都有非双手型 Better Combat 属性,则每只手独立解析配置档与排除项。

世界渲染开始时捕获逆视图与摄像机,实体之后绘制世界轨迹,渲染结束时关闭采集窗口。第一人称手部轨迹使用独立通道并直接绘制到主目标;Player Animator 的第一人称玩家模型通道与普通第三人称渲染分开处理。Iris 阴影通道忽略。加入、断线与世界切换会清空轨迹;同时关闭两类实体开关也会清空系统。

采集遵守配置档和本地视角开关,距离取两者配置的较小值。人形变身渲染代理会解析回宿主。世界采样相对移动中的实体锚点保存,因此保留的轨迹会跟随实体移动,而不是留在原世界位置。

几何与采样

自动锚点优先使用平面生成模型的可见精灵图轮廓,再使用渲染顶点采集。平面模型分析会检查动画各帧的非透明像素并缓存凸轮廓;内置或有深度的模型走顶点路径。顶点采集保留最多 32 个最远且不同的候选点,再对平方距离不低于最远值的 0.98、方向相似度至少 0.9 的候选点取平均。无法探测时使用经变换的模型空间根点/尖端坐标;没有可用物品变换矩阵时不产生采样。

采样间隔为max(0.0001, min(duration × (end − start), lifetime) / (max_samples − 2)) ticks。每条轨迹都为尾部裁切邻点和可替换的实时头部预留容量。慢速攻击按可见寿命内的运动采样,不再把整个预算分散到攻击全程。同一时间的重复捕获不会增加采样。

采集会保留窗口开始前的最后一个姿态,并允许捕获窗口结束后最多1 tick(0.05秒)的姿态。轨迹用窗口两侧的观测插值出精确起止位置,前摇姿态不参与绘制。如果没有一对可用观测,就无法重建该窗口内的斩击。

普通挥砍把刀身方向按旋转插值,长度单独插值,手部沿对应圆弧移动;沿旋转轴的位移保持线性。因此,即使捕获间隔较大,刀身也不会向手部收缩。无效或零长度姿态会清空历史;时间倒退、间隔超过所选寿命、根点位移或刀身长度变化过大,以及无法由旋转动画解释的超过150度的方向变化,都会重新开始轨迹。max_segment_distance限制的是根点位移和刀身长度变化,长武器尖端的正常圆弧行程可以超过该值。

世界空间旋转重建检查已启用且至少有2个关键帧的body/torso偏航通道,选择展开后旋转量较大的通道。旋转要求净转角至少π弧度(180度),且净转角与总角度行程之比至少为0.75。同一tick编写的完整转圈会保留。统一的姿态插值器先移除编写的偏航,再插值剩余运动,最后按偏航缓动重新旋转。旋转引导遵循Better Combat的攻击镜像:副手选择和左惯用手各自反转方向,双武器轨迹使用同一方向。第一人称轨迹使用实测旋转和slash样式;在手部与玩家模型坐标之间切换时,会清空第一人称历史。

网格按时间距离加权邻点来平滑内部姿态,再恢复每个实测刀身长度。曲线以6度或0.12格为细分目标,仅有两个姿态时至少生成8段,整条轨迹最多129个截面。宽轨迹和窄边缘共用网格,每个材质最多128个四边形,每条轨迹最多提交1024个顶点。两端保持在实测或窗口裁切后的姿态上。

两端收窄和纵向UV都按累计扫过距离计算,每段取根点与尖端行程中的较大值。无论攻击时长如何,完整遮罩都会覆盖当前可见轨迹。短轨迹的宽度随行程增长,达到配置的内半径后不再加宽。过期清理会保留寿命边界之前的一个邻点,网格在边界处裁切,因此尾部能连续移动。透明度仍按剩余寿命的smoothstep淡出,停止采集后继续消失。达到轨迹数量上限时优先移除已停止采集的轨迹。

约定

本地配置

以下 IsekaiConfig 字段来自 IsekaiExpansion.localConfig(),分类为 weapon_slash_fx。它们控制轨迹生成与渲染,不阻止攻击事件。

字段类型默认值作用
weaponSlashFxPlayersEnabledbooleantrue启用玩家轨迹。
weaponSlashFxMobsEnabledbooleantrue启用生物轨迹。
weaponSlashFxFirstPersonbooleantrue与配置档开关共同控制第一人称采集。
weaponSlashFxThirdPersonbooleantrue与配置档开关共同控制第三人称采集。
weaponSlashFxOpacityPercentinteger100限制为 0–100%,乘到材质透明度。
weaponSlashFxRenderDistanceinteger64限制为 8–256 格;采集距离取它与配置档距离的较小值。
weaponSlashFxMaxActiveTrailsinteger64限制为 8–128 条;优先移除旧的已停止轨迹,否则移除最旧活动轨迹。
weaponSlashFxExcludedItemsarray<string>[“isekaiexpansion:crescent_rose”]精确物品 ID,去除首尾空白;空白/null 项忽略,无效 ID 警告后忽略。查询时检测改动并重建排除快照。

配置档选择与继承

配置档按优先级降序、具体度降序、ID 升序排序,首个匹配项生效。物品 ID 组增加 1000 具体度,标签 500,精确动画 200,动画正则 100,类别 50。非空选择器组之间为 AND,同组条目之间为 OR;精确动画与正则共同组成一个 OR 组。

继承会深度合并对象。数组、基本值与 null 直接替换父值,不追加数组。extends 在解析前移除。子项空对象保留继承的内部键;null 或类型错误的嵌套对象则可能回退到加载器默认值。未覆盖的选择器与优先级也会继承。

最小合法配置档为 {},但它以优先级 0 匹配全部内容,会压过内置后备配置。需要局部修改时应使用有明确目标的子项:

{
  "extends": "isekaiexpansion:default_hybrid",
  "priority": 100,
  "selector": { "items": ["minecraft:diamond_sword"] },
  "sheet": { "color": "#5CCBFFE0" }
}

下表是资源继承前的加载器默认值,不是内置后备配置的最终值。

字段类型默认值作用
extendsidentifierAbsent: no parent递归继承另一个配置档 ID,不是文件路径;父项缺失或循环继承会拒绝子项。
priorityinteger0数值越高越优先,先于选择器具体度比较。
selectorobject空选择器匹配所有物品、动画和类别;见选择器表。
kindenum(auto, slash, stab, slam, other, none)auto去除首尾空白,不区分大小写;未知值拒绝配置档。只有 slash 生成内置轨迹。
rootarray<number>[0.28, 0.18, 0.5]模型空间后备根点,3 个有限坐标,不限制数值范围。
tiparray<number>[0.76, 0.92, 0.5]模型空间后备尖端,解析规则与 root 相同。
anchorsobject自动探测尖端或使用显式模型坐标。
anchors.modeenum(auto, model_space)auto未知字符串回退到 auto;自动模式在可用时使用手部原点与探测出的可见尖端。
anchors.root_insetnumber0限制为探测手部到尖端长度的 0–0.9,仅自动模式使用。
anchors.tip_insetnumber0限制为 0–0.9;端点至少为根部内缩 + 0.01,再限制到 0.01–1。
windowobject归一化攻击进度采集窗口。
window.use_upswing_startbooleantrue使用同步的前摇进度,而不是编写的起点。
window.startnumber0.35限制为 0–1;仅在关闭前摇起点时使用。
window.start_offsetnumber-0.02限制为 −1–1,加到所选起点后再限制为 0–1。
window.endnumber0.96限制为 0–1;终点不大于实际起点时不生成轨迹。
max_samplesinteger28限制为 4–64,包含可替换的实时头部采样。
min_sample_distancenumber0.004正有限距离,单位格;两个端点移动距离都小于它时,才跳过固定采样。
max_segment_distancenumber3正有限距离,单位格;根点位移或刀身长度变化超过此值时重置历史。检查前会先移除编写的旋转偏航。
geometryobject包含普通斩击与旋转的独立样式。
geometry.slashobjectDefault slash style用于实测旋转轨迹,包括全部第一人称轨迹。
geometry.spinobjectDefault spin style用于符合条件的世界空间身体/躯干旋转轨迹。
max_distancenumber64先要求正有限值,再限制到 4–256 格。
first_personbooleantrue配置档的第一人称采集许可。
third_personbooleantrue配置档的第三人称采集许可。
sheetobjectDefault sheet material宽轨迹材质,见材质表。
edgeobjectDefault edge material外侧窄轨迹材质,见材质表。
字段类型默认值作用
selector.itemsstring or array<string>[]精确物品 ID,任一项匹配即可;无效 ID 拒绝配置档。
selector.item_tagsstring or array<string>[]不带 # 的物品标签 ID,匹配任一标签即可。
selector.animationsstring or array<string>[]精确且区分大小写的动画 ID。
selector.animation_patternsstring or array<string>[]Java 正则匹配整个动画 ID;无效表达式拒绝配置档。
selector.categoriesstring or array<string>[]去除首尾空白并转小写的 Better Combat 类别;运行时类别缺失时不能通过非空列表。

选择器缺失或为 null 时成为空集合。基本值转换为字符串,数组去重并移除空字符串;只有类别会去除首尾空白并转小写。语法合法但未注册的物品/标签 ID 会接受,不过可能永远不匹配。正则匹配完整字符串,不是子串搜索。

几何字段

每一行都可用于 geometry.slash 或 geometry.spin。非有限值回退到对应样式默认值;非正寿命先回退再限制范围。

字段类型默认值作用
inner_radiusnumberslash 0.28; spin 0.42限制为 0–0.98;沿根点到尖端的比例,决定宽轨迹内边缘。
edge_inner_radiusnumberslash 0.72; spin 0.76限制为 inner_radius–0.995,决定窄轨迹内边缘。
tail_tapernumberslash 0.22; spin 0.10限制为 0.01–0.49,控制旧端平滑收窄。
head_tapernumberslash 0.10; spin 0.08限制为 0.01–0.49,控制实时端平滑收窄。
smoothingnumberslash 0.16; spin 0.08限制为 0–0.45;以2s的比例向按时间加权的邻点混合,再恢复刀身长度,端点保持不变。
lifetime_ticksnumberslash 4.5; spin 12正有限值,限制为 0.25–40 ticks(0.0125–2 秒)。默认分别为 0.225 与 0.6 秒;控制过期、淡出、采样密度和允许的最大捕获间隔。

材质字段与遮罩

每一行都可用于 sheet 或 edge。两个材质使用相同采样轨迹,但内半径不同。

字段类型默认值作用
textureidentifierminecraft:textures/misc/white.png完整纹理路径;无效标识符回退,配置档加载器不检查资源是否存在。
colorstring or array<number>sheet #BDEBFF8F; edge #ECFAFFFFRGB/RGBA 十六进制或 3–4 分量数组,规则见下文。
opacitynumbersheet 1; edge 0.92限制为 0–1,非有限值变为 0。
blendenum(translucent, additive)sheet translucent; edge additive不区分大小写,未知字符串使用该材质默认值。
shaderenum(auto, vanilla, satin)autovanilla 强制实体着色器;auto 与 satin 仅在没有活动 Iris 着色器包时使用 Satin。
blurbooleantrue纹理过滤开关。
mipmapbooleanfalse纹理 mipmap 开关。
flip_ubooleanfalse在缩放与滚动前反转纵向坐标。
flip_vbooleanfalse交换内外横向坐标。
depth_testbooleantruetrue 为 LEQUAL,false 为始终通过。
depth_writebooleanfalsetrue 同时写深度与颜色,否则只写颜色。
uvobject纹理缩放与滚动。
uv.u_scalenumber1纵向正有限缩放,否则回退。
uv.v_scalenumber1横向正有限缩放,否则回退。
uv.u_scrollnumber0沿 U 每游戏 tick 的有限 UV 位移;非有限值回退。
uv.v_scrollnumber0沿 V 每游戏 tick 的有限 UV 位移;非有限值回退。

十六进制颜色接受 6 位 RGB 或 8 位 RGBA,可带 # 前缀;RGB 默认完全不透明。数组接受 3 或 4 个分量。任一显式分量超过 1 时,全部通道除以 255,否则按归一化值处理。数组省略 alpha 时,两种模式都保持完全不透明。通道限制为 0–1。颜色字符串错误、数组长度错误或转换异常时使用材质默认值。

U从可见尾部的0递增到实时头部的1,按累计扫过距离占可见总距离的比例计算。翻转前,V 在该通道内边界为 0,在武器尖端边界为 1。滚动使用世界时间加 tick delta,对 10000 ticks(500 秒)取模后乘以每 tick 滚动量。需要透明区域时应编写 PNG 的 alpha 通道;RGB 为黑但 alpha 不透明,并不等于透明遮罩。

最终顶点 alpha 由颜色 alpha、材质不透明度、平滑寿命淡出与本地不透明度百分比共同相乘。Satin 将纹理采样 RGBA、顶点颜色与 ColorModulator 相乘,并丢弃 alpha 低于 0.003 的片元。宽轨迹与边缘都关闭面剔除。启用 Iris 着色器包后,即便 shader: satin 也走实体格式后备路径,使用全亮光照与朝向摄像机的法线。该路径使用物品实体目标,Satin 世界通道使用半透明目标。纹理过滤、深度测试与深度写入遵守各材质设置。

内置配置档

isekaiexpansion:default_hybrid 没有选择器,优先级为 −10000,根部内缩 0.04,窗口偏移 −0.015,终点 0.98,采样数 32,最小距离 0.003,最大分段距离 2.5。几何样式、距离与视角开关保持默认。宽轨迹使用 isekaiexpansion:textures/weapon_slash/dcsd_slashtrail_003.png,颜色 #5CCBFFE0,不透明度 0.68;边缘使用 isekaiexpansion:textures/weapon_slash/dcsd_slashlead_001.png,颜色 #E8FBFFFF,不透明度 0.88。两者都翻转 V。

子项 ID物品选择器优先级宽轨迹颜色边缘颜色
isekaiexpansion:demon_swordisekaiexpansion:demon_sword100#f200b5#b80089
isekaiexpansion:demon_spearisekaiexpansion:demon_spear100#1ed4c4#0cb0a2
isekaiexpansion:crescent_roseisekaiexpansion:crescent_rose100#ac0000#870303

各子项都继承 isekaiexpansion:default_hybrid。Crescent Rose 的配置档不会绕过默认本地物品排除项。

验证与重载

读取或解析失败会警告并跳过该配置档。父项缺失、循环或无效,选择器 ID 或正则错误,未知 kind,以及严格类型字段错误,都可能导致拒绝。空文件是解析错误,空对象则有效。未知字段通常忽略,但已移除的顶层 edge_start 与 lifetime_ticks 只要出现就会拒绝,即使值为 null。

嵌套对象缺失或类型错误时使用空对象。root/tip 缺失、不是数组或长度不是 3 时整体回退;非有限坐标按分量回退,无法转换的坐标元素则拒绝配置档。正距离字段为零、负数或非有限值时回退。材质枚举拼写错误会回退,与 kind 拼写错误不同。此处不检查纹理是否存在,也不检查物品/标签是否已注册。

客户端资源重载会重建不可变的排序配置列表,并清空动画元数据、平面模型轮廓和渲染层缓存。它不会清空活动轨迹;每条轨迹继续持有攻击开始时捕获的配置档。服务端数据包 /reload 不属于此资源监听器。

扩展方式

在客户端初始化期间注册监听器。下例将调用者提供的效果回调限定到刺击开始;导入 WeaponAttackFxEvents、WeaponAttackFxContext 与 java.util.function.Consumer。

public static void registerStabEffect(
        Consumer<WeaponAttackFxContext> effect) {
    WeaponAttackFxEvents.ATTACK_ANIMATION_START.register(context -> {
        if (context.isStab()) {
            effect.accept(context);
        }
    });
}
上下文成员约定
entity()实时实体引用,不是快照。
weapon()事件创建时物品快照的防御性副本。
animationId()、animatedHand()非 null 动画标识符字符串与 Better Combat 手部类型。
durationTicks()、upswing()由控制器按前文规则清理;公开记录构造器本身不限制它们。
kind()非 null 视觉类型;isStab() 检查 STAB。
slashProfileId()可空的选中配置档 ID,包括最终不会绘制的配置档。
isLocalPlayer()、isMob()是否为本地玩家同一实例,或是否属于 MobEntity。

自动分类先将动画 ID 转小写,依次检查刺击词(stab、thrust、pierce、lunge、poke),重击词(slam、smash、ground),斩击词(slash、sweep、swing、spin、uppercut、upperslash、_lr、_rl、_lrl、_rlr),否则为 OTHER。NONE 只能由配置档显式指定。这是视觉分类,不代表命中确认。

回调在客户端动画调用路径上同步执行,不能取消攻击或替换配置档。监听器应自行处理异常并实施效果策略:本地排除或关闭轨迹后,事件仍会触发。注册与渲染代码必须保持客户端专用。自定义物品渲染集成应以会话包围采集,转发其消费者,并在 finally 中调用 end();会话结束是幂等的,嵌套上下文使用线程局部存储。

验证

运行./gradlew.bat compileJava。观测姿态之间按连续旋转重建,无法补回未被观测到的反向运动。

检查本地/远程玩家、生物、双手分别攻击、左主手、双持与双手型动画。比较第一人称手部、Player Animator 第一人称通道、第三人称与人形变身。测试普通斩击、刺击/重击不绘制、身体旋转、移动攻击者、低帧率与分段跳变。

配置档需测试仅覆盖颜色的继承、数组替换、选择器 AND/OR、同优先级排序、错误正则、循环继承、已移除几何键和纹理缺失。在攻击中重载,区分活动轨迹保留配置与新攻击使用新配置的行为。检查 Iris 开关、阴影通道、深度开关、不透明度为零、排除项变化和数量上限淘汰。确认关闭轨迹后事件仍触发,并确认监听器不会向动画路径抛出异常。

相关页面