这是适用于Minecraft Java版1.21.4的Fabric模组开发系列教程专栏第十章——更多物品交互行为。想要阅读其他内容,请查看或订阅上面的专栏。
在之前的创建自定义数据组件章节中,我们在自定义物品类中重写了来自Item
类中的use()
方法,实现了在右键使用物品时修改数据组件中数据值的功能。实际上,在Item
类中还有很多方法可以在自定义物品类中重写,以此来为自定义物品添加更多的自定义交互行为。
在本章中,我们将创建一个物品——烫手马铃薯(hot_potato),并使其在背包中存放时,给玩家造成一定伤害。
完成创建物品的常规流程
与创建其他物品相同,我们要为其创建物品类、模型文件、模型描述文件和纹理图,然后再为其在语言文件中添加中文翻译,最后在游戏注册表中注册物品并将其添加到指定物品组。
1.在com/example/test/item
目录中创建自定义方块类HotPotato.java
,使其继承Item
类并重写相关方法;
public class HotPotato extends Item {public HotPotato(Settings settings) {super(settings);}@Overridepublic void appendTooltip(ItemStack stack, TooltipContext context, List<Text> tooltip, TooltipType type) {tooltip.add(Text.translatable("已烫鼠...").formatted(Formatting.RED));}
}
2.在assets/test/models/item
目录中创建模型文件hot_potato.json
;
{"parent": "minecraft:item/generated","textures": {"layer0": "test:item/hot_potato"}
}
3.在assets/test/items
目录中创建模型描述文件hot_potato.json
;
{"model":{"type": "minecraft:model","model": "test:item/hot_potato"}
}
4.在assets/test/textures/item
目录中为物品创建纹理“hot_potato.png”;
5.在语言文件zh-cn.json
中为其添加中文翻译“烫手的马铃薯”;
{..."item.test.hot_potato": "烫手的马铃薯",...
}
6.在入口点类中声明静态常量HOT_POTATO
,类型为Item
,使用ModItems.register()
方法对其初始化;
public static final Item HOT_POTATO = ModItems.register("hot_potato", HotPotato::new, new Item.Settings());
设置路径为“hot_potato”;
7.在入口点类的onInitialize()
方法中,将物品添加到指定物品组中;
@Override
public void onInitialize() {//...ItemGroupEvents.modifyEntriesEvent(CUSTOM_ITEM_GROUP_KEY).register((itemGroup) -> {//...itemGroup.add(HOT_POTATO);//...});//...
}
如果从未了解过自定义物品创建,请先访问我的世界Java版1.21.4的Fabric模组开发教程(二)创建物品。
Item
类中的其他方法
除了自定义数据组件章节中用到的use()
方法外,Item
类中还有很多方法可以被物品类重写,用于为物品添加更多的交互行为,其中包括:
postHit()
:当玩家攻击实体时被调用;postMine()
:当玩家挖掘方块时被调用;inventoryTick()
:当物品在物品栏或背包中时,每游戏刻调用一次;onCraft()
:当物品被合成时调用;useOnBlock()
:当玩家手持物品右键方块时调用(确切的说是对着方块按下使用按键);use()
:当玩家手持物品按下右键时调用(确切的说是按下使用按键);
在本章中我们将使用inventoryTick()
方法,来实现给玩家造成伤害的功能。
编写物品的自定义交互行为逻辑
现在我们为物品添加自定义交互行为,即当玩家背包或物品栏中有“烫手的马铃薯”,每游戏刻给玩家造成0.25颗心的伤害。要实现这个功能,在自定义物品类中重写inventoryTick()
方法最为合适。
1.在自定义物品类中重写inventoryTick()
方法;
@Override
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {}
方法提供了5个参数:
ItemStack stack
:当前物品堆叠实例;World world
:世界对象;Entity entity
:持有该物品的实体,通常为玩家;int slot
:物品所在的库存槽位索引;boolean selected
:该物品是否在玩家的选中快捷栏槽位(即手持状态);
如果不了解部分API,可以参考我的世界Java版1.21.4的Fabric模组开发教程(七)创建自定义魔咒效果其中的部分API描述。
2.调用Entity
对象的damage()
方法,对玩家造成伤害;
damage()
方法是抽象类Entity
中的抽象方法,这个方法被Entity
的子类所重写。在inventoryTick
方法的参数列表中,entity
的类型实际上是Entity
的子类PlayerEntity
,因此,此处调用的damage()
方法的逻辑同样是来自PlayerEntity
类。
public abstract boolean damage(ServerWorld world, DamageSource source, float amount);
其中需要传递的参数有:
ServerWorld world
:指定实体所在的服务器世界对象;DamageSource source
:指定伤害来源,若无法确定伤害来源,可以使用world.getDamageSources().generic()
,代表通用类型;float amount
:指定为实体扣除的生命值。
在inventoryTick()
方法中,调用entity.damage()
方法;
entity.damage((ServerWorld) world,world.getDamageSources().generic(),0.5f);
首先传递世界对象,需要强制转换;然后传递伤害类型为world.getDamageSources().generic()
通用类型,最后设置伤害值为0.5,即0.25颗心。
现在,只要玩家背包中存放了“烫手的马铃薯”,玩家将会以每游戏刻0.25颗心的速度扣除生命值,直至玩家死亡。玩家血量的红心图标不会以剩余1/4或3/4的形式体现,因此,玩家满血状态下第一次受伤时不会在血量的红心图标中体现。
3.需要注意的是,由于游戏服务端和客户端的同步性,inventoryTick()
方法将在客户端和服务端交替执行。而entity.damage()
方法只能在服务端执行,在客户端执行时会因为无法强制转换ServerWorld
对象而发生错误。
因此,我们必须在执行damage()
方法前判断当前世界对象是否为服务器世界ServerWorld
对象,需要调用world.isClient()
方法;
@Override
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {if(!world.isClient()){entity.damage((ServerWorld) world,world.getDamageSources().generic(),0.5f);}
}
当确定当前世界对象的类型不是ClientWorld
,则可以继续执行damage()
。
启动游戏测试
1.启动游戏,打开创造模式物品栏,找到“烫手的土豆”,可以看到贴图、名称和物品工具提示能够正常显示;
2.将“烫手的土豆”加入背包,然后手持“烫手的土豆”,按Q键将其丢弃;
3.将游戏模式切换为生存模式,然后捡起“烫手的土豆”;
可以看到玩家的血量正以每游戏刻0.25颗心减少。
本章小结
本章详细阐述了为自定义物品添加物品交互行为的步骤,使读者可以创建带有自定义交互行为的物品。本文总体难度较小,篇幅较短,但其中仍使用了之前几个章节中的前置知识,想没有障碍的完成学习还需参考专栏的其他章节。感谢各位的阅读,有兴趣可以订阅此专栏!