Skip to content

自定义模型 ​

模型(Geometry)定义了实体、附着物(attachable)和方块的可视外形。基岩版使用自己独有的模型格式,与 Java 版完全不兼容。推荐使用 Blockbench 进行模型制作,它是目前社区最主流的基岩版模型编辑器。

前置条件

本页面主要讲解实体/附着物模型(minecraft:geometry 格式,format_version: "1.12.0")。方块模型使用中国版独有的 netease:block_geometry 格式,将在本文末尾简要介绍,详细内容请参考 自定义基础方块。在开始之前,建议先阅读 基础概念 和 自定义基础生物。

概述 ​

在基岩版中,模型由一系列**骨骼(Bone)和方块体(Cube)**组成,用 JSON 文件描述。每个骨骼可以包含若干方块体,骨骼之间通过父子关系组成层级树。动画系统可以对骨骼进行旋转、平移、缩放,从而实现行走、攻击等效果。

模型文件放在资源包的 models/entity/ 目录下,扩展名为 .geo.json(也可以使用 .json,但 .geo.json 是推荐的命名约定)。

模型的使用场景:

使用者引用方式说明
实体(Entity)客户端实体定义 RP/entity/ 中的 geometry 字段最常见的用法,为生物定义外形
附着物(Attachable)RP/attachables/ 中的 geometry 字段用于自定义 3D 物品,模型挂载在玩家手上
方块(Block)blocks.json 中的 netease_model 或组件引用使用中国版独有的 netease:block_geometry 格式

基岩版 vs Java 版

基岩版模型是纯 JSON 格式,不支持 Java 版的 .java 模型或 OBJ/FBX 等通用 3D 格式(方块模型也不支持)。如果你从 Java 版迁移,需要在 Blockbench 中重新制作或转换。

Blockbench 工作流 ​

Blockbench 是一款免费、开源的体素模型编辑器,原生支持基岩版模型格式。以下是从零开始制作一个自定义生物模型的完整流程。

1. 创建项目 ​

  1. 打开 Blockbench,点击 File > New > Bedrock Entity
  2. 填写项目设置:
    • File Name:文件名,如 custom_mob
    • Model Identifier:模型标识符,如 geometry.mymod.custom_mob(必须以 geometry. 开头)
    • Texture Width / Height:贴图尺寸,如 64 x 32(必须是 2 的幂次或其整数倍)
    • 勾选 Box UV 可以使用盒式 UV 映射(适合简单模型),取消勾选则使用逐面 UV

标识符命名规范

模型标识符必须以 geometry. 开头,推荐格式为 geometry.命名空间.模型名,例如 geometry.mymod.ghost。标识符中不能包含冒号(冒号在旧版格式中用于模型继承,现代格式已不支持)。只允许使用小写字母、数字、下划线和点号。

2. 搭建骨骼结构 ​

  1. 在右侧 Outliner 面板中点击 Add Group 创建骨骼(Bone)
  2. 按 F2 重命名骨骼,例如 body、head、left_leg、right_leg
  3. 通过拖拽调整骨骼的父子关系(将子骨骼拖到父骨骼上)
  4. 选中骨骼,在左下角设置 Pivot Point(旋转中心点)

骨骼是动画的基本单位。合理的骨骼层级对后续动画制作至关重要。

3. 添加方块体 ​

  1. 选中目标骨骼,点击 Add Cube 添加方块体
  2. 使用顶部工具栏的移动、缩放、旋转工具调整方块体的位置和大小
  3. 方块体的尺寸以像素为单位,1 个单位 = 游戏中 1/16 格(即 1 像素大小)

4. 贴图与 UV 映射 ​

  1. 在左侧面板中点击 Create Texture 创建空白贴图,或将 PNG 文件拖入 Blockbench
  2. 切换到 Paint 模式绘制贴图
  3. 如果使用逐面 UV(Per-face UV),可以在 UV 面板中单独调整每个面的映射区域
  4. 如果使用盒式 UV(Box UV),Blockbench 会自动展开贴图

5. 导出模型 ​

  1. 点击 File > Export > Export Bedrock Geometry
  2. 保存到资源包的 RP/models/entity/ 目录
  3. 文件名建议为 模型名.geo.json,例如 custom_mob.geo.json

