Minecraft Fabric 模組開發(fā)案例——霰彈槍

摘要?在本文章中將基于Fabric展示Minecraft Java版模組開發(fā)制作一把霰彈槍的案例,所使用的Minecraft版本為1.19.2。由于霰彈槍模組開發(fā)所使用的編寫方式不再限于基礎(chǔ),文章涉及Java基礎(chǔ)語(yǔ)法和Fabric-MDK中的基礎(chǔ)概念將不再贅述。
1. 開發(fā)環(huán)境的搭建
????1.創(chuàng)建項(xiàng)目
? ? 本文的IDE使用Jetbrains IDEA作為演示,需要提前下載對(duì)應(yīng)IDE和JDK環(huán)境,JDK需要Java17。
? ? ? 啟動(dòng)Jetbrains IDEA,下載插件Minecraft Development,也推薦下載Minecraft Resource Support插件以便于編輯資源文件。
? ? ?新建一個(gè)項(xiàng)目,選擇Minecraft -> Fabric Mod,然后點(diǎn)擊“下一步”(如圖1-1)。

????????????填寫組織名和ArtifactId,組織名(GroupId)任意,最好是<網(wǎng)站后綴>.<網(wǎng)名>,有自己的網(wǎng)站主頁(yè)的最好填自己的,沒有就隨意了,因?yàn)檫@個(gè)組織名不影響模組編寫;而ArtifactId作為模組的id,也就是模組的命名空間。需要注意的是組織名和ArtifactId不允許數(shù)字開頭,不允許使用大寫字母,參考Java的包名命名規(guī)范就好。版本(Version)時(shí)實(shí)際情況而定。讀者根據(jù)自己的實(shí)際情況填寫,然后“下一步”(參考圖1-2)。

? ? ? ? ? ? 然后就要添加模組的基本配置信息:Mod Name是模組名稱,可自定義;Miecraft Version 即游戲版本,這里選擇1.19.2;Yarn Version是反編譯后的源碼版本,不同版本會(huì)有略微差別,導(dǎo)致編寫時(shí)某些細(xì)節(jié)處理方式不同,但不影響運(yùn)行效果,這里選1.19.2+build.28(推薦使用當(dāng)前Minecraft穩(wěn)定版本的最新版),Loader Version版本是以后導(dǎo)出模組后所需的Fabric加載器最低版本,F(xiàn)abric加載器一般是向下兼容的,這里選0.14.9;Loom Version保持默認(rèn);Environment選擇Both,因?yàn)檫@個(gè)模組需要在邏輯Client和Server端運(yùn)行;Use Mixins和Decompile Minecraft保持勾選(后面要用Mixin);License任意;Fabric API 勾選,版本讓IDE自己推薦;剩下的Description/Authors/Website/Repository分別是模組描述、模組作者、網(wǎng)站主頁(yè)、遠(yuǎn)程倉(cāng)庫(kù),這四個(gè)都是選填項(xiàng),也可以不填,讀者按實(shí)際情況自行填寫(參考圖1-3)。

????????????最后是項(xiàng)目名稱和存放路徑,讀者自行填寫(如圖1-4)。設(shè)置完畢點(diǎn)擊“創(chuàng)建”即完成了項(xiàng)目的創(chuàng)建。

????2.加載Gradle任務(wù)
????????點(diǎn)擊項(xiàng)目中的加載Gradle任務(wù)的按鈕(圖1-5),即可加載Gradle任務(wù),將下載模組開發(fā)所需的源碼和庫(kù)。需要注意的是Gradle任務(wù)一般需要很長(zhǎng)時(shí)間加載,可以參考《如何加速Fabric模組的構(gòu)建》[1] 。

2. 物品/實(shí)體/音效的加載
????????在該步驟中要完成物品(霰彈槍、霰彈物品)/實(shí)體(霰彈實(shí)體)/音效(包括霰彈槍裝填時(shí)和射擊的音效)的編寫、加載和注冊(cè),值得注意的是該步驟僅僅是定義了這些游戲元素,其功能還有貼圖、模型目前還沒有加載,這將在后文中另外說明。在下文中讀者可以根據(jù)包名判斷Java類文件的位置。
????????1. 物品
????????????先需要編寫霰彈槍物品的類,為了方便直接繼承CrossbowItem(即弩的類)
????????????這個(gè)ShotgunItem類僅僅是定義了一個(gè)物品,具體功能將在后文中論述。在定義了ShotgunItem后還需要注冊(cè)物品。現(xiàn)在編寫物品注冊(cè)的類ItemRegistry
? ? ? ? ? ? 此時(shí)由于ItemRegistry.register()還沒有被調(diào)用,將在定義完成音效和實(shí)體后統(tǒng)一注冊(cè)。
?? ? ?? 2. 音效
????????????根據(jù)Fabric Wiki的教程 [2],加載音效是需要注冊(cè)的,但是在本案例中發(fā)現(xiàn),即使不注冊(cè)音效也可以正常調(diào)用,現(xiàn)在就來(lái)加載音效。
????????????step 1:準(zhǔn)備音效文件(OGG格式)
????????????????這些音效文件應(yīng)該存放于resources/assets/shotgun/sounds,其中shotgun是模組的?命名空間,這里一共準(zhǔn)備了4個(gè)音效文件(圖2-1)

