Fabric 模组开发 - Minecraft1.20.1:#3. 创造模式物品组
物品组
前面我们成功向 MC 中添加了一个 Mod 物品,但是,它不像原版物品,还不存在于任何物品组中,你不能在创造模式轻易获取!有两种方式实现:
- 将物品添加到已存在的物品组
- 创建你自己的物品组并添加物品
不论是哪种,添加到任何物品组的物品都可以在创造模式物品栏中搜索到。
添加到已存在的物品组
首先,我们需要决定要添加到哪个物品组。那我怎么知道 MC 有哪几个物品组呢?我们可以通过查看源码来确定。双击 shift
唤出搜索面板,搜索 ItemGroups
类(搜索位置为所有位置,以后将不再赘述):
这第一个来源为 net.minecraft.item
的(查看源码都是基于来源 minecraft,以后将不再赘述)就是我们需要查看的源码了:
对照表如下:
物品组 | 译 |
---|---|
BUILDING_BLOCKS | 建筑方块 |
COLORED_BLOCKS | 染色方块 |
NATURAL | 自然方块 |
FUNCTIONAL | 功能方块 |
REDSTONE | 红石方块 |
HOTBAR | 已保存的快捷栏 |
SEARCH | 搜索物品 |
TOOLS | 工具与实用物品 |
COMBAT | 战斗用品 |
FOOD_AND_DRINK | 食物与饮品 |
INGREDIENTS | 原材料 |
SPAWN_EGGS | 刷怪蛋 |
OPERATOR | 管理员物品 |
INVENTORY | 生存模式物品栏 |
原版总共 14 种物品组。其中,
OPERATOR
在按键控制中的管理员用品标签页
设置启用。
知道物品组之后,我们尝试将前面创建的紫色粉末和蓝色粉末加到 INGREDIENTS
原材料物品组中。
参考 Fabric 提供的 API(底层是 Mixin),我们使用 ItemGroupEvents.modifyEntriesEvent
为修改物品组创建事件处理器(ModItems.java
):
1
2
3
4
5
6
7
8
9
10
11
12
// 添加物品到原有的物品组中
public static void registerToVanillaItemGroups() {
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register(content -> {
content.add(PURPLE_POWDER);
content.add(BLUE_POWDER);
});
TutorialMod.LOGGER.info("fk-ItemGroups: items registered to vanilla item groups");
}
public static void registerItems() {
registerToVanillaItemGroups();
}
别忘记在主类 TutorialMod.java
中,在 onInitialize()
对 registerItems()
进行调用。效果如下:
如果觉得默认添加到物品组的底部比较麻烦,也可以精细化地控制添加位置。比如,将蓝色粉末加在原材料物品组的木炭后面:
1
2
3
4
5
6
7
8
// 添加物品到原有的物品组中
public static void registerToVanillaItemGroups() {
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register(content -> {
content.add(PURPLE_POWDER);
content.addAfter(Items.CHARCOAL, BLUE_POWDER); // 将蓝色粉末加在木炭之后
});
TutorialMod.LOGGER.info("fk-ItemGroups: items registered to vanilla item groups");
}
效果如下:
创建自定义物品组
创建自定义的物品组会被放置在单独的标签页中,这是一般 Mod 都会做的操作。
写法一(使用 Fabric API)
在 java/com/frozen/tutorialmod/item/
中,创建 ModItemGroups.java
类。
参考 Fabric 提供的 api,我们使用 FabricItemGroup.builder
方法来创建物品组的构建器,并调用 build
方法完成构建(ModItemGroups.java
):
1
2
3
4
5
6
7
8
9
public class ModItemGroups {
public static final ItemGroup TUTORIAL_GROUP = FabricItemGroup.builder()
.icon(() -> new ItemStack(ModItems.BLUE_POWDER)) // 标签页图标
.displayName(Text.translatable("itemGroup.tutorial-mod.tutorial_group")) // 必须显式指定显示名称,否则会报错
.entries((context, entries) -> {
entries.add(ModItems.BLUE_POWDER);
entries.add(ModItems.PURPLE_POWDER);
}).build();
}
紧接着,完成物品组的注册:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ModItemGroups {
public static final ItemGroup TUTORIAL_GROUP = FabricItemGroup.builder()
.icon(() -> new ItemStack(ModItems.BLUE_POWDER)) // 标签页图标
.displayName(Text.translatable("itemGroup.tutorial-mod.tutorial_group")) // 必须显式指定显示名称,否则会报错
.entries((context, entries) -> {
entries.add(ModItems.BLUE_POWDER);
entries.add(ModItems.PURPLE_POWDER);
}).build();
public static void registerMyItemGroups() {
TutorialMod.LOGGER.info("fk-ItemGroups: created new item group and registered items to it");
Registry.register(Registries.ITEM_GROUP, new Identifier("tutorial-mod", "tutorial_group"), TUTORIAL_GROUP);
}
}
别忘记在主类 TutorialMod.java
中对 registerMyItemGroups()
方法进行调用。
写法二(原生写法)
上面的方法一是 Fabric 提供的 API 实现的物品组注册,没什么好说的,参照官方写法就行。
值得注意的是,这种写法,Mod 的物品组默认位置为 TOP
且索引为 7
(14
种物品组分为上下各 7
组,且索引均从 0
开始),或者说索引为 -1
,是不能控制物品组的位置的。
那么原生写法如何写呢?其实,在 MC 源码的 ItemGroups.java
中就已经写了:
第一个 register
方法主要是在
那么参照原生写法,我们再来写一遍 ModItemGroups.java
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 写法二:使用Vanilla API 写法
public static final RegistryKey<ItemGroup> TUTORIAL_GROUP = register("tutorial_group");
private static RegistryKey<ItemGroup> register(String id) {
return RegistryKey.of(RegistryKeys.ITEM_GROUP, new Identifier(TutorialMod.MOD_ID, id));
}
public static void registerMyItemGroups() {
Registry.register(Registries.ITEM_GROUP,
TUTORIAL_GROUP,
ItemGroup.create(ItemGroup.Row.TOP, 7)
.displayName(Text.translatable("itemGroup.tutorial-mod.tutorial_group"))
.icon(() -> new ItemStack(ModItems.BLUE_POWDER))
.entries((context, entries) -> {
entries.add(ModItems.BLUE_POWDER);
entries.add(ModItems.PURPLE_POWDER);
}).build());
}
好像也挺不错?但是似乎又稍微麻烦了一点,需要写三个部分。
写法三(简化原生写法)
那么,可以尝试将上面的原生写法进行简化:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 写法三: 简化Vanilla API 写法
public static final ItemGroup TUTORIAL_GROUP = Registry.register(
Registries.ITEM_GROUP,
new Identifier("tutorial-mod", "tutorial_group"),
ItemGroup.create(ItemGroup.Row.TOP, 7) // 可选位置参数
.icon(() -> new ItemStack(ModItems.BLUE_POWDER))
.displayName(Text.translatable("itemGroup.tutorial-mod.tutorial_group"))
.entries((context, entries) -> {
entries.add(ModItems.BLUE_POWDER);
entries.add(ModItems.PURPLE_POWDER);
})
.build()
);
public static void registerMyItemGroups() {
TutorialMod.LOGGER.info("fk-ItemGroups: registered new item group using vanilla builder");
}
这种写法将方法二的构造、注册、返回链式的串在一起,声明即注册。
三种写法的优劣
其实优劣谈不上,这主要看构建 Mod 的目的是什么,是快速得到一个简单的 mod?还是它长期更新维护且可能涉及多人协作?我的考虑基于后者,会更推荐第二种写法:原生写法加上显式声明 RegistryKey
。这使得它更容易维护,并且显式声明 RegistryKey
也支持数据生成,风格标准。参考表如下:
写法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
写法一(Fabric API) | 简洁;API 现代;链式风格 | 不支持数据生成;不原生;依赖 Fabric API | 快速开发、小型项目 |
写法二(Vanilla + RegistryKey) | 支持数据生成;结构清晰;可扩展性强 | 稍微繁琐一点 | 中大型项目、多人协作、需要数据生成 |
写法三(简化 Vanilla) | 语法最短;直观;快速上手 | 不适配数据生成;不易重用 | 学习阶段、测试用途 |
PS:以后就不提版本控制和运行测试了,大家自己操作就行。