导出后会生成一个标准的 format_version: "1.12.0" 格式的模型 JSON 文件。

模型文件格式 ​

实体/附着物模型使用 format_version: "1.12.0" 格式。一个模型文件可以包含多个几何体定义。

顶层结构 ​

json
{
  "format_version": "1.12.0",
  "minecraft:geometry": [
    {
      "description": { ... },
      "bones": [ ... ]
    }
  ]
}
键类型说明
format_versionstring固定为 "1.12.0"
minecraft:geometryarray几何体数组,每个元素是一个完整的模型定义

description 字段 ​

json
"description": {
  "identifier": "geometry.mymod.custom_mob",
  "texture_width": 64,
  "texture_height": 32,
  "visible_bounds_width": 2,
  "visible_bounds_height": 2.5,
  "visible_bounds_offset": [0, 1, 0]
}
键类型说明
identifierstring模型的唯一标识符,必须以 geometry. 开头
texture_widthint贴图宽度(像素),用于计算 UV 坐标
texture_heightint贴图高度(像素),用于计算 UV 坐标
visible_bounds_widthfloat可选。可见范围宽度(格),用于视锥剔除。默认为 0
visible_bounds_heightfloat可选。可见范围高度(格),用于视锥剔除。默认为 0
visible_bounds_offsetarray可选。可见范围偏移 [x, y, z],一般设为模型中心。默认为 [0, 0, 0]

visible_bounds 的重要性

visible_bounds_width 和 visible_bounds_height 定义了模型的可见判定范围。如果设得太小,当实体在屏幕边缘时模型可能突然消失;如果设得太大,会增加不必要的渲染开销。对于标准体型的生物,width: 1 + height: 2 + offset: [0, 1, 0] 是合理的默认值。

完整示例:自定义生物模型 ​

以下是一个包含 4 个骨骼(body、head、left_leg、right_leg)的完整自定义生物模型。该生物高约 1.5 格,身体为长方体,头部为正方体,两条腿在身体下方。

json
{
  "format_version": "1.12.0",
  "minecraft:geometry": [
    {
      "description": {
        "identifier": "geometry.mymod.custom_mob",
        "texture_width": 64,
        "texture_height": 32,
        "visible_bounds_width": 1,
        "visible_bounds_height": 2,
        "visible_bounds_offset": [0, 0.75, 0]
      },
      "bones": [
        {
          "name": "body",
          "pivot": [0, 12, 0],
          "cubes": [
            {
              "origin": [-4, 8, -3],
              "size": [8, 8, 6],
              "uv": [0, 0]
            }
          ]
        },
        {
          "name": "head",
          "parent": "body",
          "pivot": [0, 16, 0],
          "cubes": [
            {
              "origin": [-3, 16, -3],
              "size": [6, 6, 6],
              "uv": [28, 0]
            }
          ]
        },
        {
          "name": "left_leg",
          "parent": "body",
          "pivot": [2, 8, 0],
          "cubes": [
            {
              "origin": [0, 0, -1.5],
              "size": [4, 8, 3],
              "uv": [0, 14]
            }
          ]
        },
        {
          "name": "right_leg",
          "parent": "body",
          "pivot": [-2, 8, 0],
          "cubes": [
            {
              "origin": [-4, 0, -1.5],
              "size": [4, 8, 3],
              "uv": [14, 14]
            }
          ],
          "mirror": true
        }
      ]
    }
  ]
}

骨骼层级关系:

body (根骨骼)
├── head
├── left_leg
└── right_leg

UV 布局说明:

骨骼UV 起点尺寸 (宽×高×深)占用贴图区域
body[0, 0]8×8×6盒式 UV 展开
head[28, 0]6×6×6盒式 UV 展开
left_leg[0, 14]4×8×3盒式 UV 展开
right_leg[14, 14]4×8×3镜像 left_leg

在客户端实体中引用 ​

将模型文件保存为 RP/models/entity/custom_mob.geo.json 后,在客户端实体定义中引用:

json
{
  "format_version": "1.10.0",
  "minecraft:client_entity": {
    "description": {
      "identifier": "mymod:custom_mob",
      "materials": {
        "default": "entity_alphatest"
      },
      "textures": {
        "default": "textures/entity/custom_mob"
      },
      "geometry": {
        "default": "geometry.mymod.custom_mob"
      },
      "render_controllers": [
        "controller.render.default"
      ]
    }
  }
}

骨骼 (Bones) ​

骨骼是模型的骨架结构单元。每个骨骼可以包含方块体、定位器,以及通过 parent 字段指定的父子关系。动画系统通过骨骼名称来对模型进行变换。

骨骼字段 ​

键类型说明
namestring骨骼名称,在同一模型中必须唯一。动画通过此名称引用骨骼
parentstring可选。父骨骼的名称。省略时该骨骼为根骨骼
pivotarray(float)旋转中心点 [x, y, z],动画旋转时以此为轴心
rotationarray(float)可选。初始旋转角度 [rx, ry, rz](度),默认为 [0, 0, 0]
cubesarray(object)可选。该骨骼包含的方块体列表
locatorsobject可选。定位器,用于粒子/拴绳等附着点
mirrorbool可选。是否镜像该骨骼下所有方块体的 UV。默认为 false

骨骼层级 ​

骨骼通过 parent 字段形成树状层级结构。当父骨骼旋转时,所有子骨骼会跟随旋转。这是实现关节动画的基础。

root                    ← 根骨骼,通常不可见,放在原点
├── body                ← 身体
│   ├── head            ← 头部,跟随身体移动
│   ├── leftArm         ← 左臂
│   └── rightArm        ← 右臂
├── leftLeg             ← 左腿(挂在 root 下,不跟随身体旋转)
└── rightLeg            ← 右腿

Pivot(旋转中心) ​

pivot 是骨骼最重要的属性之一。它定义了骨骼旋转时的中心点。

  • 手臂的 pivot 应设在肩膀位置,这样旋转时手臂会像真实关节一样摆动
  • 头部的 pivot 应设在脖子底部,这样抬头/低头时看起来自然
  • 腿的 pivot 应设在大腿根部(连接身体的位置)
json
{
  "name": "head",
  "parent": "body",
  "pivot": [0, 24, 0],
  "cubes": [
    {
      "origin": [-4, 24, -4],
      "size": [8, 8, 8],
      "uv": [0, 0]
    }
  ]
}

在上面的示例中,头部的 pivot 设在 [0, 24, 0](身体顶部),这样当动画旋转头部时,它会围绕脖子位置转动。

Pivot 与 Origin 的关系

pivot 定义旋转中心,origin 定义方块体的最小角位置。两者是独立的。通常将 pivot 设在骨骼与其父级的连接点(即关节位置),而 origin 是方块体的实际起始坐标。

方块体 (Cubes) ​

方块体是模型中的基本视觉元素。每个方块体是一个长方体(Box),可以有自己的位置、大小、旋转和 UV 映射。

方块体字段 ​

键类型说明
originarray(float)方块体最小角(左下后角)的坐标 [x, y, z]
sizearray(float)方块体的尺寸 [宽, 高, 深],单位为像素(1 像素 = 1/16 格)
pivotarray(float)可选。方块体自身的旋转中心,仅当方块体有独立旋转时使用
rotationarray(float)可选。方块体的独立旋转 [rx, ry, rz](度)
uvarray/objectUV 映射,见下方详细说明
inflatefloat可选。膨胀值,正值让方块体在各方向上均匀扩大(不影响 UV),负值缩小。常用于制作外层覆盖(如盔甲层)
mirrorbool可选。是否镜像该方块体的 UV。默认为 false

UV 映射 ​

基岩版支持两种 UV 映射方式:

盒式 UV(Box UV):

使用 [u, v] 数组指定 UV 起点,引擎自动按方块体尺寸展开六个面。这是最简单的方式,适合大多数方块体。

json
{
  "origin": [-4, 12, -2],
  "size": [8, 12, 4],
  "uv": [16, 16]
}

盒式 UV 的贴图展开规则(从左到右):

         [上面]
[左面] [前面] [右面] [后面]
         [下面]

逐面 UV(Per-face UV):

