FreeMinecraftModels 模型创作注意事项
本页记录了在 FreeMinecraftModels 代码库中可见的当前创作详情。内容有意保持保守:重点关注导入/运行时契约,而非每个 Blockbench 工作流偏好。
源格式
FreeMinecraftModels 目前接受:
.bbmodel文件,用于可编辑的源导入.fmmodel文件,用于精简的运行时就绪模型数据
正常的导入流程是:
- 将模型放置在
plugins/FreeMinecraftModels/imports - 运行
/fmm reload - 让 FreeMinecraftModels 将模型导入到活动模型集中并重建生成的资源包
文件夹角色
plugins/FreeMinecraftModels/imports
plugins/FreeMinecraftModels/models
plugins/FreeMinecraftModels/models_disabled
imports是手动模型导入和官方包下载在处理前的接收文件夹models包含已激活安装的模型内容models_disabled包含当前已禁用的已下载或已安装的包内容
较旧的安装可能存在一个首字母大写的 Models 文件夹。FreeMinecraftModels 的处理方式是:当规范的小写 models 存在时优先使用它,只有在它不存在时才回退到旧的 Models。在 Windows 和 macOS 上这两个名字本来就是同一个目录;在区分大小写的 Linux 文件系统上,旧安装仍能正常工作,但如果两个目录同时存在,只有小写的 models 会被读取。如果你发现两者都在,请合并到 models 中。
模型 ID
- 运行时模型 ID 来自文件名,不包含
.bbmodel或.fmmodel扩展名 - 使用稳定、唯一的文件名,因为 ID 是命令和 API 调用解析的目标
- Blockbench 动画引用基于名称,因此模型内重复或不清晰的命名比干净、明确的命名方案更容易造成问题
- 扩展名匹配不区分大小写,因此
.BBModel和.FMModel也会被接受
ID 规范化与冲突
文件名在成为运行时模型 ID 和资源包文件名之前会先被规范化:
- 转为小写
- 每一个不属于
a-z、0-9、.、_、-的字符都会被替换为_
因此 My Table.bbmodel、my table.bbmodel 和 my_table.bbmodel 全都规范化成同一个 ID:my_table。
在任何转换开始之前,FreeMinecraftModels 会扫描整个 models 目录树,检查是否有文件规范化后得到相同的 ID。当它发现冲突时:
- 每一个发生冲突的文件都会被拒绝 —— 它们都不会被加载,因此不存在悄悄的"最后一个胜出"
- 该模型 ID 在本次加载过程的剩余阶段被封锁
- 被拒绝的模型也会被排除在基岩版自定义实体资源包的导出之外
- 控制台会输出:
[FMM Models] Rejected normalized model ID collision '<id>'. These files normalize to the same ID: <paths>. Rename the files so every normalized model ID is unique; no colliding model was loaded.
修复办法始终是重命名文件,使规范化后的 ID 各不相同。最稳妥的习惯是从一开始就用小写加下划线来命名模型文件(stone_table.bbmodel),这样文件名与运行时 ID 就完全一致,也彻底消除了意外冲突的可能。
Blockbench 兼容性
- FreeMinecraftModels 会从
.bbmodel中读取meta.format_version,并根据其主版本号分支 - 缺少
meta块、缺少format_version,或者值无法解析,都会回退到版本4,并输出一条指明该模型的信息/警告 - 缺少
textures数组是被接受的,会被当作空纹理列表处理,而不会让导入器崩溃。此后基岩版自定义实体导出器会跳过该模型,因为基岩版导出至少需要一张纹理 - 提取出的纹理文件名会被统一规范为单一的
.png扩展名。诸如body.jpg、body.PNG或body这样的源名称,都会以body.png的形式写出并被引用 format_version4.x及更早:骨骼直接从outliner树中读取,该树内联携带骨骼名称format_version5.x及更新:outliner 的结构发生了变化。outliner现在是由纯 UUID 字符串和{uuid, isOpen, children}字典构成的嵌套树,其中没有 name 键,而真正的骨骼数据(包括name)保存在一个独立的扁平groups数组中。FMM 通过 UUID 把两者连接起来 —— 每个 outliner 节点会被替换为它在groups中匹配的条目,然后递归合并子节点 —— 因此骨骼名称、原点和旋转都能正常解析- 手工编辑或程序生成
.bbmodel文件时值得了解的影响:- 如果一个 v5 文件缺少
groups数组,它会被原样透传而不做合并,于是它的骨骼会丢失名称,保留前缀(tag_、h_、b_、m_、hitbox)也就不再被识别 outliner和groups之间的 UUID 必须完全一致;没有匹配 group 的 outliner 节点会被原样保留,而不是被丢弃- 不要把 v5 的
format_version与 v4 形态的 outliner 混在一起,反之亦然 —— 分支是根据声明的版本选择的,而不是根据实际结构
- 如果一个 v5 文件缺少
- 如果导入日志显示模型格式与 FreeMinecraftModels 不兼容,请首先将其视为模型格式问题,而不是 wiki 或命令问题
运行时重要的骨骼约定
当前转换器和骨架管线识别以下命名约定:
hitbox- 保留用于碰撞箱生成
- 应该清晰地定义模型碰撞箱,而不是用作视觉骨骼
- 必须是 outliner 中的顶级骨骼。导入器只在根层级查找它;嵌套在其他骨骼内部的
hitbox组会被当作普通骨骼处理 - 必须恰好包含一个立方体,用于定义运行时的宽(x)、深(z)和高(y)。多余的立方体会记录
has more than one value defining a hitbox! Only the first cube will be used;空的 hitbox 骨骼会记录has a hitbox bone but no hitbox cube!,并且不会生成碰撞箱 - 永远不会被动画化(指向
hitbox的动画轨道会被跳过)、永远不会被渲染,并被排除在基岩版几何体导出之外。在基岩版上,没有hitbox骨骼的模型会回退到1.0x2.0
tag_...- 模型获得名牌的唯一途径。参见下方的 名牌需要一个
tag_骨骼
- 模型获得名牌的唯一途径。参见下方的 名牌需要一个
h_...- 被视为头部骨骼
b_...- 非显示骨骼(运行时隐藏)。用于不应在游戏中渲染的结构性或组织性骨骼。
m_...- 骑乘点骨骼。带有此前缀的每个骨骼会在模型上创建一个可骑乘的座位位置。玩家或实体可以在运行时被挂载到这些位置上。多个
m_骨骼会创建多个座位。由MountPointManager内部管理。
- 骑乘点骨骼。带有此前缀的每个骨骼会在模型上创建一个可骑乘的座位位置。玩家或实体可以在运行时被挂载到这些位置上。多个
这些不仅仅是风格约定;它们会影响转换和运行时行为。
名牌需要一个 tag_ 骨骼
这是"发布出去的内容里怪物没有名字"最常见的单一原因,因此值得直说:
只有当模型中包含一根名称以 tag_ 开头的骨骼时,它才会获得名牌。 没有任何兜底机制。
它的工作方式:
- 导入时,任何名为
tag_...的骨骼都会获得一根平行的自动生成"元"骨骼(fmm_nametag_bone_<name>),并被标记为名牌骨骼。流水线中没有其他任何环节会设置该标记 - 运行时,骨架会收集这些被标记的骨骼,并在
tag_骨骼的原点处生成一个文本显示实体。它的位置和文本跟随该骨骼 ModeledEntity.setDisplayName(...)与setDisplayNameVisible(...)会遍历这个集合。没有tag_骨骼时,该集合为空,这两个调用都会静默地什么都不做 —— 没有错误、没有警告、也没有名字
为什么原版名牌无法替你兜底:
- 动态模型会对客户端隐藏其底层的生物实体(
setVisibleByDefault(false),外加隐身标志),因此原版怪物自己的名牌同样不会被渲染 - 最终结果就是一只完全没有名字的怪物,尽管调用方插件确实成功设置了显示名称
实用规则:
- 如果某个模型代表一个有名字的实体(EliteMobs Boss、任务 NPC,或任何插件会对其调用
setDisplayName的对象),请添加一根tag_骨骼 - 把它放在名牌应当悬浮的位置 —— 通常就在头部正上方
- 该骨骼不需要有立方体;它只是一个定位锚点。尤其是
tag_name,在生成物品模型定义时会被跳过,因此它永远不会作为几何体被渲染 - 允许存在多根
tag_骨骼;它们中的每一根都会得到自己的文本显示,显示同一个名字 - 如果你在控制台看到
nametag bone did not spawn name tag,说明该骨骼已被识别但它的文本显示未能生成 —— 这与完全没有tag_骨骼是两个不同的问题
自由漂浮的立方体
在 outliner 顶部声明、不属于任何组的立方体,会以纯 UUID 字符串而不是骨骼条目的形式到达。FreeMinecraftModels 会把它们挂到自动生成的根骨骼(freeminecraftmodels_autogenerated_root)上,因此它们仍会被渲染。它们无法被动画寻址,所以任何你打算做动画的东西都要放进一个真正的组里。
IK、空对象和定位器
当前代码确认支持:
- Blockbench 空对象作为 IK 控制器
- IK 链蓝图和运行时 IK 求解(FABRIK)
- 定位器解析
只有当空对象在 .bbmodel 中的条目同时带有 ik_source(骨链起始的骨骼)和 ik_target(骨链要够到的骨骼或定位器)时,它才会成为 IK 控制器。lock_ik_target_rotation 也会被读取。骨链是通过从目标沿骨骼层级向上走到源来发现的,因此两者必须真正连通。
重要的实际限制:
- 源/目标之间的关联是通过 UUID 建立的,因此重命名不会破坏它 —— 但如果任一 UUID 在模型中缺失,控制台会记录
IK chain in model <model>: Could not find source bone with UUID .../Could not find target with UUID ...,并跳过该骨链 - 如果沿层级向上走找不到从源到目标的路径,你会得到
Could not find path from source to target,该骨链被跳过 - 控制器的动画查找是基于名称的,因此控制器命名需要在模型结构和动画数据之间保持稳定
- 空对象上只有位置关键帧有意义 —— 它们会成为 IK 的目标偏移。空对象上的旋转和缩放轨道会被忽略
关于 IK 是如何逐帧被驱动的,参见动画。
1.21.4+ 输出拆分
FreeMinecraftModels 声明了 api-version: 1.21.4,因此 1.21.4 是最低服务器版本,你拿到的永远是现代的物品模型定义布局:
plugins/FreeMinecraftModels/output/FreeMinecraftModels/assets/freeminecraftmodels/items
旧版(1.21.4 之前)基于皮革马铠 override 的分支在代码中依然存在,但任何能加载当前插件的服务器都不会走到那里。如果你在看旧的笔记或旧的输出文件夹,你看到的差别就是这个。
展示模型 JSON(1.21.4+)
管理员可以在 .bbmodel 或 .fmmodel 文件旁边放置一个同名的 .json 文件(例如 table.bbmodel + table.json)。此 JSON 应从 Blockbench 导出为"Java Block/Item"模型,定义物品在手持或在物品栏中显示时的外观。
在导入时,FMM 会将此 JSON 复制到资源包输出中,并自动将其中的裸纹理引用重写为指向模型提取的纹理。如果不存在配套 JSON,该物品在游戏中将显示为普通纸张。
YML 中的自定义物品配置
配套的 .yml 配置文件(与模型同名)现在支持可选的物品字段。如果设置了 material:,模型也可作为自定义可手持物品使用。完整的 YML 格式如下:
isEnabled: true
scripts:
- my_script.lua
material: DIAMOND_SWORD # optional — if set, model is also a custom item
name: '&b&lMy Custom Sword' # optional — display name
lore: # optional
- '&7A custom weapon'
enchantments: # optional — format: ENCHANTMENT_NAME,LEVEL
- SHARPNESS,5
- FIRE_ASPECT,2
当 material: 存在时,模型会与道具一起显示在管理员内容浏览器中,并可作为功能性物品给予玩家。
基岩版和渲染路径注意事项
- 基岩版支持取决于
sendCustomModelsToBedrockClientsV2(默认值为true;替代了较旧的sendCustomModelsToBedrockClients键)以及周围的 Floodgate/Geyser/资源包路径 - 受支持版本上的 Java 客户端可以在启用
useDisplayEntitiesWhenPossible时使用展示实体渲染 - 不要假设在 Java 客户端上显示正确的模型对您的基岩版路径自动安全
实用创作建议
- 保持文件名稳定,因为它们会成为运行时 ID
- 保持控制器和动画命名明确,因为 Blockbench 动画查找是基于名称的
- 有意识地使用保留的虚拟骨骼名称(
hitbox、tag_、h_、b_、m_)—— 并且记住,任何需要显示名字的模型都必须有一根tag_骨骼,否则它会悄无声息地没有名字 - 如果你希望状态动画自动触发,就把它们命名为
spawn、idle、walk、attack和death;其他一切都是需要你自己触发的自定义动画(参见动画) - 打算用于
/fmm disguise的模型至少也应当附带idle,还可以加上sneak和jump,这两者只有伪装控制器才会识别(参见玩家伪装) - 在
/fmm reload后验证导入的输出,而不仅仅是在 Blockbench 内部 - 验证为目标 Minecraft 版本生成的包内容,尤其是
1.21.4+
弓和弩状态模型
FMM 支持自定义弓和弩物品的自动拉弓动画状态。无需配置 -- 只需使用正确的后缀命名模型文件,FMM 在资源包生成期间会自动检测状态集。
命名约定
| 后缀 | 用途 | 弓 | 弩 |
|---|---|---|---|
_idle | 手持物品,未拉弓或未装填 | 必需 | 必需 |
_draw_start | 刚开始拉弓 | 必需 | 必需 |
_draw_half | 拉到一半 | 必需 | 必需 |
_draw_full | 完全拉满 | 必需 | 必需 |
_charged | 已装填的弩(箭或火箭) | -- | 必需 |
弓需要四个模型(除 _charged 外全部)。弩需要全部五个。
示例文件布局
plugins/FreeMinecraftModels/imports/
cool_bow_idle.bbmodel
cool_bow_draw_start.bbmodel
cool_bow_draw_half.bbmodel
cool_bow_draw_full.bbmodel
cool_bow.yml <-- config uses the base name, not _idle
对于弩,您需要添加第五个文件 cool_bow_charged.bbmodel。
检测机制
- 检测在资源包生成时(启动时或
/fmm reload)自动进行。 - 只有
_idle模型在输出包中获得物品定义 JSON。拉弓和装填状态作为该定义中的条件条目引用。 - 只有基础名称获得 YML 配置文件。对于上述示例,配置是
cool_bow.yml,而不是cool_bow_idle.yml。
展示模型 JSON
每个状态模型可以有自己的配套 .json 展示模型(例如 cool_bow_idle.json、cool_bow_draw_full.json)。FMM 会自动将它们接入生成的物品定义中。有关生成的确切 JSON 结构,请参阅资源包输出。
超出范围
本页不尝试保证:
- 确切的 Blockbench UI 步骤
- 艺术工作流偏好
- 旧版本地 README 材料中描述的每个遗留
.bbmodel特性
这些细节的变化速度比上述已验证的运行时契约更快。