注册类别介绍

添加基本的物品是编写模组的第一步。基本逻辑是创建 Item 对象,注册,并提供纹理。要向物品添加其他行为,就需要编写自定义的 Item 类。

但在这之前,有必要了解一下 Minecraft 中常见的注册内容类别:

类型 Java 类 举例 说明
Item(物品) net.minecraft.item.Item 铁锭、剑、药水 所有背包里的物体(不一定是方块)
Block(方块) net.minecraft.block.Block 石头、熔炉、小麦地块 可以放在世界里的立体方块
BlockItem(方块物品) net.minecraft.item.BlockItem 石头物品、栅栏物品 在背包中代表某个方块的 “物品形式”
Entity Type(实体) EntityType<?> 僵尸、箭、村民 会动的东西(生物、投掷物)
Sound Event(声音事件) SoundEvent 挖掘声、击中声 可播放的音效事件
Fluid(流体) Fluid 水、岩浆、自定义油 可流动的液体类型
Enchantment(附魔) Enchantment 锋利、经验修补 给物品附加能力的系统
Potion(药水类型) Potion 治疗、跳跃、力量 药水本身的类型(不是瓶子)
StatusEffect(状态效果) StatusEffect 中毒、再生 药水或食物带来的效果
RecipeSerializer / RecipeType(合成配方) RecipeSerializer, RecipeType 合成表、锻造、熔炉配方 定义如何制作物品
ItemGroup(创意模式标签) ItemGroup 红石、装饰方块 创造模式分类标签

创建物品

java/com/frozen/tutorialmod/ 下创建 item 软件包,用来存放相关物品实例项和注册表项。接着在 item 包下创建 ModItems 类,我们将在这个类中编写实例物品和注册方法。

类有了,方法哪来?Fabric 端可以参考 Minecraft 的物品创建方式,使用 IDEA 快捷键双击 shift 打开搜索面板,搜索 net.minecraft.itemItems 类(搜索位置为所有位置):
image-20250726160337401

基本上,一个单词若是 Item,在 MC 大概率是定义物品基本属性的,而 Items 则是注册方面的。

打开该文件,我们搜索(快捷键是 ctrl+f)diamond 钻石这一单词,找到这个最简单的物品,观察它是何如被 MC 实现的:
image-20250726161340167

可以看到 DIAMOND 被一个 register 方法进行注册,于是我们使用鼠标选中这个 register,点击鼠标中键进行跳转,观察 register 方法(或者按住 Ctrl 建对 register 点击鼠标左键):
image-20250726161901011

可以看到从上到下总共存在三个静态注册物品的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Item register(String id, Item item) {
  return register(new Identifier(id), item);
}

public static Item register(Identifier id, Item item) {
  return register(RegistryKey.of(Registries.ITEM.getKey(), id), item);
}

public static Item register(RegistryKey<Item> key, Item item) {
  if (item instanceof BlockItem) {
    ((BlockItem)item).appendBlocks(Item.BLOCK_ITEMS, item);
  }

  return Registry.register(Registries.ITEM, key, item);
}
看不懂?点我展开!

首先,这三个方法的调用顺序如下:
image-20250726164216874

第一个方法,帮你把要创建的物品标记上你指定的名字。但 Identifier 它本身不包含任何其他基本信息,是不知道你在注册物品还是方块的,所以需要第二个方法进一步增强类型安全。具体来说,主要是接收你定义的物品名字 String id,并将其转换为 Identifier 对象,Item item 参数若没有对物品基本属性进行指定则使用默认属性,然后将这俩作为参数传入给第二个 register 方法进一步增强类型安全。

其中的 Identifier 是 MC 用来标识资源的类,就是用来声明一个名字。格式为:

1
new Identifier("mod_id", "item_name") // 比如: tutorial-mod:blue_powder

