一个 Bedrock 皮肤包由四个文件组成,而其中两种失败模式是静默的
两个必须互不相同的 UUID、一个并非可选的 lang 文件,以及会剥离标点的皮肤键——于是“My Skin!”和“My Skin?”会变成同一个皮肤,却不报任何错误,重新导入用相同 UUID 打包的同一个皮肤包,游戏会静默保留原来那一份。
一个 .mcpack 就是一个改了扩展名的 zip,而其中的皮肤包恰好需要四种文件:
manifest.json a header and a skin_pack module, each with its own UUID
skins.json one entry per skin: texture, geometry, and a KEY
texts/en_US.lang the display names — skins.json holds only keys
*.png the textures themselves
四种里有三种很容易做对。真正有意思的失败全都来自它们之间的关系。
两个 UUID 必须不同
manifest.json 在它的 header 里带一个 UUID,在它的 skin_pack 模块里带另一个:
{
"format_version": 1,
"header": { "name": "…", "uuid": "…", "version": [1, 0, 0] },
"modules": [ { "type": "skin_pack", "uuid": "…", "version": [1, 0, 0] } ]
}
它们是两个彼此独立的身份——包本身,以及包里的东西——把同一个值用在两处是实实在在的错误,尽管构建时不会有任何东西报怨。
还有第二个更微妙的后果:用相同 UUID 重新导入会被当作同一个包,Minecraft 会静默保留它已有的那份。编辑一个包、用相同的标识符重新构建并重新导入,看起来就像什么都没发生。本页的每次构建都会拿到全新的 UUID,正是出于这个原因;这也意味着重新导入一个改过的包需要重新下载,而不是把旧包重新打包成 zip。
lang 文件并非可选
skins.json 从不包含显示名称。它包含的是一个键,而 texts/en_US.lang 把这个键映射到文本:
skinpack.<PackKey>=My Pack
skin.<PackKey>.<SkinKey>=My First Skin
不带 lang 文件就发布这个包,什么都不会报错——每个皮肤只会以它的原始键出现在游戏里。这就是手工拼装的包看起来坏掉了、校验却一切正常的最常见原因。
键会剥离标点,重复项会合并
键必须是字母数字。其他一切都会被移除:
| 你的名称 | 键 |
|---|---|
My Skin! | MySkin |
My Skin? | MySkin |
Skin #2 | Skin2 |
!!! | Skin1(回退,按位置) |
前两行就是陷阱。两个在人类看来截然不同的皮肤名会变成同一个键,而重复的键不会报错——它会在游戏里静默合并成一个皮肤。你上传十二个皮肤,拿到十一个,而且没有任何东西告诉你丢的是哪一个。
这里的构建器对此的防御是:给它已经用过的任何键追加一个计数器,于是第二个 MySkin 会变成 MySkin2。如果你手工拼装一个包,这一点值得知道:你需要检查的是剥离后的名称,而不是你输入的那些。
一个完全由标点组成的名称什么都不会剩下,于是它会回退到 Skin 加上它在列表中的位置。
Slim 需要正确的几何字符串
每个条目都会指定一个模型,而模型有两种:
geometry.humanoid.custom—— 经典,4 像素手臂geometry.humanoid.customSlim—— slim,3 像素手臂
搞错这一点,通常就会导致 一个 Alex 比例的皮肤长出了 Steve 的方块手臂。纹理没问题;被要求的是那个模型。包构建器会根据 slim 手臂留空的那两列(x 54–55)来猜测它,并允许你逐个条目修正,但手写的 skins.json 必须明确写出来,而这两个字符串在末尾相差四个字符。
条目还会带上 "type": "free",而披风是一个可选的额外 PNG,从同一个条目里引用——披风是随皮肤包一起走的,而不是作为单独的下载。