用户:MashKJo/1.12.2模组开发教程/10.BlockState系统及Forge BlockStates V1模型格式:修订间差异

(// Edit via Wikiplus)
→‎Forge BlockStates V1模型文件格式:​ // Edit via Wikiplus
 
(未显示同一用户的3个中间版本)
第48行: 第48行:


== 模型文件的书写 ==
== 模型文件的书写 ==
现在,blockstates JSON文件中的那个“variants”应该很好理解了——它实际上是囊括了该方块的所有IProperty。对于那些单BlockState方块而言,它们没有任何的额外IProperty,因此我们直接开个“normal”,再来指定它的模型文件路径即可,就这么简单。
但是对于多BlockState方块而言,显然我们期望不同的方块状态对应不同的样子,比如两种熔炉方块的外观会随着朝向的不同发生变化。这个时候就需要在variants下列出所有可能的IProperty的不同值之间的所有可能的组合情况了,我们来看看原版的熄灭的熔炉方块的blockstates JSON文件:
{
    "variants": {
        "facing=north": { "model": "furnace" },
        "facing=south": { "model": "furnace", "y": 180 },
        "facing=west":  { "model": "furnace", "y": 270 },
        "facing=east":  { "model": "furnace", "y": 90 }
    }
}
熄灭的熔炉方块有且只有一个PropertyDirection,它被构造时传入的name是<code>"facing"</code>,而它实际上是一个PropertyEnum<EnumFacing>,EnumFacing实现了IStringSerializable,因此对于该方块,我们就可以列出"facing=[A certain EnumFacing instance.getName()]"这种式子表示一个BlockState,再指定它的具体模型情况即可。这里我们注意到朝向实际上不影响方块的材质,本质上模型只是转了一定的角度而已,因此这里model不变,而y(即方块模型绕过方块上下两面中心点的直线旋转的角度)则有所变化。注意这里指定模型文件路径时并没有指定域名(domain),那是因为这是原版blockstates文件,不写域名的话,域名默认为minecraft,而对于我们的模组的blockstates文件,域名必须得写——就是我们的模组的modid。
=== Forge BlockStates V1模型文件格式 ===
可能已经有读者注意到,原版的blockstates JSON文件格式是有优化空间的,比如在上面那个例子中,同样的“facing=”这种东西重复了四次,而且每种情况下模型文件实际上是相同的,只是绕y轴旋转的角度有所变化,但我们却依然要重复书写同样的模型文件路径,这实际上有些冗余。
而且当一个方块拥有的IProperty数量不只是一个时,情况往往会变得更加糟糕——比如再来一个有四种可能值的IProperty的话,我们真的就要把总共十六种情况全部列出来,真的就要在variants里写上十六行相差不大的东西。这听起来就让人头皮发麻。
所幸,Forge给我们提供了一个基于原版格式的blockstates文件格式——Forge BlockStates V1格式,让我们用它来重写原版的这一blockstates文件:
{
    "forge_marker": 1,
    "defaults": { "model": "furnace" },
    "variants": {
        "facing": {
            "north": { "y": 0 },
            "south": { "y": 180 },
            "west": { "y": 270 },
            "east": { "y": 90 }
        }
    }
}
首先,<code>"forge_marker": 1</code>代表这是一个符合Forge BlockStates V1格式的blockstates文件,这会告知Forge来处理它。然后我们可以通过设定defaults来设定该blockstates文件的一些默认值——毕竟四个方块状态共用一个模型文件,因此我们只需把它当成默认值写入即可。
然后就是variants中IProperty写法的简化了,也很容易让人明白。如果有多个IProperty,只需分开写即可,Forge会自动求出它们的所有可能值的所有可能的组合情况的。这就大大简化了blockstates的模型映射。
值得注意的是,虽然名字叫Forge BlockStates V1,但实际上这也可以用于方块的物品形式的模型的指定。还记得ModelResourceLocation构造方法中那个String类型的variantIn吗?我们传入的是<code>"inventory"</code>,所以这个inventory实际上就是一个variant,换言之,你只需要在variants中这么写即可:
{
    ...
    "inventory": [{
        "transform": "forge:default-block"
    }],
    ...
}
甚至于和方块没什么关系的普通物品我们也可以写在blockstates文件中,只需要把transform改成forge:default-item,再指定一下textures即可。不过并不推荐这么做,因为普通的物品和方块既然没关系,那么放在blockstates文件中既多此一举,也容易造成歧义。
== 方块的Meta-hack ==
类似于ItemStack,方块也是可以有Meta-hack的——实际上是用同一个Block的不同BlockState及其对应的meta代表设定上不同的多种方块(实际上在代码层面都对应同一个Block对象)。和ItemStack的Meta-hack类似,方块的Meta-hack也是不推荐使用的。
首先我们需要一个区分方块类型的IProperty,一般是用PropertyEnum,用到的枚举类一般作为静态内部类的形式出现在方块类中;再设定该方块的hasSubtypes为true;然后在blockstates文件中,针对这个variant分别指定各自不同的模型文件路径,即可。没错,你没看错,就这么简单。
然而使用Meta-hack的方块对应的物品却不是这么简单——因为ItemBlock类的默认实现很可能不能满足我们的需求,为何?因为ItemBlock的默认逻辑是:若传入的Block的hasSubtypes为true,那么使用ItemBlock对应的某个ItemStack放置该方块时,该ItemStack的meta值和放置的BlockState对应的meta值,是相等的。因此如果你的方块只有这一个区分类型的IProperty,倒还没问题;但如果你同时还有其他几个IProperty呢,比如一个决定朝向的PropertyDirection?那就糟了,该ItemBlock的meta会影响到放置出来的方块的朝向,这显然不是我们所期望的,我们所期望的应当是:由放置该方块的实体的朝向来决定该方块的朝向。因此这个时候你就得覆写<code>Block#getStateForPlacement</code>才行,这个方法会传入对应的ItemStack,由此我们就可以手动指定符合我们自己的需求的BlockState和ItemStack的各自的metadata的正确对应关系。
另外,我们还得覆写<code>Block#getSubBlocks</code>方法,它的用途和之前提到过的getSubItems差不多,实际上ItemBlock类中的getSubItems方法就是代理给了getSubBlocks的。
行政员、​优秀编辑者、​界面管理员、​监督员、​管理员、​小部件编辑者
3,417

个编辑