使用对象格式,为每个面单独指定 UV 坐标和大小。可以精确控制每个面的贴图映射,也可以省略不需要渲染的面。

json
{
  "origin": [-4, 0, -4],
  "size": [8, 4, 8],
  "uv": {
    "north": { "uv": [0, 0], "uv_size": [8, 4] },
    "south": { "uv": [0, 0], "uv_size": [8, 4] },
    "east":  { "uv": [8, 0], "uv_size": [8, 4] },
    "west":  { "uv": [8, 0], "uv_size": [8, 4] },
    "up":    { "uv": [16, 0], "uv_size": [8, 8] },
    "down":  { "uv": [16, 8], "uv_size": [8, 8] }
  }
}

每个面的字段:

键类型说明
uvarray(float)该面 UV 区域的左上角坐标 [u, v]
uv_sizearray(float)该面 UV 区域的宽高 [width, height]

何时使用逐面 UV

当你需要以下功能时,使用逐面 UV:

  • 移除某些不可见的面(减少面数,提升性能)
  • 让不同面使用贴图上不同区域
  • 精确控制 UV 坐标和大小

inflate 的用法 ​

inflate 是一个非常实用的属性。正值会让方块体在所有方向上均匀膨胀,但 UV 映射保持不变。

常见用途:

  • 盔甲/外层:在原始身体方块体外加一层 inflate: 0.25 或 inflate: 0.5 的方块体,作为盔甲或衣服的外层
  • 解决 Z-fighting:两个面重叠时用 inflate: 0.01 微调来消除闪烁
  • 缩小方块体:当方块体的某个维度小于 1 像素时 UV 会出问题,此时可以将尺寸设为 1 然后用负 inflate 缩小
json
{
  "name": "hat",
  "parent": "head",
  "pivot": [0, 24, 0],
  "cubes": [
    {
      "origin": [-4, 24, -4],
      "size": [8, 8, 8],
      "uv": [32, 0],
      "inflate": 0.5
    }
  ]
}

上面的示例为头部添加了一个帽子层,比头部大 0.5 像素,常见于玩家模型的第二层。

定位器 (Locators) ​

定位器(Locator)是附着在骨骼上的命名点,用于在特定位置生成粒子、附着拴绳或放置其他效果。

格式 ​

定位器定义在骨骼的 locators 字段中:

json
{
  "name": "rightItem",
  "pivot": [-6, 15, 1],
  "parent": "rightArm",
  "locators": {
    "lead_hold": [-6, 15, 1]
  }
}

定位器的值是一个 [x, y, z] 坐标数组,表示定位器在模型空间中的位置。

常用定位器 ​

定位器名称用途通常附着的骨骼
lead_hold拴绳的连接点rightItem 或 rightArm

在粒子中使用 ​

动画和粒子系统可以通过定位器名称找到对应的模型空间位置。例如,在动画中可以在定位器位置触发粒子效果:

json
{
  "particle_effects": {
    "effect_name": {
      "effect": "mymod:flame_particle",
      "locator": "lead_hold"
    }
  }
}

玩家模型骨骼 ​

了解玩家模型的骨骼结构对于制作附着物(Attachable)和自定义 3D 物品至关重要。附着物需要引用玩家模型的骨骼名称来确定挂载位置。

完整骨骼树(Steve 模型) ​

root
└── waist
    └── body
        ├── head
        │   └── hat
        ├── cape
        ├── jacket (body 外层)
        ├── leftArm
        │   ├── leftSleeve (外层)
        │   └── leftItem (持物骨骼)
        └── rightArm
            ├── rightSleeve (外层)
            └── rightItem (持物骨骼,含 lead_hold 定位器)
├── leftLeg (挂在 root 下)
│   └── leftPants (外层)
└── rightLeg (挂在 root 下)
    └── rightPants (外层)

关键骨骼说明 ​

骨骼名称Pivot说明
root[0, 0, 0]模型根节点,位于脚底
waist[0, 12, 0]腰部,连接上下半身
body[0, 24, 0]身体主体(8x12x4)
head[0, 24, 0]头部(8x8x8),Pivot 在脖子
hat[0, 24, 0]头部外层,inflate: 0.5
rightArm[-5, 22, 0]右臂(4x12x4)
rightItem[-6, 15, 1]右手持物点,3D 物品挂载在此
leftArm[5, 22, 0]左臂(4x12x4)
leftItem[6, 15, 1]左手持物点
rightLeg[-1.9, 12, 0]右腿(4x12x4)
leftLeg[1.9, 12, 0]左腿(4x12x4)

