1. 依赖于具体情况的物品模型

这里讲述的内容只适用于物品模型;方块模型若需要类似的效果,可能需要使用 Forge BlockState V1。
原版的物品模型都很简单,绝大多数物品的外貌也都是几个大版本下来都雷打不动的。但 Mod 不一样,很多时候我们都会发现“我们的物品的模型需要随着物品的某一数据的改变而改变”。有没有办法实现这个效果呢。

1.1. 依赖于损害值的物品模型

可以说是所有情况中最简单的一种,多见于 meta hack。

// 还记得这个方法吗?第二个参数就是 metadata。之前我们写了 0,因为物品没什么特别的。
// 对于这个情况,只需在调用的时候传入别的 metadata 并指定合适的模型位置就可以了。
ModelLoader.setCustomModelResourceLocation(myItem, 0, targetModelResourceLocation);

1.2. Property Override

但不是所有的时候我们都有 metadata 可以用。比如工具,它实际上没有 metadata,但它有耐久。如果我们希望一个工具的耐久会决定其外观该怎么做?最简单的方法就是 Property Override。
这是原版物品模型的一个机制,允许物品模型根据一个浮点数(准确地说是一个 float)的值来决定该使用什么模型。举个例子,对于有耐久度设定的工具来说,可以考虑这么做:

{
    "parent": "minecraft:item/handheld",
    "textures": {
        "layer0": "..."
    },
    "overrides": [
        {
            "predicate": { "damage": 0.5 },
            "model": "..."
        }
    ]
}

这样,在这个工具只剩下一半耐久的时候,它就会使用另外一个模型。
这个 predicate 是原版提供的。它有一个“缺陷”:这里的 0.5 是精确匹配的。也就是说,当这个值是 0.49 或 0.51 的时候它都不会改变模型——实际上,这个“缺陷”正好给地图制作者和服务器插件开发者提供了契机,因为利用这个特性可以用来给原版“新增一堆物品”。
不过这里讲的是 Forge Mod 啊,既然这个 predicate 满足不论我们的需求,那我们自己写一个不就好了。为此你需要的类叫做 IItemPropertyGetter

public class MyEpicTool extends Item {
    public MyEpicTool() {
        super();
        // 必要的初始化,请按需添加
        this.addPropertyOverride(new ResourceLocation("my_mod", "test_damage"), new IItemPropertyGetter() {
            @Override
            @SideOnly(Side.CLIENT)
            public float apply(ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity) {
                return 0F;
            }
        });
        // 是的,这里可以用 lambda,但这里仍然使用匿名内部类的原因是那个 @SideOnly,它的含义在后面的章节会讲到。
    }
}

你应该发现了,你可以利用的东西不止当前的 ItemStack 对象,还有当前的 World 和持有它的实体。为什么不是 EntityPlayer?因为能拿东西的实体多了去了,僵尸和骷髅都可以。我们只需要在这里实现我们需要的逻辑就好,然后让模型用这个 predicate:

{
    "...": "...",
    "overrides": [
        {
            "predicate": { "my_mod:test_damage": 0.1 },
            "model": "..."
        },
        {
            "predicate": { "my_mod:test_damage": 0.2 },
            "model": "..."
        }
    ]
}

尽管如此,它的局限依然很大——比如返回的值必须在 [0, 1] 之间。而且这只是一个 float,面对其他不能完美转化的数据它就束手无策了。

1.3. ItemMeshDefinition

如果我要依赖的数据是字符串一类的东西该怎么办?是时候让 ItemMeshDefinition 登场了。
ItemMeshDefinition 是一个原版类,允许某个物品只根据 ItemStack 提供的信息返回不同的 ModelResourceLocation。一个 ItemStack 里有什么?物品类型、数量、metadata(或损害值)甚至是 NBT 数据。理论上应该足够满足各种奇怪的需求了。

@SubscribeEvent
public static void onModelRegistration(ModelRegistryEvent event) {
    // 理论上这个也可以用 lambda。
    ModelLoader.setCustomMeshDefinition(new ItemMeshDefinition() {
        @Override
        public ModelResourceLocation getModelLocation(ItemStack stack) {
            return aCertainLocation;
        }
    });
}

和方块模型类似,使用 ItemMeshDefinition 时必须能提前知道所有可能的返回值,并告知 Minecraft 加载对应的模型。尝试返回一个 Minecraft 没有加载的模型的位置会导致实际显示为紫黑块。这也意味着:它不能用来动态生成模型。

@SubscribeEvent
public static void onModelRegistration(ModelRegistryEvent event) {
    // 第二个参数是 var-arg,类型 ModelResourceLocation。
    // 注意这里使用了原版的 ModelBakery,而非 Forge 的 ModelLoader。
    ModelBakery.registerItemVariants(myItem, ...);
}

1.4. 重新实现 ItemOverrideList

这一手段虽然仍然是基于原版类的,但有鉴于它牵扯到的其他信息,它有自己的独立章节。请直接前往那里获取有关信息。