物品栏
提醒一下
本教程相当于是我在写笔记
在遇到问题的时候可以选择,先阅读fabricmc官网的文档
以及通过工具如AI,chatGPT去搜索一下,或者去必应一下
- 也可以先去b站搜索一下,b站上有很多大佬,可以去学习一下
- 本教程适用于Minecraft 1.21 Fabric的模组开发,不是Java教程。在开始之前也请先学习Java,具有一定的Java基础后再来学习
- 在此真诚感谢北山大佬的模组开发教程
什么是物品栏?
我们来说说什么是物品栏,好的下图就是物品栏,再见(hh
玩过一些中大型mod内容基本上都知道,创造界面在原版基础上,mod物品大多数都不是在原版物品栏,如材料,方块等中里面的
而是在原版GUI界面变化出的翻页中里面拥有,mod自己图标,名字的物品栏
我这节学的就是如何实现上图
Fabric API方法
介绍
Fabric API提供了能将我们的物品加入原版物品栏的方法,本质上使用的是Mixin。
不过,一旦你的模组里面使用了Fabric API,那么你的模组就需要这个API运行,打包之后放到真正的游戏中就要安装Fabric API。
使用方法
Fabric API提供了一个FabricItemGroupEntries
类,
我们先将物品加入到这个entries中,再由ItemGroupEvents
添加至原版物品栏
添加物品
1 | private static void addItemToIG(FabricItemGroupEntries fabricItemGroupEntries){ |
这里我们先创建一个方法,将我们的物品加入到entries中。
这个方法目的就是给你的新建的一个物品栏取个名字
添加至原版物品栏
然后我们将之前创建的物品加入到entries中,这里我们使用的是ICE_ETHER
也就是上面取的名。
1 | ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register(ModItems::addItemToIG); |
在之前写的初始化方法registerModItems
中添加这一行代码,这样我们的物品就会被加入到原版的材料物品栏中。
modifyEntriesEvent
方法的参数是一个ItemGroup
,这里我们使用的是ItemGroups.INGREDIENTS
,这个是原版的材料物品栏。
具体其他的物品栏可以查看ItemGroups
类。(注意,你需要genSource才能够正确调用ItemGroups
中的字段)
后面的register
方法直接引用我们之前创建的方法即可。
- 注册事件监听器:
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS).register(ModItems::addItemToIG);
- 这行代码注册了一个事件监听器,当“原料”物品组的内容需要被修改时,
addItemToIG
方法会被调用。
- 事件触发:
- 当 Minecraft 游戏需要修改“原料”物品组的内容时,事件处理机制会调用所有注册的回调函数。
- 在这种情况下,
addItemToIG
方法会被调用,并传入一个FabricItemGroupEntries
对象。
- 方法调用:
addItemToIG
方法的签名是private static void addItemToIG(FabricItemGroupEntries fabricItemGroupEntries)
。- 事件处理机制会自动传入一个
FabricItemGroupEntries
对象,这个对象包含了当前物品组的所有条目。 - 在
addItemToIG
方法中,你可以对这个FabricItemGroupEntries
对象进行操作,例如添加新的物品。 - 这个静态方法引用说简单点就是,省略了一个接受
FabricItemGroupEntries
参数的函数的基础上再直接引用了一个ModItems
类的静态addItemToIG
方法。
举一反三
那如果说我现在还有一个物品,想加入到原版的杂项(MISC)物品栏中,应该怎么做呢?
1 | private static void addItemToIG2(FabricItemGroupEntries fabricItemGroupEntries){ |
这里我们再创建一个方法,与之前的方法类似,将我们的物品加入到entries中。
添加多个物品直接用add
方法即可。
1 | ItemGroupEvents.modifyEntriesEvent(ItemGroups.MISC).register(ModItems::addItemToIG2); |
那么同样的,使用不同的物品栏,只需要将ItemGroups.INGREDIENTS
替换成ItemGroups.MISC
即可。
然后再引用addItemToIG2
方法即可。`
原版方法
介绍
使用Fabric API并不能创建自定义的物品栏,只能将物品加入到原版的物品栏中。
所以说如果想要创建自定义的物品栏,还是需要使用原版的方法。
查看源代码
我们先来看看原版是如何添加物品到物品栏的。上面也提到过了,原版物品栏的注册在ItemGroups
类中。
1 | java |
首先我们看到它的注册,这里使用的是ItemGroups.register
方法,这个方法是一个静态方法,返回一个RegistryKey<ItemGroup>
。
1 | java |
看着这个方法的返回语句,是否和之前物品的注册有些类似呢?是的没错,同样的,Identifier
要我们自行更改
那么除此之外,还有什么要注意的呢?我们可以看到registerAndGetDefault
方法中一堆的entries.add(...)
那么这些东西便是将物品加入到物品栏的方法了
-
1
2
3
4
5
6
7
8java
Registry.register(registry, INGREDIENTS,
ItemGroup.create(ItemGroup.Row.BOTTOM, 3)
.displayName(Text.translatable("itemGroup.ingredients"))
.icon(() -> new ItemStack(Items.IRON_INGOT))
.entries((displayContext, entries) -> {
...
}).build());
- 这里我们以
INGREDIENTS
为例,我们可以看到它使用的是Registry.register
方法,这个方法是用来注册物品栏的 - 但是首先我们得知道这个
registry
应该写什么,其实它就是Registries.ITEM_GROUP
,这个是原版的物品栏注册器 -
1
public static final Registry<ItemGroup> ITEM_GROUP = Registries.create(RegistryKeys.ITEM_GROUP, ItemGroups::registerAndGetDefault);
- create(RegistryKeys.ITEM_GROUP, ItemGroups::registerAndGetDefault):使用 create 方法创建一个注册表。
- RegistryKeys.ITEM_GROUP:这是注册表的键,表示物品组注册表。
- ItemGroups::registerAndGetDefault:这是一个方法引用,表示 ItemGroups 类中的 registerAndGetDefault 方法。当注册表需要初始化时,会调用这个方法来注册默认的物品组并返回该物品组。
我们在
Registries
类中可以看到,ITEM_GROUP
是一个Registry<ItemGroup>
类型的常量,创建的时候使用的是registerAndGetDefault
方法而我们自己写的时候直接使用
Registries.ITEM_GROUP
即可那么接下来,我们看到
INGREDIENTS
的第二个参数是INGREDIENTS
,这个是一个RegistryKey<ItemGroup>
类型的常量,这个是物品栏的ID然后我们看到
ItemGroup.create
方法,这个方法是用来创建物品栏的,里面有一些参数,比如ItemGroup.Row.BOTTOM
,这个是指物品栏所在的位置,这里的BOTTOM
表示它在GUI的下面那行中,3
是指在第4个;
那么其他的还有ItemGroup.Row.TOP
,这个是指在GUI的上面那行中我们接着看
displayName
方法,这个是用来设置物品栏的名字的,这里使用的是Text.translatable
方法,这个方法是用来设置物品栏名字的,这里使用的是itemGroup.ingredients
,这个是一个翻译键,我们可以在语言文件中找到这个翻译键,然后设置物品栏的名字然后我们看
icon
方法,这个是用来设置物品栏的图标的,这里使用的是Items.IRON_INGOT
,这个是物品栏的图标,这里使用的是铁锭entries
方法是用来设置物品栏的物品的,这里使用的是一个lambda表达式,这个lambda表达式有两个参数,
一个是displayContext
,一个是entries
,这个displayContext
是用来设置物品栏的显示的,这里没有用到,entries
是用来设置物品栏的物品的,这里的东西省略了,我们就不展开了
创建ModItemGroups类
1 |
|
注册方法
1 |
|
那么Identifier
同样的还是要改
注册物品栏Key
1 |
|
这里的都是仿照原版在编写
初始化注册方法
1 |
|
不要忘记到主类调用这个初始化方法
1 | java |
注册物品栏
这个语句直接写在初始化注册方法中就好了,这样在模组初始化时就可以注册完成
1 | java |
这个就是和原版一模一样的语句,不同的地方在于第一个参数我们直接用了Registries.ITEM_GROUP
而ItemGroup.create
方法里面的参数,不要和原版重叠(虽然我没试过会发生什么),然后具体的位置其实可以在确定有多少个物品栏之后再写。
实际情况是,如果前面空着,比如说我里面的参数写8
,但7
的位置没有东西,那么你新增的物品栏的位置还是7
。TOP
和BOTTOM
同理,前者补完补后者
简化?YES
是不是觉得还是很复杂?能不能简化呢?当然可以(不过有个弊端,后面dataGen跑语言文件生成的时候就不能直接调写的KEY了,不过直接复制displayName
也一样)
这里我们就要利用返回值为ItemGroup
这个特性,还记得源代码里面registerAndGetDefault
方法的返回值吗?没错,它就是ItemGroup
所以在这里,我们依旧可以选择让它修饰为和Item
一样的static final
,利用初始化完成注册
1 | java |
中间的Identifier
直接写你的MOD_ID
和id
即可
然后我这里的ItemGroup.create
直接使用null
和-1
,这个就直接让它填在最后一个位置上,如果你使用FabricItemGroup
进行创建,它就是这样写的
这样就可以简化很多,另外你也可以添加原版中有的物品,比如Blocks.BRICKS
和Items.DIAMOND
,这样就可以直接添加原版的物品了
不过,初始化方法还是要写的,也记得在主类中调用这个初始化方法
整体代码
1 | java |
语言文件
1 | json |
这个就是我们的物品栏的名字,这里的itemGroup.tutorial_group
就是我们在displayName
方法中设置的翻译键
测试
现在我们启动游戏,由于原版的物品栏已经填满了第一页,不过它会自动生成一个翻页符,我们点击翻页符,就可以看到我们的物品栏了