Minecraft Fabric 模组开发案例——霰弹枪

摘要 在本文章中将基于Fabric展示Minecraft Java版模组开发制作一把霰弹枪的案例,所使用的Minecraft版本为1.19.2。由于霰弹枪模组开发所使用的编写方式不再限于基础,文章涉及Java基础语法和Fabric-MDK中的基础概念将不再赘述。
1. 开发环境的搭建
1.创建项目
本文的IDE使用Jetbrains IDEA作为演示,需要提前下载对应IDE和JDK环境,JDK需要Java17。
启动Jetbrains IDEA,下载插件Minecraft Development,也推荐下载Minecraft Resource Support插件以便于编辑资源文件。
新建一个项目,选择Minecraft -> Fabric Mod,然后点击“下一步”(如图1-1)。

填写组织名和ArtifactId,组织名(GroupId)任意,最好是<网站后缀>.<网名>,有自己的网站主页的最好填自己的,没有就随意了,因为这个组织名不影响模组编写;而ArtifactId作为模组的id,也就是模组的命名空间。需要注意的是组织名和ArtifactId不允许数字开头,不允许使用大写字母,参考Java的包名命名规范就好。版本(Version)时实际情况而定。读者根据自己的实际情况填写,然后“下一步”(参考图1-2)。

然后就要添加模组的基本配置信息:Mod Name是模组名称,可自定义;Miecraft Version 即游戏版本,这里选择1.19.2;Yarn Version是反编译后的源码版本,不同版本会有略微差别,导致编写时某些细节处理方式不同,但不影响运行效果,这里选1.19.2+build.28(推荐使用当前Minecraft稳定版本的最新版),Loader Version版本是以后导出模组后所需的Fabric加载器最低版本,Fabric加载器一般是向下兼容的,这里选0.14.9;Loom Version保持默认;Environment选择Both,因为这个模组需要在逻辑Client和Server端运行;Use Mixins和Decompile Minecraft保持勾选(后面要用Mixin);License任意;Fabric API 勾选,版本让IDE自己推荐;剩下的Description/Authors/Website/Repository分别是模组描述、模组作者、网站主页、远程仓库,这四个都是选填项,也可以不填,读者按实际情况自行填写(参考图1-3)。

最后是项目名称和存放路径,读者自行填写(如图1-4)。设置完毕点击“创建”即完成了项目的创建。

2.加载Gradle任务
点击项目中的加载Gradle任务的按钮(图1-5),即可加载Gradle任务,将下载模组开发所需的源码和库。需要注意的是Gradle任务一般需要很长时间加载,可以参考《如何加速Fabric模组的构建》[1] 。

2. 物品/实体/音效的加载
在该步骤中要完成物品(霰弹枪、霰弹物品)/实体(霰弹实体)/音效(包括霰弹枪装填时和射击的音效)的编写、加载和注册,值得注意的是该步骤仅仅是定义了这些游戏元素,其功能还有贴图、模型目前还没有加载,这将在后文中另外说明。在下文中读者可以根据包名判断Java类文件的位置。
1. 物品
先需要编写霰弹枪物品的类,为了方便直接继承CrossbowItem(即弩的类)
这个ShotgunItem类仅仅是定义了一个物品,具体功能将在后文中论述。在定义了ShotgunItem后还需要注册物品。现在编写物品注册的类ItemRegistry
此时由于ItemRegistry.register()还没有被调用,将在定义完成音效和实体后统一注册。
2. 音效
根据Fabric Wiki的教程 [2],加载音效是需要注册的,但是在本案例中发现,即使不注册音效也可以正常调用,现在就来加载音效。
step 1:准备音效文件(OGG格式)
这些音效文件应该存放于resources/assets/shotgun/sounds,其中shotgun是模组的 命名空间,这里一共准备了4个音效文件(图2-1)