1.21 之前一般是通过 new Identifier("namespace", "path")new Identifier("namespace:path") 的方式进行使用,这里第一个方法只传入了 id,那么默认将会使用 mod 的 id 作为 namespace 命名空间,所以用来给 Mod 物品做标识需要引入 Mod 的 ID。(此处可参考 Fabric Wiki

第二个方法,把第一个方法传来的 Identifier 结合 item 属性转换成了 RegistryKey<Item>RegistryKey<T> 是一个键对象,表示某个注册表中某个项目的唯一标识。例如第二个方法中:

1
RegistryKey.of(Registries.ITEM.getKey(), id)

相当于:

1
RegistryKey<Item> itemKey = RegistryKey.of(Registries.ITEM.getKey(), new Identifier("my_mod", "my_item"));

Registries.ITEM 是一个 Registry<Item>,代表的是 Minecraft 的 ITEM物品注册表。

所以这个意思是:“这是一个 Item 类型注册表里的条目,它的 ID 是 my_mod:my_item”。
所以,第二个方法主要是让注册系统知道你是在哪个注册表中注册哪个对象。

第三个方法,存在 Registry.register() 语句,为真正执行注册的操作。对于 if 语句,它的作用很明显,主要是判断当前注册的物品是否为 BlockItem 方块物品。

如果为真则会将这个物品加入到 Item.BLOCK_ITEMS 这个静态 Map 中,而这个 Map 是一个从 BlockItem 的映射,用于在需要时(比如显示方块物品)找到对应的物品。

真正执行注册的语句是:Registry.register(Registries.ITEM, key, item),意思很明显了,使用 Registry 大注册表的 register 注册方法将物品注册到注册表,括号内指明要注册的注册表(Registeries.ITEM)、键名(key)和基本属性(item)。

我们将这三个注册方法直接复制粘贴到 java/com/frozen/tutorialmod/item/ModItems.java 中来,然后将第一个注册方法使用到的 id 改为我们 Mod 的 ID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ModItems {
    public static Item register(String id, Item item) {
        return register(new Identifier(TutorialMod.MOD_ID, id), item);
    }

    public static Item register(Identifier id, Item item) {
        return register(RegistryKey.of(Registries.ITEM.getKey(), id), item);
    }

    public static Item register(RegistryKey<Item> key, Item item) {
        if (item instanceof BlockItem) {
            ((BlockItem)item).appendBlocks(Item.BLOCK_ITEMS, item);
        }

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

现在注册方法有了,我们就可以开始注册物品实例了(ModItems.java):

1
2
// 注册一个紫色粉末,最大堆叠数量为30
public static final Item PURPLE_POWDER = register("purple_powder", new Item(new Item.Settings().maxCount(30)));

new Item(new Item.Settings().maxCount(30)) 中的.maxCount() 是不必须的,仅作演示。
你可以通过 new Item.Settings() 后接一个. 来唤起很多属性方法。

分类的重要性

可以看到,现在我们注册的物品是不分类别的,因为我们的项目现在相对来说还较为简单,但是一旦物品数量多到不得不分类的时候,我们等到那时候再去修改吗?这也不太现实,维护成本巨大。所以,对物品进行分门别类是很重要的。

分门别类这一操作在代码体现上,实际是对注册的物品写好路径(ModItems.java):

1
2
public static final Item PURPLE_POWDER = register("purple_powder", new Item(new Item.Settings().maxCount(30))); // 注册一个紫色粉末,最大堆叠数量为30
public static final Item BLUE_POWDER = register("material/blue_powder", new Item(new Item.Settings().maxCount(30))); // 注册一个蓝色粉末,最大堆叠数量为30

在对蓝色粉末进行注册的时候, 我们在 "bule_powder" 中使用 / 路径符完成路径的编写。索性,把紫色粉末也加入原材料的类别中吧(ModItems.java):

1
2
- public static final Item PURPLE_POWDER = register("purple_powder", new Item(new Item.Settings().maxCount(30))); // 注册一个紫色粉末,最大堆叠数量为30
+ public static final Item PURPLE_POWDER = register("material/purple_powder", new Item(new Item.Settings().maxCount(30))); // 注册一个紫色粉末,最大堆叠数量为30

完成分类的操作,这将会在后面使用数据生成的时候,极大的方便资源的查找。

完成注册

接下来还要为 ModItems.java 加上一个静态的方法用来辅助我们注册:

1
2
public static void registerItems() {
}

该方法暂无需做任何事,只需要在主类 java/com/frozen/tutorialmod/TutorialMod.java 中进行调用即可:

1
2
3
4
5
  @Override
  public void onInitialize() {
+   ModItems.registerItems();
    LOGGER.info("Hello Fabric world!");
  }

这是 Java 的特性。类在首次使用时会被加载并初始化,所有静态字段也就被初始化了且只初始化一次,避免重复注册。

添加资源文件

resources/assets/tutorial-mod 中新建:

  1. lang 包:存放语言文件。如 en_us.jsonzh_cn.json 等,若其他语言文件损坏将默认采用 en_us.json
  2. models 包:存放模型文件。
  3. textures 包:存放贴图文件。

lang 包,我们完成 en_us.json 文件的编写:

1
2
3
4
{
  "item.tutorial-mod.material.purple_powder": "purple Powder",
  "item.tutorial-mod.material.blue_powder": "Blue Powder"
}

也就是:内容类别.namespace.路径名(如果有).内容名的格式。而中文语言文件,只需要把对应的值写成中文就可以了:

1
2
3
4
{
  "item.tutorial-mod.material.purple_powder": "紫色粉末",
  "item.tutorial-mod.material.blue_powder": "蓝色粉末"
}

models 包,新建两个软件包:blockitem 包,对于 textures 包也是如此(请注意单复数区别)。

resources/assets/tutorial-mod/models/item/ 中创建 material 包(若没有分类则不需要创建该软件包直接新建 json 文件),并在这个包内创建 purple_powder.json(文件名和注册时写的物品名一致):

1
2
3
4
5
6
{
  "parent": "item/generated",
  "textures": {
    "layer0": "tutorial-mod:item/purple_powder"
  }
}

物品模型的 parent 改变了物品在手中以及在物品栏内等情形下的渲染。item/generated 用于许多简单的物品。item/handheld 用于手持其纹理左下角的物品。

如果在 textures 包的 item 也新建 material 软件包的话,purple_powder.json 需要将 "tutorial-mod:item/purple_powder" 修改为 "tutorial-mod:item/material/purple_powder"

看到这个 models 里面的 textures 就是来指定 assetstextures 中具体的面数,我们这个物品比较简单,所以就是一个面数就行。

resources/assets/tutorial-mod/textures/item/material/ 中,将准备好的贴图文件放到该目录下(文件格式为 png,且文件名和你定义的物品名一致)。

我的图片是:
blue_powderpurple_powder

贴图素材网站

  1. NOVA SKIN:能在线修改 MC 本体的内容素材并免费下载到本地。
  2. Plant Minecraft-Texture Packs:主要是用户上传的素材。

如果有更好的网站,欢迎推荐!

运行测试

至此,已经可以尝试运行 MC 进行测试了。由于我们还没有完成物品组的创建,所以在游戏中无法直接获取到物品。不过我们可以通过 /give 指令来测试,例如:/give Player537 tutorial-mod:material/blue_powder,最终效果如下:
image-20250726191020648