Minecraft 1.8 Forge API IBlockState 与渲染

在 Minecraft 内部,方块也可以看作是可放置的物品,使用 Item 类的静态方法 getItemFromBlock(Block someBlock) 可以获得对应的物品。向游戏内注册该方块也是类似的使用 GameRegistry.registerBlock(Block blockToReg, String unlocalizedName)。然而在处理渲染时需要多额外的一步。

以下为 Item 渲染

String nameToReg = MOD_ID + ':' + item.getUnlocalizedName().substring(5);
Minecraft.getMinecraft()
        .getRenderItem().getItemModelMesher()
        .register(item, 0,
        new ModelResourceLocation(nameToReg, "inventory");

以下为 Block 渲染

Item item = Item.getItemFromBlock(block);
// 从 Block 得到对应的 Item
String nameToReg = MOD_ID + ':' + item.getUnlocalizedName().substring(5);
Minecraft.getMinecraft()
        .getRenderItem().getItemModelMesher()
        .register(item, 0,
        new ModelResourceLocation(nameToReg, "inventory");

区别主要在于多了一句从 Block 获得 Item。虽然是以 Item 注册了渲染,实际上这里和 Item 处理渲染的方法并不相同。

assets/<MOD_ID>/blockstates/<blockUnlocalizedName>.json

我所写的 mod 中许多方块都会根据不同的 BlockState 改变渲染模型,而 BlockState 中不同 IProperty 组合所对应状态所对应的模型在这里指定。关于 BlockState,见上一篇 Forge API 详解。我在这里以 TransitRailMod 的电缆架作为例子。

在源代码中,WirePanel 是 CustomDirectionalBlock 的子类,因此继承其 EnumFacing FACING 的属性,这个方块又根据电缆架上是否插了日光灯改变 EnumBool LAMP 属性、是否是封闭的改变 EnumBool SHUT 属性。因此罗列一下:

  • FACING 指示方块朝向
  • LAMP 决定是否有日光灯底座
  • SHUT 决定电缆架是否有罩子封闭

assets/transitrailmod/blockstates/wire_panel.json 中:

{
	"variants": {
		"facing=north,lamp=true,shut=false": {
			"model": "transitrailmod:wire_panel_with_lamp" },
		"facing=north,lamp=false,shut=false": {
			"model": "transitrailmod:wire_panel" },
		"facing=east,lamp=true,shut=false": {
			"model": "transitrailmod:wire_panel_with_lamp",
			"y": 90 },
		...
		"facing=east,lamp=false,shut=true": {
			"model": "transitrailmod:wire_panel_shut",
			"y": 90 },
		"facing=south,lamp=true,shut=true": {
			"model": "transitrailmod:wire_panel_shut",
			"y": 180 },
		...
	}
}

有 16 行不同的以 IProperty=value 的组合为键,以 modely 作为值的 json 数据对,需要列出所有可能的组合。为了不需要为了不同的方块朝向单独建立模型,Minecraft 在这里提供了一个 y,在此处输入 {0, 90, 180, 270} 中的一个就可以定义旋转角度,然而在游戏中,x 轴和 z 轴的正方向分别是 south 和 east,朝向的值应该和模型的朝向一致。

然而万一你的方块非常 normal(不区分方向也没有别的属性,看上去就是个纯粹装饰性的方块也不会互动啊啥的),像 LogoBlock 这样写一个 normal 的键值对就可以了:

{
	"variants": {
		"normal": { "model": "transitrailmod:logo_block" }
	}
}

assets/<MOD_ID>/models/block/<model_name>.json

这里是存放模型文件的主要场所,在之前的 blockstates/<unlocalizedName>.jsonmodel 键对应的值 <model_name> 应当在这里找到对应的 <model_name>.json。关于建立方块模型,可以参照 Minecraft 原版的源代码,也可以使用第三方工具,比如 Mr Crayfish’s model editor(至少比全手动好得多然而然而不够好)。

assets/<MOD_ID>/models/item/<unlocalizedName>.json

如果你希望你的方块在物品栏、玩家手上(第一人称和第三人称)正确渲染,那么这里的 json 文件是必需的。这也就是说,如果是技术性方块(门的上下两半之类的)等不会在玩家手上和物品栏出现的的东西就可以没有这个文件。这里的文件对于物品和方块同样适用

Minecraft 自带的物品钻石剑:

{
    "parent": "builtin/generated",
    "textures": {
        "layer0": "items/diamond_sword"
    },
    "display": {
        "thirdperson": {
            "rotation": [ 0, 90, -35 ],
            "translation": [ 0, 1.25, -3.5 ],
            "scale": [ 0.85, 0.85, 0.85 ]
        },
        "firstperson": {
            "rotation": [ 0, -135, 25 ],
            "translation": [ 0, 4, 2 ],
            "scale": [ 1.7, 1.7, 1.7 ]
        }
    }
}

Minecraft 自带的泥土

{
    "parent": "block/dirt",
    "display": {
        "thirdperson": {
            "rotation": [ 10, -45, 170 ],
            "translation": [ 0, 1.5, -2.75 ],
            "scale": [ 0.375, 0.375, 0.375 ]
        }
    }
}

很容易发现和方块的模型一样,也有 parent 用于继承上一级别的物品, texture 用于指定贴图。然而作为 Item 模型,必须有 display 键,并根据需要写第一人称视角的 firstperson 或者第三人称视角的 thirdperson。对于完整的方块,通常直接复制并修改内建的玩整方块模型即可,对于不完整的方块或者物品,可以参照类似性质的 json 修改。

然而注意到这里的 dirt 并没有 firstperson 键而只有 thirdperson 键。无论是 firstperson 还是 thirdperson 都有预先定义过的值。主要的点在于,其 scale 默认为 [ 1, 1, 1 ] 也就是方块放置下来时候的大小,对于第一人称视角,普通的方块大小正好所以无需指定,不完整的方块略小,所以对于火把一类的来说 scale 写 [ 2, 2, 2 ] 都是可以的,然而在第三人称视角,方块若还是按照原样渲染,将会导致拿在手上的方块在他人看来巨大无比麒麟臂,因此必须在 thirdperson 中指定缩小的 "scale": [ 0.375, 0.375, 0.375 ]

Leave a Reply

Your email address will not be published. Required fields are marked *