第一个物品 1.20 Fabric 长线教程计划
本篇教程的视频
本篇教程的源代码
GitHub地址:TutorialMod-Item-1.20.1
前述
视频教程里稍微提了一下,本系列教程为长线教程计划
教程模式和1.21
的一样,我将从源代码的层面来讲模组开发
这里的话,顺便浅谈一下Fabric
和Forge
及NeoForge
区别
它们两个的话,你就只能用它们的那个注册系统,而不能像Fabric
那样直接用原版的注册系统
不知道大家有没有像我一样去尝试过,如果你按照原版的方法去注册物品、方块等东西,
在游戏加载的时候,会直接抛出类似于注册表已冻结
这样的信息,游戏崩溃
所以,我猜测(对,只是猜测
,因为没去完全了解过他们具体的工作逻辑),Forge
和NeoForge
在加载的时候,会先加载Minecraft
的主程序,然后再加载模组
但Fabric
其实在它自己的Wiki
上也说了,Fabric
是完全通过Mixin
来工作的,
它本身也就是个Mixin
,我们的模组也是通过它直接注入游戏中的
所以Fabric
很轻,加载速度比那两个快得多,但看起来没那么稳定
以前确实是有大型模组
在Forge
开发,而轻量级
的模组在Fabric
开发这样的说法
不过现在,其实已经差不多了,你在哪个端开发都没关系。
只是,Forge
和NeoForge
有更多的轮子
(API
),而Fabric
的话,
相对欠缺一点,但也可以自己造
而且在Fabric
,Mixin
也会用得多一点,不过,Mixin
使用前提是你对Minecraft
的源码有一定了解,
否则很容易把正常的游戏给改崩
本篇教程目标
- 理解物品注册
- 理解模组开发之路的第二块绊脚石——Identifier类
- 理解物品的基本属性
- 编写物品模型的json文件(贴图就自己学吧)
查看源代码
那么现在,我们正式开始讲解第一个物品
假设说,你现在什么都不会,不知道物品该怎么添加,然后其他的教程也没看过
那么,很简单,看源代码
我们利用之前讲过的IDEA
的随处搜索,快捷键double shift
,搜索item
item
是物品的英文,玩Minecraft
的应该都知道吧
注意,这里搜索的范围不只是我们的项目文件,我们要将范围拓展到所有位置
所有位置的话,它就包括了外部库里的那些东西
而后,会冒出来一堆类
但要注意分辨
,我们要找的是Minecraft
的源代码,不是其他的库
它是net.minecraft.item
下的类,注意看各个类后面的包的名字
不过,我们要找的其实并不是Item
这个类,而要找Items
这两个类都存在,也都是Minecraft
的类,随着我们教程的开展,你还会发现另外的,
例如Block
和Blocks
、ItemGroup
和ItemGroups
等等
感兴趣的同学可以先自己去研究一下,我这里的话也简单说明一下
一般来说,带s
的是Minecraft
的注册类,而不带s
的是定义各个类的基本属性
比如Item
类,你可以在这个类中发现物品的一些基本属性,像默认最大堆叠数量64
、默认最大使用次数32
、稀有度
等等
还有各种各样的方法
,到时候我们讲解自定义物品类的时候可以重写它们,来实现自己的一些逻辑
而Items
类,这是物品的注册类,这里注册了所有的Minecraft
物品,你会发现清一色的public static final
字段,以及一系列的register方法
源代码中的注册方法
那么register
翻译翻译,就是注册
的意思
所以现在,我们就来看看源代码中的注册方法
不过先得挑一个,因为在这里不只有单纯的物品,还有像方块物品
这样的,这个东西我们在后面讲方块
的时候会讲到
还有像一些物品实例化的并不是Item
类,而是实例化它们自己的类,像我们之后会讲到的盔甲
、武器
等
所以这里的话,我们挑一个简单的,几乎没什么特别的属性物品,DIAMOND
,也就是钻石
搜索的快捷键是CTRL + F
,搜索diamond
,可以再加一个空格
,就能定位到钻石这个物品了
1 | public static final Item DIAMOND = register("diamond", new Item(new Item.Settings())); |
register
里面的东西我们先不用看,先看这个方法本身
1 | public static Item register(String id, Item item) { |
DIAMOND
使用的方法是第一个register
,同名的重载方法有一堆,也层层叠叠调用了一堆方法
最终调用了最后一个register
方法,也就是我们真正要看的那个方法
1 | Registry.register(Registries.ITEM, key, item); |
这里的Registry
是Minecraft
的注册类,在这个类的注释
中,也有提示,告诉我们物品该怎么注册
不过,我们先不管他,还是回过头来看这个方法
这个方法接受三个参数,第一个是注册表
,第二个是注册键
,第三个是注册的物品
Registries
是Minecraft
的注册表类,它里面定义了所有Minecraft
的注册表,
之后我们注册方块
、物品栏
都会用到其中的字段
至于第二个注册键,我们可以根据上面三个方法进行整合
1 | Registry.register(Registries.ITEM, RegistryKey.of(Registries.ITEM.getKey(), new Identifier(id)), item); |
这里传入的id
是String
类型的,然后,实例化Identifier
类,传入id
Identifier类
那么在这里,我们就要重点讲讲Identifier
类了
这是定义Minecraft中所有资源文件路径
的类,我们可以看看它的注释
1 | An identifier used to identify things. |
它这里也说了,这个类也被称为资源位置
、命名空间ID
、位置
或者ID
1 | Identifiers are formatted as <namespace>:<path>. |
它的格式也在注释这有说明,就是<命名空间>:<路径>
,命名空间缺省的话,默认为minecraft
在之后的教程中,我提到的所有关于命名空间
的事情,都是从这个类里来的
而我们模组
的命名空间
,则是我们的modid
1 | The namespace and path must contain only |
敲黑板,划重点了
为什么说命名空间
能成为模组开发之路上的第二块绊脚石
(第一块是Gradle)?
因为不少人在定义物品
、方块
的名字,其他的一些命名空间等东西的时候,会写非法字符
这里的注释说的很清楚了,命名空间和路径只能包含ASCII小写字母[a-z]
、ASCII数字[0-9]
、下划线[_]
、点[.]
和短横线[-]
而路径还可以包含标准路径分隔符/
其余的全是非法字符
,包括大写字母
所有,定义命名空间及路径的时候看清楚,不要写非法字符,再写我可要嘲笑你了
物品的基本属性
好,看完Identifier
类,我们再回过头来看物品具体注册时候实例化
的方法
1 | public static final Item DIAMOND = register("diamond", new Item(new Item.Settings())); |
还是DIAMOND
,第一个参数是它的名字
,后面实例化了一个Item
类,并传入了一个Item.Settings()
在这个Settings()
中就是物品的基本属性的类,嵌套在Item
类中
1 | public static class Settings { |
可以看到,默认定义好的最大堆叠数量64
,稀有度为普通
,另外的属性可以通过下面的方法加入,也可以更改默认的这两个
不过,具体的我们到后面再说,因为现在我们只需要注册一个最简单的物品即可
注册第一个物品
好了,前面铺垫工作基本完成了
你看,要讲的东西其实很多很多,要换种教程模式的话,我就直接从这里开始讲了
创建注册物品类
现在我们来真正开始写第一个物品,和原版一样,我们也先创建一个用于注册物品的类ModItems
1 | public class ModItems { |
然后我们把原版的注册方法
搬过来
1 | public static Item register(String id, Item item) { |
当然,这里的命名空间改成了我们模组的MOD ID
,不然到后面找你资源文件的时候就跑到Minecraft
命名空间下去了
优化注册方法
不过,上面我也提了一下整合方法,这四个方法其实是可以整合为一个方法的
1 | public static Item registerItems(String id, Item item) { |
什么?还能优化?还有高手?
是的,Registry.register
其实还有一个简化的重载方法,我们可以先看一下
1 | static <V, T extends V> T register(Registry<V> registry, Identifier id, T entry) { |
这里的第二个
是我们目前使用的方法,而第一个
调用的也是第二个方法,唯一的区别就是第一个方法的第二个参数为Identifier
类型,而第二个方法的第二个参数为RegistryKey
类型的
我们可以直接利用第一个方法来简化我们的代码,顺带一提,它也是我们在后面讲方块时,原版采用的注册方法
1 | public static Item registerItem(String id, Item item) { |
注册物品
好,现在我们就可以开始注册我们的第一个物品了
和DIAMOND
的写法一样,写一个最简单的物品
1 | public static final Item ICE_ETHER = registerItem("ice_ether", new Item(new Item.Settings())); |
还是我们的老朋友ICE ETHER
那么,我们举一反三,再写一点
1 | public static final Item RAW_ICE_ETHER = registerItem("raw_ice_ether", new Item(new Item.Settings())); |
我们再加入RAW ICE ETHER
和CARDBOARD
,
其中CARDBOARD
我加了一个路径material
,我们在后面写模型文件的时候会看到它和另外两个的区别
初始化注册方法
我们的物品已经写好了,但还没用,你到游戏中还是找不到添加的物品
因为我们这个类没有初始化
这里我们再写一个无返回值的空方法
1 | public static void registerItems() { |
随后,到模组主类
的onInitialize
方法中去调用它
1 |
|
onInitialize
方法会在模组加载的时候调用,而此时我们的方法也会被调用
那么为什么加了这个方法就可以完成初始化
了呢?
因为你会发现,我们写的这三个物品注册,都是static final
修饰的,这个类被调用了,那么它们就必须初始化
初始化的结果呢,就是调用注册方法
,从而让物品注册到游戏中
资源文件
物品注册到这里就好了,不过现在进入游戏,
虽然可以获得你的物品,但你会发现它是一个大大的黑紫块
,名字还老长一串
因为现在,我们的物品没有模型
,也没有材质
原版的资源文件
都可以在外部库
中找到,在其assets
文件夹下,另外一边的data
则为数据文件
那么我们的模组
也有一个assets
文件夹,这里是存放我们模组的资源文件
语言文件
这里我们先来写语言文件,在assets/<modid>
下新建一个lang
文件夹,然后新建一个en_us.json
文件
注意文件路径
,Minecraft
的文件读取是有严格的规范的,你不能自己乱写
这里的en_us.json
文件是英文
语言文件,这是默认的语言文件,在其他语言文件缺省时,会默认使用它
当然你也可以新建一个zh_cn.json
文件,用于简体中文
的语言文件
1 | { |
这里我们定义了三个物品的名称,其中item.tutorial-mod.ice_ether
是实际注册在Minecraft中的名字
它是由你注册的类型.你的MOD ID.你的物品名字
组成的
也就是你不写语言文件则会显示这么长一串,而后面的值则会翻译这一串东西
中文的语言文件同样如此,这里就不再赘述
物品模型
语言文件写好了,现在我们再来看模型文件
在assets/<modid>/models/item
下新建一个ice_ether.json
文件,用于存放ICE ETHER
的模型文件
1 | { |
这个模型继承自minecraft:item/generated
,也就是原版物品的模型
它是原版最基本的物品模型,也就是在你贴图基础上加厚一层
其中layer0
是贴图,我们这里贴图路径是tutorial-mod:item/ice_ether
那么RAW ICE ETHER
的模型文件也差不多
1 | { |
但CARDBOARD
呢?前面我们多加了一个路径
所以,在这里我们就要在assets/<modid>/models/item/material
下新建一个cardboard.json
文件
注意文件路径多了一个material
1 | { |
我在这里加这么一个路径
的原因呢很简单,一方面让大家再感受一下Identifier
的用法,
另一方面呢,也是方便管理
虽然我们现在的东西不多,但你自己的开发过程中,或许会有成百上千个
东西要写,不给它分类的话,如果要修改某些文件,找就得找半天
方舟家具模组
全面重写之后,也就采用了这种方法,毕竟成百上千个模型,后面要改的话,光是找就得要命了
不过,这里贴图路径的material
可以不写,为了方便管理也可以写
物品材质
物品材质的绘制的话,Photoshop
就可以画,也可以拿专业的像素画软件来画,如Aseprite
我们要将贴图文件放在assets/<modid>/textures/item
下
另外,如果上面cardboard
的贴图文件路径不加material
的话,则和另外两个放一起就行
如果写上,则得将贴图放在assets/<modid>/textures/item/material
下
启动游戏测试
那么现在,我们就可以启动我们的游戏去测试一下
由于我们现在没有将物品加入到创造模式物品栏中,只能通过give
命令来获得物品
give
输入我们的物品时,命名空间改为我们模组的modid
,而不是minecraft
另外,以后当你找不到你新加的物品时,也可以用这种方法来检测一下
成功注册的物品都可以用give
命令来获得