Minecraft Modding 模组制作-自定义物品

本文详细介绍了如何在Minecraft模组开发中创建自定义物品,包括定义类、注册物品、添加材质、本地化处理以及管理含metadata的物品。还探讨了不同方法来简化材质和名称设置的过程。

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


注意:本文代码只表现个人实现方式及习惯,本文解释只体现个人理解,不一定符合规范(除非声明了这是规范)

自定义物品

一般物品

定义物品所属类

M i n e c r a f t Minecraft Minecraft中所有物品都继承类 I t e m Item Item,我们要添加的物品也不例外,它需要有一个描述自己的类并且这个类也继承 I t e m Item Item
具体操作为在<author>.<modid>.item包下新建一个ModItem.java并输入以下代码

package author.modid.item;

import author.modid.Modid;     //调用Mod的主类
import net.minecraft.item.Item;

public class ModItem extends Item {     //继承Item类
    public ModItem() {     //构造方法
        this.setUnlocalizedName(Modid.MODID + ".modItem");
        //设置本地化时.lang文件中夹在"item."和".name"中间那一段
        //参数可以直接输入'"modid.modItem"'
        this.setRegistryName("mod_item");
        //设置物品ID(只要写ID中"modid:"后面的部分就可以了)
        this.setMaxStackSize(64);
        //设置最大堆叠数
    }
}
//unlocalized name和registry name的命名规范可以从代码中看出

这样一个名为 M o d I t e m ModItem ModItem的类就创建好了

注册物品

创建好类之后还要在游戏启动时注册这种类的物品
具体操作为在<author>.<modid>.item包下新建一个RegistryOfItemHandler.java并输入以下代码

package author.modid.item;

import ...     //可能调用的类会比较多,就不一一列举了

@EventBusSubscriber
public class RegistryOfItemHandler {
    public static final ModItem MOD_ITEM = new ModItem();     //定义一个ModItem类的物品
    @SubscribeEvent     //监听
    public static void onRegistry(Register<Item> event) {     //注册物品
        event.getRegistry().registerAll(     //一次注册多个物品
                MOD_ITEM
        );
    }
}
//MOD_ITEM的命名规范可以从代码中看出

添加材质

在RegistryOfItemHandler.java的 R e g i s t r y O f I t e m H a n d l e r RegistryOfItemHandler RegistryOfItemHandler类中增加一个方法,代码如下

    @SubscribeEvent
    @SideOnly(Side.CLIENT)     //只用于客户端
    public static void onModelRegistry(ModelRegistryEvent event) {     //添加材质
        ModelLoader.setCustomModelResourceLocation(MOD_ITEM, 0,
                new ModelResourceLocation(MOD_ITEM.getRegistryName(), "inventory"));
        //参数分别为注册的物品,物品的metadata,物品为该metadata时的资源位置(其中的"inventory"是针对物品的)
    }

指定了位置之后就要在相应位置放入资源以供调用
先在main.resources.assets.modid下新建一个blockstates文件夹(虽然这是item),在文件夹中新建一个mod_item.json文件,输入以下内容

{
  "forge_marker" : 1,     //forge默认
  "defaults" : {
    "model" : "minecraft:builtin/generated",     //说明外观与原版物品一样
    "textures" : { "layer0" : "modid:items/mod_item" }     //指定图片文件位置为items下的mod_item.png
  },
  "variants" : {
    "inventory" : [{ "transform" : "forge:default-item" }]     //说明显示方式与原版物品一样
  }
}

指定了.png的位置后要在assets.modid新建textures文件夹,再在其下新建items文件夹,并在items文件夹里放入mod_item.png文件(一定要是.png),该文件像素一般为16x16,32x32也可以,这样你的物品就有了纹理

本地化

本地化十分简单,先在main.resources.assets.modid下新建一个lang文件夹,在文件夹中新建一个zh_cn.lang文件(该文件用于中文,en_us.lang用于英文),之后该mod所有的本地化都在此处完成
想必翻过mod的.jar文件的小伙伴已经知道这里要写什么了,对于刚才注册的物品,我们这么本地化:

item.modid.modItem.name=模组物品

含metadata的物品

设置不同材质

原版含metadata的物品有各种石头,各色的羊毛,床,染料等,它们本质都是一个物品在不同metadata时的不同样式
含metadata的物品的类在构造函数中需要加上这些

        setHasSubtypes(true);     //说明该物品含有metadata
        setMaxDamage(0);     //将最大损害值设定为0,避免一些奇妙的“修复物品的设备”产生的刷物品 Bug
        setNoRepair();     //防止被修复。避免一些奇妙的“修复物品的设备”强行修复该物品

为避免每个含metadata的物品的类都要加入上述代码而略显麻烦,添加一个新的类 S u b t y p e I t e m SubtypeItem SubtypeItem(继承 I t e m Item Item)并在它的构造方法中写入上述代码,再让那些含metadata的物品的类继承这个类即可
注册物品的方法和一般物品一样,但是要为每个metadata分别指定资源位置,这有多种方法,以下是我使用的类似一般物品的指定方法
先把“setCustomModelResourceLocation”的第二个参数改为相应的metadata
再把“setCustomModelResourceLocation”的第三个参数改为相应metadata的资源位置
想得到相应metadata的资源位置,可以在 S u b t y p e I t e m SubtypeItem SubtypeItem中添加一个方法,代码如下

    public ResourceLocation getSubtypeName(String meta) {
        return new ResourceLocation(this.getRegistryName().getResourceDomain(),
                this.getRegistryName().getResourcePath() + meta);
        //相应metadata的资源位置的前面部分一致(即ResourceDomain一样),只需要修改.json文件的名称(即ResourcePath),这里是在一般物品的名称后加入了一个名为meta的字符串
        //此时你的编辑器可能会报错“方法调用 'getResourceDomain' 可能产生 'NullPointerException'”,这是因为this.getRegistryName()可能为空
        //为了方便debug时知道何处为空,我们需要将ResourceLocation的第一个实参改为Objects.requireNonNull(this.getRegistryName()).getResourceDomain()
        //因为第一个实参更改了,如果this.getRegistryName()为空,报错时就会具体指出其为空,所以第二个实参就不用更改了
    }