????????????step 2:編寫sound.json (即resources/assets/shotgun/sounds.json)
????????????step 3:定義音效,需要?jiǎng)?chuàng)建一個(gè)SoundInit類。
????????????上面代碼中的Shotgun.ModID 就是模組的命名空間,為了方便調(diào)用需要在主類Shotgun中聲明,如果不聲明,使用字符串"shotgun"代替也是可以的。而Identifier中的path參數(shù)必須與sound.json中的鍵對(duì)應(yīng)。
????????3. 實(shí)體
????????????現(xiàn)在要?jiǎng)?chuàng)建一個(gè)霰彈的實(shí)體,參考Fabric的官方教程 [3]。
????????????step 1:先創(chuàng)建一個(gè)GrapeshotEntity類定義霰彈實(shí)體
????????????step 2:然后在主類Shotgun中定義注冊(cè)GrapeshotEntity的EntityType,順便把之前的物品注冊(cè)調(diào)用了。
? ? ? ? ? ? step 3:現(xiàn)在繼續(xù)編寫GrapeshotEntity:
????????????step 4:注冊(cè)實(shí)體的渲染(ShotgunClient類并沒有完成編寫,僅僅是注冊(cè)了實(shí)體渲染,后期還有其他作用)
?????至此物品、實(shí)體、音效的定義已經(jīng)完成。
3. 霰彈槍動(dòng)作模型的制作和加載(重點(diǎn))
???? 1. 建模
???????? 本案例中推薦使用Blockbench建模,需要為霰彈槍裝填和射擊時(shí)的不同狀態(tài)建模,模型的紋理存于resources/assets/shotgun/textures/item。



圖3-2 resources/assets/shotgun/models/custom/shotgun_loading_1.json 模型效果
????????????對(duì)于重復(fù)幾何特征的模型可以用修改貼圖即可
????????????然后編寫shotgun.json在resources/assets/shotgun/models/item中,文件中的"overrides"將在下文中提及用法。
????????????還要建立霰彈物品的模型resources/assets/shotgun/models/item/grapeshot.json

????????至此建模工作完成,現(xiàn)在物品是有模型和貼圖的了。
? ? ?2. 注冊(cè)模型的動(dòng)作效果
????????參考 Model Predicate Providers [4] 。
????????step 1:創(chuàng)建ModelPredicateInit類來(lái)定義模型動(dòng)作,需要與前文resources/assets/shotgun/models/item/shotgun.json的"overrides"對(duì)應(yīng):
???????????step 2:然后在ShotgunClient類中調(diào)用ModelPredicateInit.register()注冊(cè)。
????????????此時(shí)ShotgunClient也編寫完成,現(xiàn)在動(dòng)態(tài)霰彈槍模型已經(jīng)完成(但是由于ShotgunItem的功能尚未實(shí)現(xiàn),僅僅是繼承了CrossbowItem,所以此時(shí)的霰彈槍會(huì)像弩那樣射箭)。
4. 基于Mixin修改使用霰彈槍的第一/第三人稱手部動(dòng)作(重點(diǎn))
????現(xiàn)在需要通過Mixin修改人物手持霰彈槍使用時(shí)的動(dòng)作,有關(guān)Mixin的用法參考?Introduction to Mixins [5]。
? ? 1. 修改第三人稱手部動(dòng)作
????????在裝填時(shí),需要手部的換彈動(dòng)作;裝填后,需要有持槍的動(dòng)作。這可以通過Mixin修改net.minecraft.client.render.entity.PlayerEntityRenderer實(shí)現(xiàn),新建:PlayerEntityRenderer類
????????修改完成后理論效果如下(在下文中還需要接入Mixin接入點(diǎn)才能實(shí)現(xiàn)功能):


? ? 2. 修改第一人稱手部動(dòng)作
????????需要修改net.minecraft.client.render.item.HeldItemRenderer類實(shí)現(xiàn),新建HeldItemRendererMixin,里面的模型位置的矩陣基本上可以參考net.minecraft.client.render.item.HeldItemRenderer的弩等其他物品:
????????在后文中會(huì)將HeldItemRendererMixin接入Mixin入口點(diǎn),理論上的效果如下:



? ? 3. 接入Mixin接入點(diǎn)
????????需要修改resources/shotgun.mixins.json實(shí)現(xiàn)
????????這樣之前通過Mixin修改的手部動(dòng)作就可以正常工作了。
5. 霰彈槍的基礎(chǔ)功能實(shí)現(xiàn)——裝填/射擊/消耗耐久
???現(xiàn)在就需要在ShotgunItem類中逐步實(shí)現(xiàn)裝填/射擊/消耗等基礎(chǔ)功能了。其中use()定義了玩家手持霰彈槍右鍵時(shí)調(diào)用的方法,決定了是否成功調(diào)用霰彈槍,是否消耗耐久;setCharged()方法用于調(diào)用NBT標(biāo)簽來(lái)定義霰彈槍是否裝填,isCharged()可以判斷霰彈槍是否裝填;shoot()方法定義了開火的過程,包括子彈的傷害,后坐力大小等等;?inventoryTick()方法是只要霰彈槍物品位于玩家的背包中就會(huì)一直調(diào)用,而檢測(cè)霰彈槍的耐久是否耗盡的過程就位于inventoryTick()中。
????????現(xiàn)在霰彈槍裝填、射擊、消耗耐久的功能都有了,如圖5-1。

