节外生枝 - Minecraft Fabric Mod 开发:第 2 期 添加物品

本文介绍了如何在Minecraft中通过FabricAPI实现Mod的开发,包括创建Mod入口点(onInitializeClient),理解注册表机制和名称空间,以及实际操作中添加物品的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上一期,我们已经准备好了开发环境。这一期我们将带领大家进行代码实现。本期我们将尝试在Minecraft中添加物品。

实现

Mod 入口点

什么是Mod入口点呢。其实这是笔者形象表述的,而非一个专业术语。读者不妨自己设想一下,既然给出一个Mod,自然是要按照代码运行。那么问题来了,该从哪里开始运行呢?这样的一个起始点,我们就称之为Mod入口点。

学过编程的同学都知道,程序的运行需要从一个入口点开始,在面向过程的语言中,这个点通常是入口函数;而对于面向对象的语言,这个点则通常是一个对象的某个方法。开发Minecraft Mod,我们使用的是Java语言,它是一款面向对象的语言。因此,我们需要为Mod的运行提供一个入口点方法。

如上文所述,入口点方法是存在于某个对象中的,我们不妨在项目试图里打开src/main/java/org/tutorial/tutorialmod

我们可以发现,默认情况下,这个路径对应的有一个源代码和一个包含了源代码的文件夹。原因在于,我们在上一期创建Mod时,在Environment一栏选择的时Both选项,即客户端和服务端。Mod运行分为客户端运行和服务端运行。对于普通玩家而言,游玩的都是客户端,而对于服务器而言,则运行服务端。具体原理这里不过多赘述。这里只以客户端作为讲解。

我们打开TutorialModClient文件,可以发现文件中已经为我们定义好了TutorialModClient类。这就是我们这个Mod(项目)的对象。接下来就是提供入口方法了。由于Fabric有一套自己的API,我们只需使用Fabric提供的API即可。因此,我们需要在文件开头导入Fabric API

import net.fabricmc.api.ClientModInitializer;

这里导入的是Fabric为我们准备好的Mod初始化的模板。我们需要使Mod对象遵循这个模板进行初始化,只需如下即可:

public class TutorialModClient implements ClientModInitializer {
	@Override
	public void onInitializeClient() {
	}
}

值得注意的是,@Override是一种注解,表示以下方法重写了父类的方法(即我们导入的模板中要求给出的方法)。这个方法原型是固定的,必须是void型,且方法名必须是onInitializeClient
【服务端对应的模板是ModInitializer,其入口方法为onInitialize

接下来我们可以进行编译运行。按下Ctrl+F9或手动构建。构建成功后,我们选择Minecraft Client任务运行。为了方便观察Mod是否加载以及便于后期对Mod信息修改。这里推荐大家安装Mod Menu,选择1.16.5 Fabric版本。安装后运行界面如下。

到这里,Mod的入口点问题已经解决了!


注册表机制

Minecraft中采用注册表机制(与Windows系统注册表不完全类似)。注册表分为很多种。具体种类参见如下链接。
tutorial:registry_types [Fabric Wiki]

名称空间

使用give命令给予玩家物品时,还记得我们是如何表示物品的吗?比方说,铁锭我们的表述为’minecraft:iron_ingot’。冒号前我们称之为名称空间namespace

那么名称空间是用来干什么的呢?简单来说就是为了兼容的。有时我们发现,一个模组加了一个物品比如说钢片,另一个模组也有这个物品但却不是同一个,总不能一个叫钢片的物品被两个模组注册吧。所以就有了名称空间的说法,每个模组都有一个自己的名称空间,注册的物品也都属于这个空间,就不会和其他模组的同名物品冲突(尽管玩家看到相同的连个物品依旧困扰无奈)。

Coding…

知道了以上几点知识,我们大致有了开发的思路。接下来让我们用代码实现吧!

首先,让我们来实现Mod入口点。Fabric是一个成熟的框架,它为我们准备好了一切,我们只需要照着模子一点点雕刻即可。创建一个类,并指定接口。

public class TutorialModClient implements ClientModInitializer {
}

接下来,我们需要具体实现onInitializeClient方法。注意方法名前要加上@Override表示对接口的实现。

@Override
public void onInitializeClient() {
}

构建项目,同时选择执行Minecraft Client任务,有的同学的“Build”窗口中输出很多下载信息,那是项目正在补全所需文件及环境。过一会,就会弹出一个Minecraft游戏窗口。为了方便以后的开发,这里推荐安装一个Mod——Mod Menu