将该方法整合入“setCustomModelResourceLocation”替代掉第三个参数中的"getRegistryName()",代码如下

        ModelLoader.setCustomModelResourceLocation(MOD_ITEM, 1,
                new ModelResourceLocation(MOD_ITEM.getSubtypeName("1"), "inventory"));

你可以设置"getSubtypeName"的参数为任意字符串,比如这里是让MOD_ITEM的metadata为1时的资源位置为一般物品的.json文件同目录下的"mod_item1.json",如果你设置"getSubtypeName"的参数为"_1",那么位置就会是同目录下的"mod_item_1.json"
最后是在同目录新建一个mod_item1.json文件,复制原来的内容,只需要把"{ “layer0” : “modid:items/mod_item” }“改为”{ “layer0” : “modid:items/mod_item1” }“(当然如果你的参数为”_1",上述的"1"就换为"_1")
但是当一个物品有效的metadata很多时就需要在onModelRegistry方法中添加很多条ModelLoader.setCustomModelResourceLocation(,),若此时刚好又有很多含metadata的物品,onModelRegistry方法就会显得臃肿且麻烦
为此可以在 S u b t y p e I t e m SubtypeItem SubtypeItem中添加一个方法,使用循环为每一个metadata分别指定材质位置,代码如下

    public void setSubtypeResourceLocation(int maxData) {     //传入最大的有效metadata,用于循环中止的条件
        for (int i = 0; i <= maxData; ++i) {     //分别为每个metadata指定材质位置
            String meta = Integer.toString(i);     //将整型i变为字符串
            ModelLoader.setCustomModelResourceLocation(this, i,
                    new ModelResourceLocation(this.getSubtypeName(meta), "inventory"));
        }
    }
    //如果你完全理解了这段语句的意思,你会发现metadata为0时会指向mod_item0.json
    //你当然可以对metadata为0时单独指定位置,但是我为了方便就统一用循环了

这样onModelRegistry方法中就只需要写入

        MOD_ITEM.setSubtypeResourceLocation(n);     //n为最大的有效metadata

真是简单了不少呢

设置不同名称

想要为不同metadata设置不同的本地化需要重写 I t e m Item Item的getUnlocalizedName方法(如果你查看forge的源码,会发现 I t e m Item Item有好几个getUnlocalizedName方法,我们重写的是有一个 I t e m S t a c k ItemStack ItemStack实例作参数的)
在你为物品定义的类中加入这样一段代码

    @Override     //注解,用于表示此处为重写方法
    @Nonnull     //不可返回null
    public String getUnlocalizedName(ItemStack stack) {
        switch (stack.getMetadata()) {     //得到这个ItemStack的metadata并判断
            case 0 : return this.getUnlocalizedName() + ".blue";
            case 1 : return this.getUnlocalizedName() + ".green";
            case 2 : return this.getUnlocalizedName() + ".yellow";
            default : return this.getUnlocalizedName() + ".red";     //为不同的metadata返回不同的unlocalizedname
        }
    }

这里的unlocalizedname是在原来的基础上为不同的metadata加了一个不同的".color",这个字符串仍然由你决定,不过要注意在.lang文件中的等号左侧也要进行相应的改变
此时你可能注意到了一个新的类 I t e m S t a c k ItemStack ItemStack,游戏中实际操作的对象是 I t e m S t a c k ItemStack ItemStack而不是 I t e m Item Item,你可以在你为物品定义的类中定义一批 I t e m S t a c k ItemStack ItemStack实例,每个对应一个metadata,再在该物品类的构造方法中为每个 I t e m S t a c k ItemStack ItemStack实例赋值从而绑定matadata
定义实例:

    public ItemStack blue, green, yellow, red;
    //可惜这里不可以使用数组,导致下面不可以使用循环简化代码,我也不知道原因

在构造方法中添加:

        this.blue = new ItemStack(this, 1, 0);
        this.green = new ItemStack(this, 1, 1);
        this.yellow = new ItemStack(this, 1, 2);
        this.red = new ItemStack(this, 1, 3);
        //三个参数分别表示所属物品,数量(我也讲不清楚,但是一般为1),metadata

这种方式无法使用循环,简直是太麻烦了
如果你查看forge的源码,会发现 I t e m D y e ItemDye ItemDye等含有metadata的物品类都重写了一个方法getSubItems,重写这个方法我们可以设置物品的subtype,所以我们也来重写

    @Override
    @ParametersAreNonnullByDefault     //参数可以为null
    public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items) {
        if(this.isInCreativeTab(tab)) {     //检验创造模式物品栏是否一致(我也不知道为什么要判断这个,但是源码有这个东西)
            for(int i = 0; i < 4; ++i) {
                items.add(new ItemStack(this, 1, i));
            }
        }
    }

重写了这个方法后,就不再需要上面的定义实例和修改构造方法了,使用循环也使代码简单了不少
以上两种方式可能有区别,但是我并不知道

参考

Harbinger
土球球的《我的世界Minecraft模组开发指南》
Forge文档
我的世界开发者中文指南


打赏

制作不易,若有帮助,欢迎打赏!
赞赏码

支付宝付款码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒蜩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值