Fabric 模组开发 - Minecraft1.20.1:#2. 创建第一个物品
注册类别介绍
添加基本的物品是编写模组的第一步。基本逻辑是创建 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.item
的 Items
类(搜索位置为所有位置):
基本上,一个单词若是
Item
,在 MC 大概率是定义物品基本属性的,而Items
则是注册方面的。
打开该文件,我们搜索(快捷键是 ctrl+f)diamond
钻石这一单词,找到这个最简单的物品,观察它是何如被 MC 实现的:
可以看到 DIAMOND
被一个 register
方法进行注册,于是我们使用鼠标选中这个 register
,点击鼠标中键进行跳转,观察 register
方法(或者按住 Ctrl 建对 register
点击鼠标左键):
可以看到从上到下总共存在三个静态注册物品的方法:
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);
}
看不懂?点我展开!
首先,这三个方法的调用顺序如下:
第一个方法,帮你把要创建的物品标记上你指定的名字。但 Identifier
它本身不包含任何其他基本信息,是不知道你在注册物品还是方块的,所以需要第二个方法进一步增强类型安全。具体来说,主要是接收你定义的物品名字 String id
,并将其转换为 Identifier
对象,Item item
参数若没有对物品基本属性进行指定则使用默认属性,然后将这俩作为参数传入给第二个 register
方法进一步增强类型安全。
其中的
Identifier
是 MC 用来标识资源的类,就是用来声明一个名字。格式为: 1new 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 是一个从Block
到Item
的映射,用于在需要时(比如显示方块物品)找到对应的物品。
真正执行注册的语句是: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
中新建:
lang
包:存放语言文件。如en_us.json
、zh_cn.json
等,若其他语言文件损坏将默认采用en_us.json
models
包:存放模型文件。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
包,新建两个软件包:block
和 item
包,对于 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
就是来指定 assets
的 textures
中具体的面数,我们这个物品比较简单,所以就是一个面数就行。
在 resources/assets/tutorial-mod/textures/item/material/
中,将准备好的贴图文件放到该目录下(文件格式为 png,且文件名和你定义的物品名一致)。
我的图片是:
贴图素材网站
- NOVA SKIN:能在线修改 MC 本体的内容素材并免费下载到本地。
- Plant Minecraft-Texture Packs:主要是用户上传的素材。
如果有更好的网站,欢迎推荐!
运行测试
至此,已经可以尝试运行 MC 进行测试了。由于我们还没有完成物品组的创建,所以在游戏中无法直接获取到物品。不过我们可以通过 /give
指令来测试,例如:/give Player537 tutorial-mod:material/blue_powder
,最终效果如下: