物品组
前面我们成功向 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 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 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:以后就不提版本控制和运行测试了,大家自己操作就行。