6. 霰彈槍的高級(jí)功能實(shí)現(xiàn)——多重射擊(難點(diǎn))
????接下來(lái)就要實(shí)現(xiàn)霰彈槍的高級(jí)功能——多重射擊。由于霰彈槍是可以附魔多重射擊的,那么附上多重射擊附魔的霰彈槍就應(yīng)該實(shí)現(xiàn)多重射擊的效果。這里的實(shí)現(xiàn)思路是無(wú)論是否附有多重射擊,都在use()中調(diào)用一次shoot();如果附有多重射擊附魔,就額外賦予物品一小段時(shí)間的冷卻(這里是16tick),并且在inventoryTick()中檢測(cè)冷卻狀態(tài),在冷卻時(shí)間即將結(jié)束前的一個(gè)瞬間(大約是冷卻結(jié)束前最后1個(gè)tick)額外調(diào)用一次shoot(),這樣就實(shí)現(xiàn)了多重射擊,這樣就得到了ShotgunItem的完整代碼,如下:
????????????多重射擊的效果如下圖:

????????????至此霰彈槍物品的功能已經(jīng)全部實(shí)現(xiàn)。
7. 合成表與成就/進(jìn)度
????1. 物品合成表
????????現(xiàn)在要實(shí)現(xiàn)霰彈槍物品和霰彈物品的合成表,相關(guān)教程參考?Crafting recipes [6]。
????????現(xiàn)在制作霰彈槍物品的合成表(resources/data/shotgun/recipes/shotgun.json):
????????????然后是霰彈物品的合成配方(resources/data/shotgun/recipes/grapeshot.json)
????????具體合成效果如下:


????2. 合成配方的解鎖
????????很多時(shí)候玩家在獲得了對(duì)應(yīng)物品的原材料時(shí)就會(huì)解鎖合成該物品的配方,現(xiàn)在就要實(shí)現(xiàn)解鎖霰彈槍物品和霰彈物品合成表的進(jìn)度。
????????先是解鎖霰彈槍配方的文件(resources/data/shotgun/advancements/recipes/combat/shotgun.json)
? ? ? ? 然后是解鎖霰彈物品配方的文件(resources/data/shotgun/advancements/recipes/combat/grapeshot.json)
????3. 成就
????????我們希望當(dāng)玩家獲得霰彈槍時(shí)可以解鎖成就——“Shotgun Battle King”。
????????現(xiàn)在編寫成就文件(resources/data/shotgun/advancements/story/shotgun_battle_king.json)
????????這樣自定義成就就實(shí)現(xiàn)了。
8. 語(yǔ)言文件的編寫
????由于注冊(cè)游戲元素時(shí),游戲元素在游戲中的命名規(guī)則類似與Java包名的命名規(guī)則,并不適合讓用戶使用,所以需要?jiǎng)?chuàng)建一個(gè)語(yǔ)言文件,參考 Name translations [7]。現(xiàn)在一共要制作中文和英文兩個(gè)語(yǔ)言文件。
?????這是中文語(yǔ)言文件(resources/assets/shotgun/lang/zh_cn.json)
????????然后是英文語(yǔ)言文件(resources/assets/shotgun/lang/en_us.json)
????????這樣語(yǔ)言文件編寫就完成了。
9. 模組的導(dǎo)出與使用
???1. 導(dǎo)出
????????運(yùn)行Gradle任務(wù) shotgun build (不同模組會(huì)因不同的ArtifactId而不同,是build就可以),就會(huì)生成模組的JAR文件。

????????生成的JAR文件在build/libs中(這里是build/libs/shotgun-1.0.0.jar,另一個(gè)shotgun-1.0.0-sources.jar可以查看源碼,但是模組沒有版本信息,盡量不用)

? ?2. 使用
????????在使用此模組的時(shí)候需要先下載帶有對(duì)應(yīng)版本Fabric Loader的Minecraft (這里是Mine craft 1.19.2 + Fabric Loader 0.14.14),還需要下載對(duì)應(yīng)的Fabric API(這里是fabric-api-0.73.2+1.19.2)。將模組文件導(dǎo)入對(duì)應(yīng)版本Minecraft的mods文件夾。啟動(dòng)游戲即可。

????????啟動(dòng)游戲后就可以看到模組了。

源碼倉(cāng)庫(kù):https://github.com/Jaffe2718/Fabric-Shotgun-Mod-1.19.2-MDK
參考文獻(xiàn)
[1] FabricMC.如何加速Fabric模組的構(gòu)建-FabricMCCN. (2021, June 28). https://fabricmc.cn/2021/06/28/如何加速Fabric模組的構(gòu)建/
[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