附魔 1.21 Fabric 长线教程计划
本篇教程的视频
本篇教程的源代码
GitHub地址:TutorialMod-Enchantment-1.21
本篇教程目标
- 学会添加附魔
前言
来到1.21之后,附魔也变成数据驱动的了
所以,其实你完全可以按照数据包的写法来写附魔,叠几个附魔在一起
高版本很多东西都改成数据驱动的了,这对数据包开发来说是好事,但对于模组开发那可不太好
不过,如果要实现具体的附魔效果,比如像精准采集、火焰附加、荆棘等这种对工具属性进行改变的,
还是得通过代码的形式来添加
我们这里呢,源代码就不看了,具体思路是编写一个附魔类型,注册它,
再用数据生成来生成附魔相关的一些内容,比如等级、消耗的经验、可以附加在那些东西上
源代码我提一下,Enchantments是原版的注册类,其中有个bootstrap方法,用于数据生成
在net.minecraft.enchantment.effects包下,放了原版那些附魔的类型
一些作用在实体上的附魔效果,比如迅捷、火焰附加等,这些是实现EnchantmentEntityEffect接口的
附魔有些效果并没有单独的类,所以研究起来可能有一点困难,有些是直接在注册时,
用addEffect方法添加的效果,所以具体的源代码我就不提了
本篇教程参考Fabric的官方文档:Enchantments
关于工具的附魔
在1.21之前,如果我们添加自定义的工具,是可以附魔并有实际效果的
但在1.21之后,,因为附魔变成数据驱动的缘故,工具还要给他们写Tag才能在附魔台上附魔并产生效果
这里,我们先来写工具那些Tag的数据生成
我们到ModItemTagsProvider类中加上
1 | getOrCreateTagBuilder(ItemTags.SWORD_ENCHANTABLE) |
剑和其他开采用工具是分开写的
因为剑属于武器,而其他是属于采集方块的工具,有些可以用在剑上的附魔是不能用在开采类工具上的
然后跑个数据生成,再进入游戏进行测试,我们之前写的这些工具就可以在附魔台上附魔了
自定义附魔
那么,回到我们这篇教程的重点来,来添加我们自定义的附魔
创建自定义附魔
这里我们来创建一个TestEnchantmentEffect,按照原版,实现EnchantmentEntityEffect接口
还要注意的是,这个类我们写成record类,与原版一致,当然这里我们没什么参数,就空着好了
1 | public record TestEnchantmentEffect() implements EnchantmentEntityEffect { |
实现EnchantmentEntityEffect要我们重写两个方法,其中一个是CODEC,是编解码器,
这个是高版本都有的一个东西,用于数据传输的
所以这里我们得写一个
1 | public static final MapCodec<TestEnchantmentEffect> CODEC = MapCodec.unit(TestEnchantmentEffect::new); |
因为我们没有一些额外的参数,所以就写一个简单的编解码器就可以
然后在下面的getCodec方法中返回
1 |
|
而后,我们回过头来看apply方法,这个方法用来指定这个附魔会有什么效果
那么,参考Fabric的文档,我们就按照不同等级给它不同的效果
1 | if (level == 1) { |
比较简单,也可以说有点草率
这些代码就是生成闪电LIGHTNING_BOLT
spawn方法有三个参数,第一个是当前世界,第二个是生成闪电的位置,第三个是生成闪电的原因
当然,能做文章的东西很多,这里既然能生成闪电,那么也可以生成其他实体,
另外也可以结合其他的方法,比如药水效果、粒子等内容,制作更为复杂的附魔效果
注册自定义附魔
这里我们再创建一个ModEnchantments类,用于自定义附魔的注册
1 | public class ModEnchantments { |
首先是一个注册方法,用原版的方法再加上我们的命名空间
1 | private static RegistryKey<Enchantment> of(String id) { |
然后注册附魔的注册键
1 | public static final RegistryKey<Enchantment> TEST = of("test"); |
那么接下来就是bootstrap方法,用于数据生成
1 | public static void bootstrap(Registerable<Enchantment> registry) { |
里面的参数直接从原版搬过来就可以
另外还要一个注册方法,用在数据生成里的注册方法
1 | private static void register(Registerable<Enchantment> registry, RegistryKey<Enchantment> key, Enchantment.Builder builder) { |
然后我们就可以在bootstrap方法中注册了
1 | RegistryEntryLookup<Enchantment> registryEntryLookup2 = registry.getRegistryLookup(RegistryKeys.ENCHANTMENT); |
首先是两个RegistryEntryLookup,这个就直接从原版搬过来的
我们重点来看注册方法中的definition方法
它有8个参数
第一个是支持附魔的物品,第二个是主要支持的附魔物品
第三个是权重,即在附魔台出现的概率
第四个是最大等级
第五个是每个等级附魔台所需的最小经验消耗,第六个是每个等级附魔台所需的最大经验消耗,
leveledCost的两个参数,第一个是基础消耗,第二个是每级递增的消耗
第七个是铁砧附魔消耗的经验(附魔书直接附魔)
第八个是生效的槽位,一般为主手生效
上面的这些参数可以在后面生成的数据文件中找到
exclusiveSet方法是指定附魔所在的集合,因为附魔和药水效果不同,有些附魔是不能同时存在,
这里的DAMAGE_EXCLUSIVE_SET是其中一个伤害类附魔集合
addEffect方法是指定附魔造成的效果,第一个参数是附魔的组件类型,可以理解为何时附魔生效,POST_ATTACK是攻击后生效,还有其他很多种,比如DAMAGE,攻击造成伤害时生效等等
第二个参数和第三个参数分别是攻击方和被攻击方,注意顺序,不然按照我们上面的附魔效果,写反了就是我们被雷劈了
最后一个参数就是我们的自定义附魔了,实例化它即可
数据生成类调用
我们到TutorialModDataGenerator中来调用bootstrap方法
1 | registryBuilder.addRegistry(RegistryKeys.ENCHANTMENT, ModEnchantments::bootstrap); |
另外,在ModWorldGen中,也要加上对ENCHANTMENT注册键的动态注册
1 | entries.addAll(registries.getWrapperOrThrow(RegistryKeys.ENCHANTMENT)); |
编解码器注册
实际上,在EnchantmentEntityEffect接口中,还注册了很多编解码器
所以我们在自定义附魔类中写的编解码器也是要进行注册的
这里我们再创建一个ModEnchantmentEffects类,用来注册编解码器
1 | public class ModEnchantmentEffects { |
首先写一个注册方法
1 | private static MapCodec<? extends EnchantmentEntityEffect> register(String name, MapCodec<? extends EnchantmentEntityEffect> codec) { |
这个也是从原版的EnchantmentEntityEffect中搬过来的,再加上我们的命名空间
然后注册我们的编解码器
1 | public static final MapCodec<? extends EnchantmentEntityEffect> TEST = register("test", TestEnchantmentEffect.CODEC); |
另外,万变不离其宗的,一个用于初始化的注册方法
1 | public static void registerModEnchantmentEffects() { |
再在模组主类中调用这个方法
1 | ModEnchantmentEffects.registerModEnchantmentEffects(); |
然后我们就可以跑数据生成,而后,我们就能在data/tutorialmod/enchantment目录下看到我们的附魔的json文件了
1 | { |
这里面的参数就是我们上面在bootstrap方法中写的