附着物如何使用玩家骨骼 ​

制作自定义 3D 物品时,附着物的模型需要与玩家的 rightItem 骨骼对齐。更多细节请参考 自定义3D物品。

json
{
  "format_version": "1.10.0",
  "minecraft:attachable": {
    "description": {
      "identifier": "mymod:custom_sword",
      "materials": {
        "default": "entity_alphatest"
      },
      "textures": {
        "default": "textures/items/custom_sword"
      },
      "geometry": {
        "default": "geometry.mymod.custom_sword"
      },
      "render_controllers": [
        "controller.render.default"
      ]
    }
  }
}

方块模型 ​

中国版独有格式

方块模型使用 netease:block_geometry 格式,与实体的 minecraft:geometry 格式完全不同,不可混用。

中国版自定义方块支持使用 netease:block_geometry 格式的模型 JSON,让方块拥有非正方体的外观。方块模型文件位于资源包的 models/netease_block/ 目录下。

基本结构 ​

json
{
  "format_version": "1.13.0",
  "netease:block_geometry": {
    "description": {
      "identifier": "mymod:custom_chair_model",
      "textures": ["mymod:chair_texture"],
      "item_texture": "mymod:chair_item",
      "use_ao": false
    },
    "bones": [
      {
        "name": "root",
        "pivot": [0, 0, 0],
        "cubes": [
          {
            "origin": [2, 0, 2],
            "size": [12, 6, 12],
            "uv": {
              "north": { "uv": [0, 0], "uv_size": [12, 6] },
              "south": { "uv": [0, 0], "uv_size": [12, 6] },
              "east":  { "uv": [0, 6], "uv_size": [12, 6] },
              "west":  { "uv": [0, 6], "uv_size": [12, 6] },
              "up":    { "uv": [0, 12], "uv_size": [12, 12] },
              "down":  { "uv": [0, 12], "uv_size": [12, 12] }
            }
          }
        ]
      },
      {
        "name": "back",
        "parent": "root",
        "pivot": [0, 0, 0],
        "cubes": [
          {
            "origin": [2, 6, 12],
            "size": [12, 10, 2],
            "uv": {
              "north": { "uv": [0, 0], "uv_size": [12, 10] },
              "south": { "uv": [0, 0], "uv_size": [12, 10] }
            }
          }
        ]
      }
    ]
  }
}

与实体模型的关键区别 ​

特性实体模型 (minecraft:geometry)方块模型 (netease:block_geometry)
format_version1.12.01.13.0
贴图引用在客户端实体定义中引用在 description.textures 数组中直接引用 terrain_texture 短名称
贴图尺寸限制建议不超过 256x256不能超过 64x64
UV 模式盒式 UV 和逐面 UV仅逐面 UV
inflate支持不支持
骨骼 enable 字段不存在支持 MoLang 条件显隐(配合 netease:connection)
骨骼 texture 字段不存在支持为每个骨骼/方块体/面指定不同贴图索引
动画支持骨骼动画不支持

使用流程 ​

  1. 使用 Blockbench 创建 自由模型 项目(不要勾选盒式 UV)
  2. 贴图尺寸最大 64x64
  3. 通过 MC Studio 导入模型,自动生成 JSON 到 models/netease_block/
  4. 在 blocks.json 中用 netease_model 引用模型 identifier
  5. 配合 minecraft:block_light_absorption(设为 0)避免方块变黑
  6. 如需透明效果,配合 netease:render_layer 使用

更多方块模型细节请参考 自定义基础方块 - 方块模型。

模型优化建议 ​

移动端性能有限,模型的复杂度直接影响帧率。以下是一些优化建议:

面数与方块体数量 ​

指标推荐值上限
单个实体模型的方块体数量50 以内约 300(移动端建议不超过此值)
单个骨骼内的方块体数量20 以内无硬性限制,但影响渲染性能
骨骼总数30 以内无硬性限制
同屏实体数量控制在 20 以内取决于模型复杂度