step 2:编写sound.json (即resources/assets/shotgun/sounds.json)
step 3:定义音效,需要创建一个SoundInit类。
上面代码中的Shotgun.ModID 就是模组的命名空间,为了方便调用需要在主类Shotgun中声明,如果不声明,使用字符串"shotgun"代替也是可以的。而Identifier中的path参数必须与sound.json中的键对应。
3. 实体
现在要创建一个霰弹的实体,参考Fabric的官方教程 [3]。
step 1:先创建一个GrapeshotEntity类定义霰弹实体
step 2:然后在主类Shotgun中定义注册GrapeshotEntity的EntityType,顺便把之前的物品注册调用了。
step 3:现在继续编写GrapeshotEntity:
step 4:注册实体的渲染(ShotgunClient类并没有完成编写,仅仅是注册了实体渲染,后期还有其他作用)
至此物品、实体、音效的定义已经完成。
3. 霰弹枪动作模型的制作和加载(重点)
1. 建模
本案例中推荐使用Blockbench建模,需要为霰弹枪装填和射击时的不同状态建模,模型的纹理存于resources/assets/shotgun/textures/item。



图3-2 resources/assets/shotgun/models/custom/shotgun_loading_1.json 模型效果
对于重复几何特征的模型可以用修改贴图即可
然后编写shotgun.json在resources/assets/shotgun/models/item中,文件中的"overrides"将在下文中提及用法。
还要建立霰弹物品的模型resources/assets/shotgun/models/item/grapeshot.json

至此建模工作完成,现在物品是有模型和贴图的了。
2. 注册模型的动作效果
参考 Model Predicate Providers [4] 。
step 1:创建ModelPredicateInit类来定义模型动作,需要与前文resources/assets/shotgun/models/item/shotgun.json的"overrides"对应:
step 2:然后在ShotgunClient类中调用ModelPredicateInit.register()注册。
此时ShotgunClient也编写完成,现在动态霰弹枪模型已经完成(但是由于ShotgunItem的功能尚未实现,仅仅是继承了CrossbowItem,所以此时的霰弹枪会像弩那样射箭)。
4. 基于Mixin修改使用霰弹枪的第一/第三人称手部动作(重点)
现在需要通过Mixin修改人物手持霰弹枪使用时的动作,有关Mixin的用法参考 Introduction to Mixins [5]。
1. 修改第三人称手部动作
在装填时,需要手部的换弹动作;装填后,需要有持枪的动作。这可以通过Mixin修改net.minecraft.client.render.entity.PlayerEntityRenderer实现,新建:PlayerEntityRenderer类
修改完成后理论效果如下(在下文中还需要接入Mixin接入点才能实现功能):


2. 修改第一人称手部动作
需要修改net.minecraft.client.render.item.HeldItemRenderer类实现,新建HeldItemRendererMixin,里面的模型位置的矩阵基本上可以参考net.minecraft.client.render.item.HeldItemRenderer的弩等其他物品:
在后文中会将HeldItemRendererMixin接入Mixin入口点,理论上的效果如下:



3. 接入Mixin接入点
需要修改resources/shotgun.mixins.json实现
这样之前通过Mixin修改的手部动作就可以正常工作了。
5. 霰弹枪的基础功能实现——装填/射击/消耗耐久
现在就需要在ShotgunItem类中逐步实现装填/射击/消耗等基础功能了。其中use()定义了玩家手持霰弹枪右键时调用的方法,决定了是否成功调用霰弹枪,是否消耗耐久;setCharged()方法用于调用NBT标签来定义霰弹枪是否装填,isCharged()可以判断霰弹枪是否装填;shoot()方法定义了开火的过程,包括子弹的伤害,后坐力大小等等; inventoryTick()方法是只要霰弹枪物品位于玩家的背包中就会一直调用,而检测霰弹枪的耐久是否耗尽的过程就位于inventoryTick()中。
现在霰弹枪装填、射击、消耗耐久的功能都有了,如图5-1。

