1.1 第一个物品
Part A. 实战
本节了解,创建一个物品就 3 步【所有物品都是这样】:
- 创建自己的物品类,一般继承于原版的物品的类
Item
; - 在合适的位置实例化这个物品;
- 将此物品对象注册进游戏;
我们先以一个原版游戏中没有 “黑曜石锭” 为例:
public class ObsidianIngot extends Item {
public ObsdianIngot() {
super(new Properties().group(ItemGroup.MATERIALS));
}
}
这里我们就写了一个构造函数,向父类的构造 函数传递了一个 Properties
类的对象,而这个 Properties
对象调用了 group()
,表示设置物品所在组为 Materials
组(杂项栏)。
传给父类的 Properties
对象的作用是规定了物品的一些属性,比如:是不是食物,或者这个物品在创造模式的哪一个物品栏。这里的 group()
方法就是设置了物品在创造模式的杂项栏。
如果不调用 group()
方法设置,那么这个物品就不会出现在创造模式物品栏,只能用 /give
获取;
然后是实例化 + 注册。现在的 Forge
引入了 DeferredRegister
类:
public class ItemRegistry {
public static final DeferredRegister<Item> item_reg =
DeferredRegister.create(ForgeRegistires.ITEMS, Utils.MOD_ID);
public static final RegistryObject<Item> obsdianIngot =
item_reg.register("obsidian_ingot", ObsidianIngot::new);
}
先使用 DeferredRegister
泛型类的静态方法 create(<ForgeRegistries.TYPE>, <mod_id>)
向 mod_id
的 mod 创建了一个 DeferredRegister<Type>
对象,表示这是专门注册 Type
类型实体的类。
紧接着告诉这个对象要注册 “注册名为 "obsdian_ingot"
(不能大写)、其对象构造方法为 ObsidianIngot::new
” 的一个类的 supplier
。
最后,由于这个 DeferredRegister
泛型类帮助我们注册物品需要依赖注入事件。显然注册是一种初始化的事件,所以将 DeferredRegister
对象注入 Mod
总线(我们以 0.9 中的项目为例):
@Mod(Utils.MOD_ID)
public class HelloMC {
public HelloMC() {
// DeferredRegister 的 register 方法如果参数是总线,就将自己注入总线中
ItemRegistry.item_reg.register(
// 这里前面说过,获取 Mod 总线
FMLJavaModLoadingContext.get().getModEventBus()
);
}
}
运行后会发现新的、没有材质的物品出现在创造模式的物品栏中。
Part B. 理论:Item
& Register
上面是实战,我们从理论层面归纳一下,方便记忆:
我们想要创建一个新的物品,它必然需要继承于原版物品的类型,以方便对物品属性的添加。一般来说:
-
新的物品继承于
Item
类型,目前其构造函数的参数是Properties
类型,用于配置物品基本属性;Properties
类型可以使用 settergroup()
来设置属性内容:“物品应放在创造模式物品栏的哪里”;
-
将一个自定义的物品类型(
Item
),或者其他对象注册进程序时,需要找到合适的时机(也就是 Mod 加载时,这个事件我们说过在 Mod 总线上)。但是直接使用事件的方法会增大 mod 的维护成本,因此 forge 提供了一个委托注册的类:DeferredRegistry<T>
。
怎么使用 deferred register 呢?为什么要使用 deferred register?官方文档说:
DeferredRegister is the recommended way to register objects.
- It allows the use and convenience of static initializers while avoiding the issues associated with it.
- It simply maintains a list of suppliers for entries and registers the objects from those suppliers during RegisterEvent.
所以,使用延迟注册器(deferred register),可以委托它进行对象的注册,需要告诉它要注册的对象名称,以及实例创建方法(supplier),以便延迟注册器在合适的时机帮你注册这些对象。
创建一个 deferred register 的工厂方法:
/* 第一参数指明推迟注册器要向什么类型的注册表注册对象。
* Forge 中内置了一些注册表对象的常量供我们选择: ForgeRegistries.*
*
* 第二参数指明注册器是为哪个 mod 实例注册对象。这就是我们在 @Mod 中给 mod 起的 唯一标识 */
DeferredRegister.create(IForgeRegistryEntry<T> reg, String modid);
/* 返回一个 DeferredRegister<T> 对象 */
我们可以委托它注册对象,就要告诉它映射关系、创建实例的方法(对象什么名称、怎么实例化):
DeferredRegister.register(String name, Supplier<T> sup);
/* 返回一个 RegisterObject<T>,即包裹了被注册对象的对象 */
注 意,在 mod 之后的部分中,如果想要以已注册对象的方式来访问这个对象,请使用这里的返回值
RegisterObject<T>
,其中的get()
方法能获得已注册对象的完整信息(还是Item
类,但是一些信息填写完整了):RegisterObject.get() -> Item;
在上面的 register
语句被执行后,deferred register 会记住这条关系,等待时机(延迟)进行注册。那么什么时候注册?
我们知道,mod 的内容的初始化的时机是交给 mod 总线的,所以,我们还需要把 deferred register 注册到 mod 总线中,以便 mod 总线在 mod 初始化的合适时机提醒 deferred register 开始注册。
DeferredRegister.register(IEventBus bus);
这条重载方法就是让 deferred register 监听总线,等到程序准备好了,就会触发 mod 总线的事件,从而 deferred register 开始注册之前委托的内容。
显然,这个注册监听的步骤应该写在 mod 总的构造函数中,在 mod 一启动时就要监听。
记住这个
DeferredRegister
,它不仅可以注册Item
,还可以注册其他类型的对象。