贴图优化 ​

  • 实体贴图推荐使用 64x64 或更小的分辨率,除非确实需要更多细节
  • 多个简单实体可以共用同一张贴图以减少纹理切换
  • 贴图尺寸应为 2 的幂次(16、32、64、128、256),否则可能导致渲染问题
  • 避免使用超过 256x256 的贴图,移动端内存有限

减面技巧 ​

  • 使用逐面 UV 移除不可见的面(例如两个方块体紧贴的面)
  • 对称模型使用 mirror: true 共用 UV 空间
  • 避免大量重叠的半透明方块体(会产生严重的过度绘制)
  • 尽量减少骨骼层级深度(过深的层级在计算变换矩阵时开销更大)

方块体尺寸注意 ​

方块体的每个维度至少应为 1 像素。当尺寸小于 1 时,UV 计算会将其向下取整为 0,导致该面的贴图出现拉伸或不可见。如果确实需要更薄的方块体,可以将尺寸设为 1 然后使用 inflate: -0.5 来缩小。

材质 (Materials) ​

模型的渲染效果取决于客户端实体定义中指定的材质。不同材质决定了透明度和发光等效果。

材质名称说明
entity基础不透明材质
entity_alphatest支持透明像素(完全透明 or 完全不透明)
entity_alphablend支持半透明像素
entity_emissive不透明,Alpha 通道用作自发光
entity_emissive_alphaAlpha 通道用于自发光,纯黑+透明像素透明渲染

在客户端实体 JSON 中设置:

json
"materials": {
  "default": "entity_alphatest"
}

如果模型需要透明部分(如翅膀、玻璃效果),请使用 entity_alphatest 或 entity_alphablend。使用 entity_alphablend 时需要注意半透明排序问题。

常见问题 ​

模型在游戏中不可见 ​

可能原因:

  1. 模型文件路径错误 -- 确保文件在 RP/models/entity/ 目录下
  2. identifier 不匹配 -- 客户端实体中引用的 geometry 标识符必须与模型文件中的 description.identifier 完全一致
  3. visible_bounds 太小 -- 如果模型较大但 visible_bounds 设得很小,摄像机稍微移开模型就会被剔除。尝试增大 visible_bounds_width 和 visible_bounds_height
  4. format_version 错误 -- 实体模型应使用 "1.12.0",不要使用旧版格式
  5. 材质设置错误 -- 检查客户端实体定义中的 materials 是否正确

贴图拉伸或错位 ​

可能原因:

  1. texture_width/height 与实际贴图不一致 -- description 中的 texture_width 和 texture_height 必须与 PNG 文件的实际像素尺寸一致
  2. 方块体尺寸小于 1 -- 盒式 UV 模式下,尺寸小于 1 的维度会导致 UV 计算错误。使用 inflate 配合调整,或改用逐面 UV
  3. UV 坐标超出贴图范围 -- 检查所有 UV 坐标是否在 [0, 0] 到 [texture_width, texture_height] 范围内

骨骼旋转不正确 ​

可能原因:

  1. Pivot 位置不对 -- 旋转中心应设在关节位置。如果手臂的 pivot 设在手臂中间而非肩膀,旋转效果会不自然
  2. 父子关系错误 -- 检查 parent 字段。如果腿挂在 body 而不是 root 下,身体倾斜时腿也会跟着倾斜
  3. 动画引用的骨骼名不匹配 -- 动画文件中的骨骼名必须与模型中的 name 完全一致(区分大小写)

方块模型变黑 ​

在使用 netease:block_geometry 方块模型时,如果模型看起来是全黑的,需要在方块行为包 JSON 中添加:

json
"minecraft:block_light_absorption": {
  "value": 0
}

这是因为默认的光照吸收值为 15(完全不透光),导致方块内部没有光照。

半透明面闪烁(Z-fighting) ​

当两个面完全重叠时,渲染引擎无法决定哪个面在前,导致闪烁。解决方案:

  • 给其中一个方块体添加 "inflate": 0.01 或 "inflate": -0.01,让两个面微微错开
  • 重新调整方块体位置,避免完全重叠