6. 霰弹枪的高级功能实现——多重射击(难点)
接下来就要实现霰弹枪的高级功能——多重射击。由于霰弹枪是可以附魔多重射击的,那么附上多重射击附魔的霰弹枪就应该实现多重射击的效果。这里的实现思路是无论是否附有多重射击,都在use()中调用一次shoot();如果附有多重射击附魔,就额外赋予物品一小段时间的冷却(这里是16tick),并且在inventoryTick()中检测冷却状态,在冷却时间即将结束前的一个瞬间(大约是冷却结束前最后1个tick)额外调用一次shoot(),这样就实现了多重射击,这样就得到了ShotgunItem的完整代码,如下:
多重射击的效果如下图:

至此霰弹枪物品的功能已经全部实现。
7. 合成表与成就/进度
1. 物品合成表
现在要实现霰弹枪物品和霰弹物品的合成表,相关教程参考 Crafting recipes [6]。
现在制作霰弹枪物品的合成表(resources/data/shotgun/recipes/shotgun.json):
然后是霰弹物品的合成配方(resources/data/shotgun/recipes/grapeshot.json)
具体合成效果如下:


2. 合成配方的解锁
很多时候玩家在获得了对应物品的原材料时就会解锁合成该物品的配方,现在就要实现解锁霰弹枪物品和霰弹物品合成表的进度。
先是解锁霰弹枪配方的文件(resources/data/shotgun/advancements/recipes/combat/shotgun.json)
然后是解锁霰弹物品配方的文件(resources/data/shotgun/advancements/recipes/combat/grapeshot.json)
3. 成就
我们希望当玩家获得霰弹枪时可以解锁成就——“Shotgun Battle King”。
现在编写成就文件(resources/data/shotgun/advancements/story/shotgun_battle_king.json)
这样自定义成就就实现了。
8. 语言文件的编写
由于注册游戏元素时,游戏元素在游戏中的命名规则类似与Java包名的命名规则,并不适合让用户使用,所以需要创建一个语言文件,参考 Name translations [7]。现在一共要制作中文和英文两个语言文件。
这是中文语言文件(resources/assets/shotgun/lang/zh_cn.json)
然后是英文语言文件(resources/assets/shotgun/lang/en_us.json)
这样语言文件编写就完成了。
9. 模组的导出与使用
1. 导出
运行Gradle任务 shotgun build (不同模组会因不同的ArtifactId而不同,是build就可以),就会生成模组的JAR文件。

生成的JAR文件在build/libs中(这里是build/libs/shotgun-1.0.0.jar,另一个shotgun-1.0.0-sources.jar可以查看源码,但是模组没有版本信息,尽量不用)

2. 使用
在使用此模组的时候需要先下载带有对应版本Fabric Loader的Minecraft (这里是Mine craft 1.19.2 + Fabric Loader 0.14.14),还需要下载对应的Fabric API(这里是fabric-api-0.73.2+1.19.2)。将模组文件导入对应版本Minecraft的mods文件夹。启动游戏即可。

启动游戏后就可以看到模组了。

源码仓库:https://github.com/Jaffe2718/Fabric-Shotgun-Mod-1.19.2-MDK
参考文献
[1] FabricMC.如何加速Fabric模组的构建-FabricMCCN. (2021, June 28). https://fabricmc.cn/2021/06/28/如何加速Fabric模组的构建/
[2] FabricMC. (2023, January 9). Playing Sounds. Fabric Wiki. https://fabricmc.net/wiki/tutorial:sounds
[3] FabricMC. (2022, December 21). Creating a Custom Projectile. Fabric Wiki. https://fabricmc.net/wiki/tutorial:projectiles
[4] FabricMC. (2022, June 1). Model Predicate Providers. Fabric Wiki. https://fabricmc.net/wiki/tutorial:model_predicate_providers
[5] FabricMC. (2023, January 11). Introduction to Mixins. Fabric Wiki. https://fabricmc.net/wiki/tutorial:model_predicate_providers
[6] FabricMC. (2022, December 16). Crafting Recipes. Fabric Wiki. https://fabricmc.net/wiki/tutorial:recipes
[7] FabricMC. (2022, May 27). Name Translations. Fabric Wiki. https://fabricmc.net/wiki/tutorial:lang