进入游戏后在主菜单选择模组即可看到我们自己写的Mod。

注册物品
我们在TutorialModClient下创建一个类TutorialItems用于存放我们需要注册的物品并且提供一个注册的方法。代码如下:

public class TutorialItems {
	public static final Item BRONZE_INGOT = new Item(new Item.Settings().group(ItemGroup.MISC).maxCount(64));
	public static void RegisterItems()
	{
		Registry.register(Registry.ITEM,
				new Identifier("tutorial", "bronze_ingot"),
				BRONZE_INGOT);
	}
}

这里我们创建了一个物品Item用来存放物品信息,该变量称之为BRONZE_INGOT青铜锭。按照Java的初始化方法,我们调用构造方法Item(),其参数是类型是Item.Settings,接下里我们需要弄明白为什么会这么写,以及各个方法设置的属性如何。

这里为大家扒了Item.Settings的代码。方法的实现已经略去,注释即为解释。

public class Item
implements ItemConvertible {
    public static class Settings {
        private int maxCount = 64; //物品最大堆叠数量
        private int maxDamage; //物品最大耐久值
        private Item recipeRemainder; //合成剩余物,合成物品后返还给玩家的物品
        private ItemGroup group; //物品所在的组
        private Rarity rarity = Rarity.COMMON; //稀有度
        private FoodComponent foodComponent; //食品属性,能让物品变得可食用
        private boolean fireproof; //是否防火、防岩浆,能否被其破坏

        //以下分别是修改上述属性的方法
        public Settings food(FoodComponent foodComponent) {...}
        public Settings maxCount(int maxCount) {...}
        public Settings maxDamageIfAbsent(int maxDamage) {...}
        public Settings maxDamage(int maxDamage) {...}
        public Settings recipeRemainder(Item recipeRemainder) {...}
        public Settings group(ItemGroup group) {...}
        public Settings rarity(Rarity rarity) {...}
        public Settings fireproof() {...}
    }
}

观察可以发现,其中所有方法返回的对象都是Settings,同时,源文件里所有方法的返回也都是this,也就说,方法执行完以后,把自己所在的对象给出去,是不是就相当于一种传递的过程?因此,设置Item属性时,创建Item的子类Settings,接下来直接调用方法进行属性设置即可。那么同时呢,介于这种方法的传递性质,实际上调用的顺序是没有硬性要求的。

举些例子,方便大家理解(只保留关键代码):

//创建一个物品,使得其最大堆叠为16、防火、属于杂项
new Item(new Item.Settings().maxCount(16).fireproof().group(ItemGroup.MISC)
//创建一个物品,使得其最大耐久200、史诗级稀有度、属于武器
new Item(new Item.Settings().rarity(Rarity.EPIC).group(ItemGroup.COMBAT)

接下来就是将物品真正地加入MC世界中,我们可以看到使用了Registry类中的register方法。Registry表示的就是注册表,register表示注册。我们来看看注册所需要的参数。

这里给出了方法原型:

public static <V, T extends V> T register(net.minecraft.util.registry.Registry<V> registry, net.minecraft.util.Identifier id, T entry);

参数的类型很长,没关系,我们通俗解释即可。第一个参数registry,表明我们要注册的类型,具体类型参考上文已给出的关于注册表类型的链接。这里我们要注册一个物品Item,所以传入Registry.ITEM;接下来是id,这相当于为物品分配一个身份(ID),那由于这个物品我们是自己新加的,没有自己的身份,所以,我们新建一个——new Identifier("tutorial", "bronze_ingot")。前者字符串是确定这个物品所在的命名空间,后者是物品的id;最后一个参数entry,其实这就是我们要注册的对象的属性,想到属性,我们是不是在前面已经创建了一个BRONZE_ITEM,传入即可。

物品已经注册完毕,但细心地读者想必想到了一个问题,我们并没有给出物品的材质。
因此,我们现在来解决这个问题。
在项目resources目录下创建如下目录及文件。

其中,bronze_ingot.png就是青铜锭的材质,icon.pngMod本身的图标。bronze_ingot.json是来指定物品材质的。
首先展示一下这两张图片。

接下来,我们给出bronze_ingot.json的内容。

{
	"parent": "item/generated",
	"textures": {
		"layer0": "tutorial:item/bronze_ingot"
	}
}

编译并点击启动Minecraft Client,随便进入一个存档,我们可以找到,在杂项目录下,出现了我们自己加入的青铜锭。

至此,我们成功添加了一个物品。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值