本篇教程由作者设定未经允许禁止转载。
序
注:这不是一篇0基础教程,只是一篇及其片面的参考性指导。
这篇教程不会提供过多实际的代码片段(会使用“伪代码”代替实际的 Java 代码),只会提供设计思路以及基本的使用案例。
Minecraft 在愈来愈多的版本迁徙中,即使相当多部分已被数次修改,但是其内核和总体逻辑架构依然不变。
所以这篇教程所传递的思路基本能在所有版本通用,只是某些实现的细节存在差异。
注册部分
Minecraft 的大多数内容,均有被注册。
生物群系、方块、物品、实体乃至粒子,皆是需要注册才能正常使用的。
注册的方式有多种,各个版本都有不同的实现。
在查看 Modloader 提供的接口前,我们先看看原版的注册方式:
Beta 1.7.3
// 直接从数组获取注册内容
var things = new Object[<ID上限>]
things[0] = Foo()
1.7.10
var registry : Registry = Registry.make("things")
val foo = registry.register(1145, "language:foo", Foo())
1.18
val foo = Registry.register(Registry.OBJECTS, Identifier("language", "foo"), Foo())
1.19
val foo = Registry.register(Registries.OBJECTS, Identifier("language", "foo"), Foo())
然后是 Modloader 提供的接口:
Fabric
Fabric 直接调用简洁的原版注册器,并且无需考虑类的加载顺序问题。
Legacy Fabric 及以下则需要使用相关的 API,具体用例参考其文档,此处不过多赘述。
Forge
Forge 需要通过 Forge 注册器间接访问原版注册器来完成注册,而且需要注意类的加载顺序问题,所以实现上会相对复杂。
1.7.10
@Mod.EventHandler
public void doCommon(FMLInitializationEvent event) {
GameData._register(<字串ID>, <注册项>);
}
1.12.2
// 方法一
@SubscribeEvent
public void registerThings(RegistryEvent.Register<Object> event) {
event.getRegistry().register(new Foo().setRegistryName(<id>));
event.getRegistry().registerAll(new Foo[]{new Bar1().setRegistryName(<id>, new Bar2().setRegistryName(<id>});
}
// 方法二(不推荐)
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
// 注册代码
}
1.20
参阅 Forge 的官方或社区文档,此处不过多赘述。
注意类的加载顺序问题,请确保 Mod 入口点被加载时时包含注册相关代码的类也被加载。
注册事件和常规类加载并不是同步的,注册初始化的动作先于常规类加载。
大致的执行顺序如下(从左到右依次进行):Mod 入口点初始化、注册事件、常规类加载。
如果你尝试在注册事件结束的时候注册,Forge 会直接抛出异常。
实在不知道如何正确地处理类加载,请参考以下代码:
public class 注册物品 {
public static void 加载该类() {
}
// 实例化注册项
}
public class 注册类 {
public static void 加载该类() {
// 请确保你的注册项在将要被注册时也被加载了!
注册物品.加载该类();
}
}
@Mod
public class ModMain {
static {
注册类.加载该类();
}
}
“数据”部分
“数据”,此处指不需要经过注册,但是游戏正常运行所依赖的数据。
数据包、资源包中的数据便是“数据”(data、assets),1.12.2 data 中的内容存放在 assets 中,1.7.10 则直接由代码创建。
编写数据包“数据”的部分请参阅 Minecraft Wiki,此处不过多赘述。
在1.12.2,方块的模型会根据注册键自动绑定,而物品的模型需要手动指定。
ModelLoader.setCustomModelResourceLocation(<item>, <meta>,
new ModelResourceLocation(<id>, "inventory"));
燃料热值
1.7.10
实现 IFuelHandler 即可。
1.12.2
IFuelHandler 依然可用,但是可以直接覆盖物品基类中的 getFuelBurnTime 方法。
【未完待续】