本篇教程的视频
本篇教程的源代码
介绍
前面我们写了盔甲,如果你进一步挖掘源代码,其实可以发现他们不仅仅是继承了Item
,还实现了Equipment
这个接口
这个接口实现的便是让我们的物品可以被穿戴,而且还可以被穿戴在不同的位置上
那么本期我们就用这个接口来实现一个头饰,让玩家可以穿戴在头部,其他部位的也可以自己去实现
本篇教程使用了BlockBench
制作头饰的模型,可以自己搞一个,也可以直接使用我提供的模型文件
查看源代码
在写我们自己的头饰之前,我们先看看ArmorItem
和Equipment
的源代码
Equipment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public interface Equipment { EquipmentSlot getSlotType();
default RegistryEntry<SoundEvent> getEquipSound() { ... }
default TypedActionResult<ItemStack> equipAndSwap(Item item, World world, PlayerEntity user, Hand hand) { ItemStack itemStack = user.getStackInHand(hand); EquipmentSlot equipmentSlot = user.getPreferredEquipmentSlot(itemStack); if (!user.canUseSlot(equipmentSlot)) { return TypedActionResult.pass(itemStack); } else { ItemStack itemStack2 = user.getEquippedStack(equipmentSlot); if ((!EnchantmentHelper.hasAnyEnchantmentsWith(itemStack2, EnchantmentEffectComponentTypes.PREVENT_ARMOR_CHANGE) || user.isCreative()) && !ItemStack.areEqual(itemStack, itemStack2)) { if (!world.isClient()) { user.incrementStat(Stats.USED.getOrCreateStat(item)); }
ItemStack itemStack3 = itemStack2.isEmpty() ? itemStack : itemStack2.copyAndEmpty(); ItemStack itemStack4 = user.isCreative() ? itemStack.copy() : itemStack.copyAndEmpty(); user.equipStack(equipmentSlot, itemStack4); return TypedActionResult.success(itemStack3, world.isClient()); } else { return TypedActionResult.fail(itemStack); } } }
@Nullable static Equipment fromStack(ItemStack stack) { ... } }
|
Equipment
接口不算复杂,里面有些方法看它的方法名就知道是什么意思了
上面的方法分别是穿戴声音,穿戴并交换物品,从物品栈中获取Equipment
实例
其中的equipAndSwap
方法是用来穿戴物品的,我们可以看到,它会判断玩家是否可以穿戴这个物品,然后再穿戴。里面有一个附魔的判断,可以看出来是什么吗?绑定诅咒。
也就是说,如果玩家一旦穿上了这个带绑定诅咒的盔甲,就没办法换下来了,除非死亡
这个方法也在ArmorItem
中实现了,我们可以看看ArmorItem
的源代码
ArmorItem
1 2 3 4
| @Override public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) { return this.equipAndSwap(this, world, user, hand); }
|
这个重写了Item
的use
方法,实现了Equipment
接口的equipAndSwap
方法,也就是说,当我们右键点击这个物品时,会穿戴这个物品,或者将原来穿着的装备换下来(前提是没有绑定诅咒)
ArmorItem
的构造函数其实我们不用去看它,里面主要是获取它的一些属性
1 2 3
| public ArmorItem.Type getType() { return this.type; }
|
这个方法是用来获取ArmorItem
的类型的,对应有不同的装备槽,结合接口的方法
1 2 3 4
| @Override public EquipmentSlot getSlotType() { return this.type.getEquipmentSlot(); }
|
这个方法是接口里的,用来获取装备槽的,结合上面的方法,我们就可以知道这个物品是穿在哪个装备槽上的
Type
是内置在ArmorItem
中的一个枚举类,里面有一些属性,比如装备槽、基础耐久、名字等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static enum Type implements StringIdentifiable { HELMET(EquipmentSlot.HEAD, 11, "helmet"), CHESTPLATE(EquipmentSlot.CHEST, 16, "chestplate"), LEGGINGS(EquipmentSlot.LEGS, 15, "leggings"), BOOTS(EquipmentSlot.FEET, 13, "boots"), BODY(EquipmentSlot.BODY, 16, "body");
... private final EquipmentSlot equipmentSlot; private final String name; private final int baseMaxDamage;
private Type(final EquipmentSlot equipmentSlot, final int baseMaxDamage, final String name) { this.equipmentSlot = equipmentSlot; this.name = name; this.baseMaxDamage = baseMaxDamage; }
... }
|
所以Equipment
这个接口主要是让物品可以放在装备槽上
其他的倒是差不多,而本次我们写的头饰,并不会涉及材料、附魔能力、盔甲韧性和击退抗性等等东西,很多东西都可以省略
创建HatItem类
那我们就开始写吧,创建一个HatItem
类,这是个帽子,我们继承Item
,并实现Equipment
接口
1 2 3 4 5 6 7 8 9 10 11
| public class HatItem extends Item implements Equipment { public HatItem(Type type, Settings settings) { super(settings); }
@Override public EquipmentSlot getSlotType() { return null; } }
|
记得实现super函数和getSlotType
方法,这个方法返回的是装备槽,我们这里先返回null
,后面再改
创建内置Type枚举类
仿照ArmorItem
的Type
枚举类,我们也创建一个Type
枚举类,里面有装备槽、基础耐久、名字等属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public static enum Type implements StringIdentifiable { HAT(EquipmentSlot.HEAD, 11, "hat");
public static final Codec<ArmorItem.Type> CODEC = StringIdentifiable.createBasicCodec(ArmorItem.Type::values); private final EquipmentSlot equipmentSlot; private final String name; private final int baseMaxDamage;
private Type(final EquipmentSlot equipmentSlot, final int baseMaxDamage, final String name) { this.equipmentSlot = equipmentSlot; this.name = name; this.baseMaxDamage = baseMaxDamage; }
public int getMaxDamage(int multiplier) { return this.baseMaxDamage * multiplier; }
public EquipmentSlot getEquipmentSlot() { return this.equipmentSlot; }
public String getName() { return this.name; }
public boolean isTrimmable() { return false; }
@Override public String asString() { return this.name; } }
|
这个枚举类里面有一个CODEC
,这个是用来序列化的,用于数据传输的
这里面我们把isTrimmable()
设置为false
,就不让它是可锻造的,其他的就和源代码里面一样
设置Type属性
在HatItem
类中设置Type
属性
1 2 3 4 5
| protected final Type type; public HatItem(Type type, Settings settings) { super(settings); this.type = type; }
|
这里我们把Type
属性设置为final
,并在构造函数中初始化
设置getSlotType方法
我们的Type
写好了,我们就可以在getSlotType
方法中返回Type
的装备槽
1 2 3 4
| @Override public EquipmentSlot getSlotType() { return this.type.getEquipmentSlot(); }
|
这样我们的帽子就可以穿在头部了
重写use方法
我们还可以重写use
方法,让玩家右键点击时穿戴这个帽子
1 2 3 4
| @Override public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) { return this.equipAndSwap(this, world, user, hand); }
|
这里就是调用Equipment
接口的equipAndSwap
方法,穿戴这个帽子
注册物品
我们还得注册这个物品,和之前的物品注册方法一样
1 2
| public static final Item HAT = registerItems("hat", new HatItem(HatItem.Type.HAT, new Item.Settings().maxDamage(HatItem.Type.HAT.getMaxDamage(5))));
|
因为我们仿写的类和盔甲类差不多,所以这里的注册方法也差不多,这里的耐久值就设置为5
,还是一个乘数
写入物品栏
记得把我们的帽子写入物品栏
1
| entries.add(ModItems.HAT);
|
数据文件
语言文件
1
| translationBuilder.add(ModItems.HAT, "Hat");
|
模型文件
模型文件我们这里不用数据生成,因为我们用BlockBench
做了这个模型文件
不过,在BlockBench
做好之后,记得在显示模式
里面进行调节,尤其是头部
这一项。因为我们的帽子是穿在头部的,所以要调节好位置
然后如果你想制作其他部位的饰品,在调节这个显示模式的时候可能不太好搞,可能得参考原版的文件
最后导出模型文件,放到对应文件夹下
材质文件也不要忘记
随后,我们进入游戏,穿戴这个帽子,看看效果