模型
模型是决定方块或物品外观形状和纹理的 JSON 文件,由不同尺寸的长方体元素组成,并为各面分配纹理。
物品按注册名称分配模型。例如,examplemod:example_item 使用 assets/examplemod/models/item/example_item.json。方块则先分配方块状态文件,流程稍复杂,详见下文。
规范
另见 Minecraft Wiki 的模型条目。
模型根对象可包含以下可选属性:
loader:NeoForge 添加,指定自定义模型加载器。parent:指定父模型,使用相对于models文件夹的资源位置。先继承父模型的全部属性,再由当前模型属性覆盖。常用父模型包括:minecraft:block/block:所有方块模型的共同父模型。minecraft:block/cube:使用 1×1×1 立方体的模型。minecraft:block/cube_all:六面纹理相同,如圆石、木板。minecraft:block/cube_bottom_top:四个侧面相同,顶面和底面各有纹理,如砂岩、錾制石英块。minecraft:block/cube_column:侧面纹理与顶底纹理分开,如原木、石英柱和紫珀柱。minecraft:block/cross:两个使用相同纹理的平面,分别顺时针和逆时针旋转 45°,俯视形成 X,用于草、树苗、花等植物。minecraft:item/generated:经典二维平面物品的父模型,多数物品使用。四边形由纹理生成,因此忽略elements。minecraft:item/handheld:看起来真正握在玩家手中的二维物品,主要用于工具。继承item/generated,同样忽略elements。minecraft:builtin/entity:除particle外不指定纹理。使用它时,BakedModel#isCustomRenderer()返回 true,以使用BlockEntityWithoutLevelRenderer。- 方块物品通常以对应方块模型为父模型,例如圆石物品使用
minecraft:block/cobblestone,但并非绝对。
ambientocclusion:是否启用环境光遮蔽,仅对方块模型有效,默认为 true。模型明暗异常时可尝试关闭。render_type:见渲染类型。gui_light:可为"front"(正面光照,适合二维模型)或"side"(侧面光照,适合三维模型,尤其是方块)。默认 side,仅对物品有效。textures:将名称(纹理变量)映射到纹理位置的子对象。变量可用于元素;也可在元素中引用但暂不赋值,让子模型指定。- 方块模型还应指定
particle纹理,用于落到方块上、跑过方块或破坏方块时的粒子。 - 物品模型可使用
layer0、layer1等图层纹理,编号大的绘制在编号小的上方。仅当父模型为item/generated时有效,最多 5 层(layer0 至 layer4)。
- 方块模型还应指定
elements:长方体元素列表。overrides:覆盖模型列表,仅对物品有效。display:保存不同视角显示选项的子对象,键名见链接文章。仅对物品有效,但常写在方块模型中供物品继承。每个视角都是可选子对象,可按以下顺序应用:translation:平移,以[x, y, z]指定。rotation:旋转,以[x, y, z]指定。scale:缩放,以[x, y, z]指定。right_rotation:NeoForge 添加,缩放后的第二次旋转,以[x, y, z]指定。
transform:见根变换。
建议 不清楚具体写法时,可以参考具有相似功能的原版模型。
渲染类型
NeoForge 的可选 render_type 字段用于指定模型渲染类型。未设置时(所有原版模型均如此),游戏回退到 ItemBlockRenderTypes 中硬编码的类型;如果那里也没有,则使用 minecraft:solid。默认类型有:
minecraft:solid:完全不透明的方块,如石头。minecraft:cutout:每个像素只可能完全不透明或完全透明,如玻璃。minecraft:cutout_mipped:cutout 的变体,远处缩小纹理以减少视觉瑕疵(mipmap)。不为物品应用 mipmap,因为通常不需要且可能产生瑕疵,例如树叶使用此类型。minecraft:cutout_mipped_all:同时为物品应用 mipmap 的变体。minecraft:translucent:像素可以半透明,如染色玻璃。minecraft:tripwire:需要渲染到天气目标的特殊方块,即绊线。
选择类型也涉及性能:solid 快于 cutout,cutout 快于 translucent。因此应选择满足需求且限制最严格的类型。
也可以在 Mod 总线的 RegisterNamedRenderTypesEvent 事件中调用 #register 添加类型,参数为:
- 名称:自动加上 Mod ID 前缀,例如
my_cutout成为examplemod:my_cutout。 - 区块渲染类型:
RenderType.chunkBufferLayers()返回列表中的任意类型。 - 实体渲染类型:必须使用
DefaultVertexFormat.NEW_ENTITY顶点格式。 - 可选的极佳画质类型:同样要求 NEW_ENTITY 格式,在画质为 Fabulous! 时替代普通实体类型;省略则回退到普通类型。若使用透明效果,通常建议指定。
元素
元素是长方体的 JSON 表示,包含:
from:起始角坐标[x, y, z],单位是 1/16 方块。例如[0, 0, 0]是左下角,[8, 8, 8]是中心,[16, 16, 16]是右上角。to:结束角坐标,单位同上。
建议 Minecraft 将 from、to 限制在
[-16, 32],但强烈不建议超出[0, 16],否则会出现光照或剔除问题。
neoforge_data:见额外面数据。faces:最多包含 north、south、east、west、up、down 六个面。每个面的数据包括:
此外还可指定:
shade:仅对方块有效,是否应用与方向相关的明暗,默认为 true。rotation:旋转子对象,包含:angle:角度,-45 至 45,步长 22.5 度。axis:旋转轴,目前不能同时绕多轴旋转。origin:可选旋转中心[x, y, z],为绝对坐标而非相对立方体位置,默认为[0, 0, 0]。
额外面数据
可选 neoforge_data 可以应用于整个元素,也可以应用于单个面。两者同时存在时,面级数据覆盖元素级数据。属性包括:
color:用指定 ARGB 颜色着色,可为字符串或十进制整数(JSON 不支持十六进制字面量),默认0xFFFFFFFF。常量颜色可用它替代颜色处理器。block_light:覆盖此面的方块光值,默认 0。sky_light:覆盖此面的天空光值,默认 0。ambient_occlusion:启用或禁用此面的环境光遮蔽,默认采用模型设置。
使用 neoforge:item_layers 加载器时,还能为 item/generated 的所有几何设置额外面数据。下例将第 1 层染红并以最高亮度显示:
{
"loader": "neoforge:item_layers",
"parent": "minecraft:item/generated",
"textures": {
"layer0": "minecraft:item/stick",
"layer1": "minecraft:item/glowstone_dust"
},
"neoforge_data": {
"layers": {
"1": {
"color": "0xFFFF0000",
"block_light": 15,
"sky_light": 15,
"ambient_occlusion": false
}
}
}
}
覆盖模型
物品覆盖模型依据一个浮点值(覆盖值)分配不同模型,例如弓与弩依据拉弦时间改变纹理。覆盖模型同时需要 JSON 和代码两侧的配置。
模型可指定一个或多个覆盖项,覆盖值达到或超过阈值时启用。例如,弓使用 pulling(1 表示拉弦,0 表示未拉弦)与 pull(拉弦程度)。低于 65% 时只要求 pulling 为 1;达到 65% 时要求 pull 至少 0.65;达到 90% 时要求至少 0.9。如果多项同时符合,使用列表最后一项,因此顺序必须正确:
{
// other stuff here
"overrides": [
{
// pulling = 1
"predicate": {
"pulling": 1
},
"model": "item/bow_pulling_0"
},
{
// pulling = 1, pull >= 0.65
"predicate": {
"pulling": 1,
"pull": 0.65
},
"model": "item/bow_pulling_1"
},
// pulling = 1, pull >= 0.9
{
"predicate": {
"pulling": 1,
"pull": 0.9
},
"model": "item/bow_pulling_2"
}
]
}
代码部分比较简单。假设要添加 examplemod:property 属性,在客户端事件处理器中使用:
@SubscribeEvent // on the mod event bus only on the physical client
public static void onClientSetup(FMLClientSetupEvent event) {
event.enqueueWork(() -> { // ItemProperties#register is not threadsafe, so we need to call it on the main thread
ItemProperties.register(
// The item to apply the property to.
ExampleItems.EXAMPLE_ITEM,
// The id of the property.
ResourceLocation.fromNamespaceAndPath("examplemod", "property"),
// A reference to a method that calculates the override value.
// Parameters are the used item stack, the level context, the player using the item,
// and a random seed you can use.
(stack, level, player, seed) -> someMethodThatReturnsAFloat()
);
});
}
说明 原版仅允许 0 至 1 的浮点值,NeoForge 将其扩展为任意浮点值。
根变换
NeoForge 添加的模型顶层 transform 属性,可在应用方块状态文件旋转或物品 display 变换之前,为全部几何应用变换。
有两种写法。第一种是 matrix,以嵌套 JSON 数组指定 3×4 变换矩阵(行主序,省略最后一行)。矩阵依次组合平移、左旋转、缩放、右旋转和变换原点。例如:
{
// ...
"transform": {
"matrix": [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
}
}
第二种是 JSON 对象,包含以下任意属性组合,并按列出顺序应用:
translation:相对平移,三维向量[x, y, z],默认[0, 0, 0]。rotation或left_rotation:缩放前绕平移后原点旋转,默认不旋转。可以写为:- 单轴与角度的对象,例如
{"x": 90}。 - 多个单轴对象的数组,按顺序应用,例如
[{"x": 90}, {"y": 45}, {"x": -22.5}]。 - 分别指定三轴角度的数组,例如
[90, 45, -22.5]。 - 直接表示四元数的四元素数组,例如
[0.38268346, 0, 0, 0.9238795](绕 X 轴 45 度)。
- 单轴与角度的对象,例如
scale:相对于平移后原点缩放,三维向量,默认[1, 1, 1]。post_rotation或right_rotation:缩放后的旋转,默认无旋转,格式同 rotation。origin:旋转和缩放的原点,最后也会将变换移到这里。可写三维向量,或内置值"corner"([0,0,0])、"center"([0.5,0.5,0.5])、"opposing-corner"([1,1,1],默认)。
方块状态文件
另见 Minecraft Wiki 的方块状态文件条目。
游戏通过状态文件为不同方块状态分配模型。每个注册方块必须恰好有一个状态文件。模型可以通过 variants 或 multipart 指定,两者互斥。
variants 中每项对应一个状态,大多数方块使用此方式:
- 键是不含方块名的状态字符串,例如不含水上半台阶为
type=top,waterlogged=false,无属性方块为""。未使用属性可以省略,例如 waterlogged 不影响模型时,两个取值可合并为type=top;空字符串因此适用于任意方块。 - 值为单个模型对象或模型数组,数组中会随机选一个。模型对象包含:
model:相对于命名空间 models 文件夹的路径,例如minecraft:block/cobblestone。x、y:对应轴上的旋转,仅允许 90 度步长,可选,默认 0。uvlock:旋转时是否锁定 UV,可选,默认 false。weight:仅对数组有用,决定随机选择权重,可选,默认 1。
multipart 则依据状态属性组合元素,主要用于栅栏和墙,以布尔属性启用四向部件。每个元素包含 when 与 apply:
when指定状态字符串,或必须满足的属性列表。列表可命名为 OR 或 AND,对内容执行对应逻辑。单个状态与列表项都可用|分隔多个值,例如facing=east|facing=west。apply指定模型对象或模型数组,规则与 variants 相同。
染色
草、树叶等方块会依据位置或属性改变纹理颜色。模型元素可在面上指定染色索引,交给颜色处理器处理。代码通过两个事件分别注册方块和物品颜色处理器,方式相似。先看方块:
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerBlockColorHandlers(RegisterColorHandlersEvent.Block event) {
// Parameters are the block's state, the level the block is in, the block's position, and the tint index.
// The level and position may be null.
event.register((state, level, pos, tintIndex) -> {
// Replace with your own calculation. See the BlockColors class for vanilla references.
// Colors are in ARGB format. Generally, if the tint index is -1, it means that no tinting
// should take place and a default value should be used instead.
return 0xFFFFFFFF;
},
// A varargs of blocks to apply the tinting to
EXAMPLE_BLOCK.value(), ...);
}
物品处理器基本相同,主要区别是名称和 lambda 参数:
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerItemColorHandlers(RegisterColorHandlersEvent.Item event) {
// Parameters are the item stack and the tint index.
event.register((stack, tintIndex) -> {
// Like above, replace with your own calculation. Vanilla values are in the ItemColors class.
// Also like above, tint index -1 means no tint and should use a default value instead.
return 0xFFFFFFFF;
},
// A varargs of items to apply the tinting to
EXAMPLE_ITEM.value(), ...);
}
item/generated 为各层指定染色索引:layer0 为 0,layer1 为 1,以此类推。方块物品也属于物品,需要物品颜色处理器才能染色。
注册额外模型
没有关联到方块或物品,但用于方块实体渲染器等其他场景的模型,可通过 ModelEvent.RegisterAdditional 注册:
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerAdditional(ModelEvent.RegisterAdditional event) {
event.register(new ModelResourceLocation(
// The id of the model
ResourceLocation.fromNamespaceAndPath("examplemod", "block/example_unused_model"),
// The string representing what variant of the model this is for
// In normal vanilla, this would be one of three values:
// - Blocks: The stringified block state
// - Items: 'inventory' as it is the inventory model
// - Standalone: 'standalone' as this does not refer to any other model
"variant_type=true"
));
// An inventory model example
event.register(ModelResourceLocation.inventory(
ResourceLocation.fromNamespaceAndPath("examplemod", "item/example_unused_inventory_model")
));
// A standalone model example
event.register(ModelResourceLocation.standalone(
ResourceLocation.fromNamespaceAndPath("examplemod", "block/example_unused_standalone_model")
));
}
来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交
4a680cdd4d15。