简体中文
实体模型与动画
自定义生物可以挂上 3D 模型, 并配置标准动作, K帧与距离分级 (LOD)
EmakiMobs 可以把自定义生物挂到 ModelEngine 或 BetterModel 渲染的 3D 模型上, 并驱动标准动作, 关键帧 (K帧) 事件与距离分级 (LOD). 两个模型后端都是可选依赖: 一个都没装时, 模型功能会不报错地关闭, 生物照样以原版形态运行
安装前提
- 安装 ModelEngine (付费) 或 BetterModel (免费开源) 插件本体
- 在对应插件的
blueprints/目录放入 Blockbench 导出的.bbmodel蓝图, 并生成/接入含模型贴图的资源包 - 在 EmakiMobs 的
config.yml的model:节选择后端 (见下文) - 在生物定义的 YAML 里写
model:节 (见下文)
蓝图名在两个后端里怎么解析由各自的插件决定; EmakiMobs 只按名字字符串去查, 不做额外加工
主配置 model: 节
| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
api | string | 否 | auto | auto / model_engine / better_model (另接受别名 modelengine / bettermodel, 大小写不敏感) | auto 会自动选 (先试 ModelEngine, 不行再试 BetterModel). 取值非法时预检报 WARN 并回退 auto; 指定或者选中的后端没安装时, 模型功能关闭并打印提示 |
view_distance | double | 否 | 48.0 | — (格) | 模型视距. NEAR 档按这个值设置可见范围; MID 档 ModelEngine 会收缩到 min(view_distance, mid), BetterModel 只给这个距离内的玩家生成模型; FAR 档 ModelEngine 视距归零, BetterModel 暂停 tracker |
lod.near | double | 否 | 16.0 | 需 ≤ lod.mid | LOD 的下界 (格). 距离不超过这个值就是 NEAR (完整视距) |
lod.mid | double | 否 | 32.0 | 需 ≥ lod.near, ≤ lod.far | LOD 的中界 (格). near–mid 之间是 MID (降低了视距); 超过 mid 就算 FAR (mid–far 之间同样是 FAR), 所以它才是真正决定隐藏的那条界线 |
lod.far | double | 否 | 48.0 | 需 ≥ lod.mid | 距离太远时提前结束判定的上界: 最近玩家的距离超过这个值就直接当成 FAR. 它小于 lod.mid 时, MID 档会有一部分距离永远用不到 |
movement_check_interval_ticks | int | 否 | 5 | ≥ 1 (tick) | 多久检查一次移动状态 (idle/walk/run) 和 LOD; 只在档位或者状态变化时才下发给模型后端 |
death_detach_delay_ticks | int | 否 | 40 | ≥ 0 (tick) | 死亡动画开始后, 再等多少 tick 才卸下模型实体 |
walk_speed_threshold | double | 否 | 0.12 | — (格/tick) | 水平速度达到这个值就算在行走 |
run_speed_threshold | double | 否 | 0.28 | — (格/tick) | 水平速度达到这个值就算在奔跑 (判定时先看这个阈值, 再看行走阈值) |
transitions.<动作>_fade_ticks | int | 否 | idle/walk/run 为 5, death 为 0, hurt 为 2, attack/skill/interact 为 3 | ≥ 0 (tick) | 过渡混合的时长, 交给模型引擎自己补间. <动作> 只认 idle, walk, run, death, hurt, attack, skill, interact 这八个固定键, 其他键不会被读取; 负值按 0 处理 |
三档的行为: NEAR 用完整视距渲染; MID 降级 (ModelEngine 把视距收缩到 min(view_distance, mid), BetterModel 只给这个距离内的玩家生成模型); FAR 时 ModelEngine 把模型视距设成 0, BetterModel 暂停这个模型的 tracker
下面的示例把后端强制设为 BetterModel, 并把中距档收紧到 24 格:
yaml
model:
api: "better_model"
lod:
near: 12.0
mid: 24.0
far: 48.0生物定义 model: 节
yaml
model:
blueprint: "bone_lord" # 必填: 模型蓝图名
api: "model_engine" # 可选: 覆盖全局 model.api, 为该生物指定后端
scale: 1.2 # 可选: 模型缩放, 默认 1.0
animations: # 可选: 标准动作 → 引擎动画名映射
idle: "breath"
walk: "walk_slow"
run: "run_fast"
death: "die"
hurt: "flinch"
attack: "swing"
skill: "cast"
interact: "nod"
keyframes: # 可选: 为任意动画定义 K帧时间轴
attack:
duration_ticks: 24 # 时间轴总长 (tick)
loop: false # 是否循环
priority: "action" # ambient|utility|action|reactive|terminal
conflict: "replace" # replace|reject 同优先级冲突策略
frames:
- tick: 0
action: "self | play_sound sound=minecraft:entity.wither.shoot"
- tick: 10
label: "impact"
action: "self | spawn_particle particle=flame"
lod: # 可选: 覆盖全局 model.lod, 为该生物指定独立的档位边界
near: 12.0
mid: 24.0
far: 40.0| 字段 | 类型 | 必填 | 默认值 | 取值范围 | 说明 |
|---|---|---|---|---|---|
blueprint | string | 是 (该节内) | 无 | 后端 blueprints/ 中的蓝图名 | 缺失或者空白时整节被忽略, 并打一条加载告警 (console.model_missing_blueprint). 运行时如果后端找不到这个蓝图, 会打 console.model_blueprint_missing 告警并跳过挂载 |
api | string | 否 | 无 (继承全局 model.api) | auto / model_engine / better_model | 这只生物用哪个模型后端, 会覆盖全局配置. 取值非法时打 loader.model_api_invalid 告警并回退到全局配置 |
scale | double | 否 | 1.0 | — | 模型的缩放倍率, 挂载和 mob_set_model 热切换时都会用到 |
animations.<动作键> | string | 否 | 无 (使用内置默认动画名) | 后端蓝图中存在的动画名 | 动作键到引擎动画名的对应. 键名和值都会被规范化 (键转小写, 值去掉空白); 空白值会被忽略 |
keyframes.<动作键> | section | 否 | 无 | — | K帧的时间轴; 键名就是动作键 (会规范化成小写). 不是标准动作的键 (不在 idle... interact 之内) 会按 utility 优先级注册, 不循环 |
keyframes.<动作键>.duration_ticks | int | 否 | 20 | ≥ 1 | 时间轴的总时长 (tick). 小于 1 时整条时间轴会被丢弃并打告警; 循环动画把这个值当作环绕点 |
keyframes.<动作键>.loop | boolean | 否 | 标准动作取该动作的内置值 (idle/walk/run 为 true, 其余为 false); 非标准动作键为 false | true / false | 是否循环播放 |
keyframes.<动作键>.priority | string | 否 | 标准动作取该动作的内置优先级; 非标准动作键为 utility | ambient / utility / action / reactive / terminal (大小写不敏感) | 播放的优先级; 认不出来的取值按 ambient 处理 |
keyframes.<动作键>.conflict | string | 否 | replace | replace / reject (大小写不敏感) | 同优先级冲突时的处理策略; 认不出来的取值按 replace 处理 |
keyframes.<动作键>.frames | list | 否 | [] | 元素为 {tick, action, label} 映射 | 帧的列表. 帧按列表顺序执行, 不会排序; 缺 tick 或者 tick 不是数字的帧会被丢弃, 并打告警 (loader.model_keyframe_missing_tick) |
keyframes.<动作键>.frames[].tick | int | 是 (帧内) | 无 | ≥ 0 (tick) | 在哪个 tick 触发; tick: 0 会在动画开始时立刻执行.不会把它截断到 duration_ticks 之内 -- 如果某帧的 tick 比时长还大, 播放会一直等到那个 tick 才继续/收尾 |
keyframes.<动作键>.frames[].action | string | 否 | "" | 一行 CoreLib 动作管道 | 这一帧要执行什么; 空串的帧只通知监听者, 不执行动作, 编译失败会在控制台打告警 |
keyframes.<动作键>.frames[].label | string | 否 | "" | — | 帧的标签, 会随帧通知传给动画监听者 (诊断用) |
lod.near / lod.mid / lod.far | double | 否 | 16.0 / 32.0 / 48.0 | — | 这只生物的 LOD 档位边界, 会覆盖全局 config.yml 的 model.lod; 只写其中几个时, 没写的项按左侧默认值补齐 (不继承全局值) |
model:节可以整节都不写; 不写的话这只生物就不挂载模型model.api与model.lod按生物生效: 后端桥按api的取值解析并重复使用, LOD 档位按这只生物自己的边界判定; 没写api时继承全局model.api, 没写lod时继承全局model.lod
标准动作
| 动作 | 触发时机 | 默认优先级 | 循环 |
|---|---|---|---|
idle | 挂载之后 / 瞬时动作播完回落 / 静止时 | ambient | 是 |
walk | 水平速度 ≥ walk_speed_threshold | ambient | 是 |
run | 水平速度 ≥ run_speed_threshold | ambient | 是 |
attack | 攻击命中 (on_damage_give) | action | 否 |
skill | 技能释放 (经 mob_play_animation 触发) | action | 否 |
interact | 交互 (经 mob_play_animation 触发) | utility | 否 |
hurt | 受伤 (on_damage_take) | reactive | 否 |
death | 死亡 (on_death) 时, 播完再延迟卸下 | terminal | 否 |
优先级规则: 高优先级的动作抢占当前正在播的; 同优先级按 conflict 的策略替换或拒绝; 低优先级的请求被拒绝. terminal (death) 不会被抢占. 没有定义时间轴的标准动作, 用默认时长 (20 tick) 和该动作内置的优先级/循环设置
关键帧 (K帧)
K帧时间轴由 EmakiCoreLib 的动画调度器驱动: 动画开始播放时, tick: 0 的帧立刻执行, 后面的帧在对应的 tick 上, 于实体所属线程精确触发. 帧载荷就是一行 CoreLib 动作管道 (已注册的段落都能写, 比如 spawn_particle, play_sound, 属性修改, 状态切换), 所以动作组合和表达式占位符直接沿用全系管道语法
动作段
在任意 CoreLib 动作行里 (技能, 掉落动作, 第三方插件) 都可以写这三段:
text
self | mob_play_animation animation=spin
self | mob_stop_animation animation=spin
self | mob_set_model blueprint=bone_lord_phase2| 段落 | 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|---|
mob_play_animation | animation | string | 是 | 无 | 生物 model.animations / model.keyframes 里定义过的动作键; 缺失或者空白时按配置错误返回 |
mob_stop_animation | animation | string | 是 | 无 | 含义同上; 用来停止对应定义的播放 |
mob_set_model | blueprint | string | 是 | 无 | 新蓝图的名字; 缺失或者空白时按配置错误返回, 后端找不到这个蓝图时会打告警并保持原模型 |
- 这三个段落都要求目标能解析成受管生物实体; 目标不是受管生物时返回
skipped, 不报错 mob_set_model支持运行时热切换蓝图 (沿用这只生物定义里的scale), 切换后会自动回到 idle
第三方插件扩展
其他插件可以通过 EmakiCoreLibApi 注册自定义动画定义, 播放和监听 (见 CoreLib 动画系统). EmakiMobs 的动画调度也会读这张注册表, 所以第三方注册的 mob:<id>:<key> 定义可以被 mob_play_animation 直接引用
故障排查
| 现象 | 检查 |
|---|---|
| 生物没有模型 | 先看后端插件装了没有; model.api (全局或生物级) 是否指向已安装的后端; 启动日志里有没有出现 未找到可用的模型后端 (auto 且两个后端都不可用) 或者 模型后端 '<名称>' 未安装 |
| 出现蓝图缺失的告警 | 看有没有 console.model_blueprint_missing; 蓝图名和后端 blueprints/ 里的文件名 (去掉后缀) 是否一致; 后端有没有执行过 /meg reload 或者重启 |
| 动画不播放 | 引擎动画名在蓝图里存不存在; animations: 映射里的动作键写对没有 |
| K帧不触发 | frames[].action 是不是合法的动作行 (编译失败会打 Animation keyframe line did not compile 告警); frames[].tick 有没有按升序写 (帧是按列表顺序执行的, 不会排序); tick 是不是写成了比 duration_ticks 还大的值 (不会截断) |
| 距离远时模型消失 | 这是 LOD 的设计行为; 分界点是 lod.mid, 不是 lod.far, 先把 lod.mid 调大, 需要时再调大 lod.far (早退上界) 和 view_distance (NEAR/MID 档视距). 如果只有某只生物这样, 检查它 model.lod 的覆盖值 |