这是适用于Minecraft Java版1.21.4的Fabric模组开发系列教程专栏第九章——创建药水。想要阅读其他内容,请查看或订阅上面的专栏。
药水(Potion) 是能为实体提供效果的消耗品。玩家可以通过饮用药水,为自己添加指定的状态效果。
创建一个药水最简单的方法就是直接在入口点类中调用Registry.register()
方法,在游戏注册表中注册药水。另外,也可以为药水添加配方和自定义的状态效果。
直接在游戏注册表中注册药水
这是创建药水的最简单写法。
在入口点类中调用Registry.register()
方法,其中传递三个参数:
Registry<V> registry
:注册键类型,注册药水固定的写法为Registries.POTION
;Identifier id
:标识符,传递一个Identifier
对象;T entry
:物品类型。此处固定传递一个Potion
对象;
关于原生Register.registry()
方法和Identifier
类的详细用法说明,请参考我的世界Java版1.21.4的Fabric模组开发教程(二)创建物品。
药水类Potion
Potion
类是管理药水效果的核心类,用于创建药水并定义药水的基础效果类型和基础属性。其构造方法创建的对象一般作为注册药水时调用的Registry.register()
方法的第三个参数。
构造方法用于构造Potion
对象;
public Potion(String baseName, StatusEffectInstance... effects) {
this.baseName = baseName;
this.effects = List.of(effects);
}
其中传递两个参数:
String baseName
:指定药水名称;StatusEffectInstance... effects
:指定状态效果对象。可以传递多个对象。
关于状态效果对象的创建相关内容的详细说明,请参考我的世界Java版1.21.4的Fabric模组开发教程(三)创建高级物品:食物。
在入口点类声明静态常量TATER_POTION
,类型为Potion
,调用Registry.register()
方法初始化常量;
public static final Potion TATER_POTION = Registry.register(
Registries.POTION,
Identifier.of(FabricDocsReference.MOD_ID, "tater"),
new Potion("tater",new StatusEffectInstance(StatusEffects.DARKNESS,3600,0)));
第一个参数固定传递Registries.POTION
,代表注册键;第二个参数传递一个Identifier
对象,使用模组Id和路径“tater”作为标识符;第三个参数传递Potion
对象,应当调用Potion
类的构造方法;
构造方法中传递两个参数,第一个参数传递字符串“tater”作为药水名,第二个参数使用StatusEffectInstance
类的构造方法传递一个状态效果对象,参数列表中按顺序分别设置了效果为黑暗StatusEffects.DARKNESS
、持续时间为1200游戏刻(60秒)以及状态效果增幅为0。
关于原生Register.registry()
方法和Identifier
类的详细用法说明,请参考我的世界Java版1.21.4的Fabric模组开发教程(二)创建物品。
现在直接打开游戏,在创造模式物品栏中可以找到已经创建好的药水,普通药水、喷溅型和滞留型药水会同时被创建;
可以看到状态效果为黑暗,持续时间为1分钟。
由于还未添加语言文件中的翻译键值对,所以药水不显示中文名字而是显示标识符。而且,目前的药水只能通过创造模式物品栏或指令获得,在生存模式下无法使用酿造台制作。我们接下来将为药水添加酿造配方和中文名称。
在语言文件中为药水添加翻译键值对
打开语言文件zh-cn.js
,添加键值对数据;
{
...
"item.minecraft.potion.effect.tater": "土豆药水",
"item.minecraft.lingering_potion.effect.tater": "滞留型土豆药水",
"item.minecraft.splash_potion.effect.tater": "喷溅型土豆药水",
...
}
应当添加三条数据,分别为普通药水、喷溅型药水和滞留型药水添加中文翻译,不同类型药水的翻译键是不同的。“tater”是在Potion
类的构造方法中设置的药水名称。
为药水添加酿造配方
现在,我们为自定义药水添加酿造配方,使其可以在生存模式下通过酿造台获得。
Fabric酿造配方注册构造器接口FabricFabricBrewingRecipeRegistryBuilder
FabricFabricBrewingRecipeRegistryBuilder
接口是Fabric API提供的用于为指定药水添加配方并将其注册到游戏注册表中的工具。通过接口中用于处理酿造配方注册事件的BUILD
对象的register()
方法来完成酿造配方的注册。
register()
方法中传递一个FabricFabricBrewingRecipeRegistryBuilder
接口内部的函数式接口BuildCallback
的函数表达式;
@FunctionalInterface
interface BuildCallback {
void build(BrewingRecipeRegistry.Builder builder);
}
方法的参数为BrewingRecipeRegistry.Builder
对象,BrewingRecipeRegistry.Builder
由Minecraft API提供,同样用于构造酿造配方对象。FabricFabricBrewingRecipeRegistryBuilder
接口对此内部类进行了封装,在一定程度上简化了创建药水酿造配方的代码量;
在函数式中需要调用BrewingRecipeRegistry.Builder
对象的registerItemRecipe()
方法来配置酿造配方的详细信息;
public void registerPotionRecipe(RegistryEntry<Potion> input, Item ingredient, RegistryEntry<Potion> output) {
if (input.value().isEnabled(this.enabledFeatures) && ingredient.isEnabled(this.enabledFeatures) && output.value().isEnabled(this.enabledFeatures)) {
this.potionRecipes.add(new BrewingRecipeRegistry.Recipe<>(input, Ingredient.ofItem(ingredient), output));
}
}
其中传递了三个参数:
RegistryEntry<Potion> input
:指定输入药水,即使用哪种药水作为初始药水,传递一个Potions
类中的常量,通常为水瓶Potions.WATER
或粗制的药水Potions.MUNDANE
;Item ingredient
:指定制作材料,即制作药水的原材料,传递一个Items
类常量;RegistryEntry<Potion> output
:指定输出药水,即用以上材料制作出的目标药水,一般调用Registries.POTION.getEntry()
方法返回的药水注册条目对象。传递一个药水对象;
在入口点类中的onInitialize()
方法中,直接调用FabricBrewingRecipeRegistryBuilder.BUILD.register()
方法,然后传递一个函数表达式;
FabricBrewingRecipeRegistryBuilder.BUILD.register(builder -> {
builder.registerPotionRecipe(
Potions.WATER,
Items.POTATO,
Registries.POTION.getEntry(TATER_POTION)
);
});
在函数表达式中通过BrewingRecipeRegistry.Builder
对象调用registerPotionRecipe()
方法设置药水酿造配方的详细信息,其中的参数按照顺序分别设置了输入药水为水瓶Potions.WATER
、原料为土豆Items.POTATO
和输出药水的注册条目对象Registries.POTION.getEntry(TATER_POTION)
,getEntry()
方法中传递药水对象。
启动游戏测试,使用酿造台酿造药水;
使用土豆(游戏称之为马铃薯)作为原料,水瓶作为输入药水;
也可以尝试使用改性剂,将普通药水转变为喷溅型和滞留型药水。
创建自定义药水效果
若不想使用原版游戏中的药水效果,可以为药水创建自定义效果,以此创建更加定制化的药水。在本节,我们为“土豆药水”添加一个在一段时间内给玩家增加经验值的状态效果。
想要创建自定义药水效果,需要创建一个自定义药水效果类,使其继承StatusEffect
类并实现相关方法。根据官方文档中的写法,自定义药水效果类中一般重写其超类的以下方法:
- 构造方法:其中应当直接调用其超类
StatusEffect
中的构造方法,其中传递两个参数;StatusEffectCategory category
:指定药水效果类别,代表状态效果对玩家有益、有害或者是中性,传递StatusEffectCategory
枚举类中的枚举常量;int color
:指定药水的颜色,只能传递16进制RGB颜色码;
canApplyUpdateEffect()
:用于控制药水效果的触发频率。返回值为布尔类型,代表是否在当前游戏刻触达效果,也就是说,此方法在每游戏刻都会执行。方法一般返回true
;applyUpdateEffect()
:用于定义触发药水效果的具体逻辑。返回值为布尔类型,方法应当始终返回true
;
1.首先在com/example/test
目录中创建effect
目录,然后在其中创建TaterEffect.java
。使其继承StatusEffect
类并重写刚刚提到的方法;
public class TaterEffect extends StatusEffect {
public TaterEffect() {}
@Override
public boolean canApplyUpdateEffect(int duration, int amplifier) {
return super.canApplyUpdateEffect(duration, amplifier);
}
@Override
public boolean applyUpdateEffect(ServerWorld world, LivingEntity entity, int amplifier) {
return super.applyUpdateEffect(world, entity, amplifier);
}
}
2.在构造方法中使用super()
调用其父类StatusEffect
中的构造方法;
public TaterEffect() {
super(StatusEffectCategory.BENEFICIAL, 0x9fff41);
}
设置药水类型为StatusEffectCategory.BENEFICIAL
,代表药水对玩家有益;设置药水与生效时的粒子颜色为0x9fff41
(一种与经验球颜色类似的黄绿色);
3.令canApplyUpdateEffect()
方法始终返回true
;
@Override
public boolean canApplyUpdateEffect(int duration, int amplifier) {
return true;
}
4.在applyUpdateEffect()
方法中完成状态效果的具体逻辑,即为玩家增加经验值;
@Override
public boolean applyUpdateEffect(ServerWorld world, LivingEntity entity, int amplifier) {
if (entity instanceof PlayerEntity) {
((PlayerEntity) entity).addExperience(1 << amplifier);
}
return super.applyUpdateEffect(world, entity, amplifier);
}
首先确定实体为玩家,然后将LivingEntity
对象强制转换为PlayerEntity
并调用addExperience()
方法,为玩家增加经验值,此处使用了1 << amplifier
的写法,参数amplifier
代表效果等级,1指的是增加的经验值数量,并不是经验等级。
每游戏刻增加的经验值为1并按照效果等级amplifier
对其进行位运算,即效果等级为II时,每游戏刻增加的经验值为2;效果等级为III时,每游戏刻增加的经验值为4,以此类推。效果等级每增加一级,每游戏刻增加的经验值将会以指数级扩大一倍。
在游戏注册表中注册自定义药水效果
自定义药水效果类已经创建完毕,现在还需要将其在游戏注册表中注册。
这里使用到了Registry.registerReference()
方法,与Registry.register()
方法不同的是,registerReference()
返回的是状态效果注册条目RegistryEntry<StatusEffect>
对象,用于在注册药水时传递自定义药水效果。
1.在入口点类中声明静态常量EXP
,类型为RegistryEntry<StatusEffect>
,使用Registry.registerReference()
方法对其初始化;
public static final RegistryEntry<StatusEffect> EXP = Registry.registerReference(Registries.STATUS_EFFECT,
Identifier.of(FabricDocsReference.MOD_ID, "exp"),
new TaterEffect());
参数列表与Registry.register()
方法完全相同,首先确定注册键为状态效果Registries.STATUS_EFFECT
,标识符为模组Id和路径“exp”组成的Identifier
对象,最后传递自定义药水效果的对象。
2.将注册药水代码中的药水效果从黑暗StatusEffects.DARKNESS
改为EXP
;
public static final Potion TATER_POTION = Registry.register(
Registries.POTION,
Identifier.of(FabricDocsReference.MOD_ID, "tater"),
new Potion("tater",new StatusEffectInstance(EXP,1200,0)));
3.启动游戏,在创造模式物品栏中找到土豆药水;
4.可以看到颜色和中文名称已经设置完毕。然后使用土豆药水,可以看到经验值正在增加;
5.打开物品栏可以看到状态效果,但是没有状态效果的图标和状态效果的中文名称。
为自定义药水效果添加纹理并在语言文件添加翻译键值对
最后,我们为药水状态效果添加纹理图和中文翻译。
1.状态效果的纹理与物品纹理的存放位置不太相同,应当固定地添加到assets/test/textures/mob_effect
目录中,且文件名与注册药水效果时使用的路径相同;
图片大小应为18x18;
2.打开语言文件zh-cn.js
,添加一条键值对;
{
...
"effect.test.exp":"经验增长",
...
}
test
为模组Id,exp
为注册药水效果时使用的路径;
3.再次启动游戏,查看药水效果的图标与中文名称。
本章小结
本章详细阐述了如何在游戏中添加自定义药水,并为其配置酿造配方和自定义药水效果。总体来讲,使用到的新APi较少,难度适中。感谢各位的阅读,有兴趣可以订阅此专栏!