本篇教程的视频

(待发布)

本篇教程的源代码

(待发布)

本篇教程目标

  • 理解原版物品注册(1.21.2)
  • 学会物品注册(1.21.2)

注意

本篇教程为1.21 第一个物品1.21 第一个方块的附属教程,
所以本篇教程只专注于讲解1.21.21.21之间的不同之处,后续的附属教程同理

1.21.21.21物品/方块注册上有些许不同,而像它们的模型方块状态文件等并没有改变

所以我们这篇教程的主要任务在于搞清楚1.21.2物品/方块注册方法

推荐的学习路线是,先学习1.21的教程,再来学习本教程

关于Blog提示

那么,如果说你用1.21教程中的方法,到1.21.2中注册物品,那么在启动游戏时,
游戏就崩溃了,并抛出类似NullPointerException: Item id not set异常

这个Item id是个什么玩意?我们来看看Fabric Blog上的介绍

1
2
3
4
Minecraft 1.21.2 uses registry keys to pre-compute certain block or item settings. 
This allows referencing them in default item components.
For example, rather than computing a default name based on an item’s registry key inside `getName` method if the `minecraft:item_name` component is not present,
every item now contains the `minecraft:item_name` component.

简单来说,Minecraft 1.21.2中使用registry keys(注册键)来预处理物品和方块的一些设置

现在物品和方块都有了一个与他们注册键相关联的组件,其实就是他们的名字
这个名字呢,在语言文件缺失时,就会以item.命名空间.物品注册名的形式显示

之前的版本中,有些地方要用到这个组件(比如显示名字),如果这个组件缺失,
就会调用getName方法来生成一个默认的名字

但是现在,所有物品和方块都包含这个组件了,如果你不给物品和方块设置注册键,游戏就直接崩溃了

查看源代码

我们再结合源代码来看看这个注册键是怎么注册的

这里我们直接来看它的注册方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static RegistryKey<Item> keyOf(String id) {
return RegistryKey.of(RegistryKeys.ITEM, Identifier.ofVanilla(id));
}

public static Item register(String id) {
return register(keyOf(id), Item::new, new Item.Settings());
}

public static Item register(RegistryKey<Item> key, Function<Item.Settings, Item> factory, Item.Settings settings) {
Item item = (Item)factory.apply(settings.registryKey(key));
if (item instanceof BlockItem blockItem) {
blockItem.appendBlocks(Item.BLOCK_ITEMS, item);
}

return Registry.register(Registries.ITEM, key, item);
}

可以看到,注册键是通过keyOf方法生成的,而keyOf方法又调用了Identifier.ofVanilla方法

这个Identifier在前面的教程中讲过了,ofVanilla方法是以minecraft为默认命名空间的,、
所以我们写的时候呢,得加上我们自己的命名空间

后面的注册方法与之前的版本有点区别,但不是很大,最后一个Registry.register(Registries.ITEM, key, item);还是很熟悉吧

对于最后一个方法,在之前的版本中,第二个参数是Item item
现在变成了Function<Item.Settings, Item> factoryItem.Settings settings

这两个参数呢,第一个是物品构造方法,第二个是物品设置

FunctionJava中的函数式接口,其泛型的第一个参数是函数输入的类型,第二个是函数结果的类型

那么放到这里来呢,根据传入的Item.Settings创建并返回一个新的Item实例

在方法内部呢,通过apply方法,再在已配置的Item.Settings基础上再加上一个registryKey方法,
这个方法就是用来给物品配置注册键的

1
Item item = (Item)factory.apply(settings.registryKey(key));

这一串代码呢,你要简单理解,其实就是实例化一个物品,类似于直接new Item

但对于实际开发而言,这就太局限了,因为像一些武器工具,比如斧头镐子等,
它们都有自己的物品类,这些类虽然是Item的子类,但要实现它们特有的逻辑,
并不能直接new Item,所以这里就用了Function来实现更加灵活的注册

注册物品

那么这里我们就来注册自己的物品

注册方法

我们还是来先写注册方法,按照我的习惯还是将原版的那些方法进行整合,
如果你实在不会整合,就直接复制原版的那些方法好了,但记得改命名空间

1
2
3
4
5
6
7
8
9
10
11
public static Item registerItems(String name, Function<Item.Settings, Item> factory, Item.Settings settings) {
RegistryKey<Item> key = RegistryKey.of(RegistryKeys.ITEM, Identifier.of(ReTutorial.MOD_ID, name));

Item item = (Item)factory.apply(settings.registryKey(key));

if (item instanceof BlockItem blockItem) {
blockItem.appendBlocks(Item.BLOCK_ITEMS, item);
}

return Registry.register(Registries.ITEM, key, item);
}

这里我们直接将keyOf方法整合进来了,这样一个方法就能解决了

注册物品

再来注册物品

1
2
public static final Item ICE_ETHER = registerItems("ice_ether", Item::new, new Item.Settings());
public static final Item RAW_ICE_ETHER = registerItems("raw_ice_ether",Item::new, new Item.Settings());

我们也可以拿以前版本的进行对比

1
2
public static final Item ICE_ETHER = registerItems("ice_ether", new Item(new Item.Settings()));
public static final Item RAW_ICE_ETHER = registerItems("raw_ice_ether", new Item(new Item.Settings()));

区别是显然易见的吧

后面的语言文件模型文件1.21的是一样的,这里就不再赘述

参见:#2 第一个物品