数据生成 26.1 Fabric
本篇教程的视频:
本篇教程源代码
GitHub地址:
注意事项!!!
在开始本篇教程之前,请确保你在生成模板文件的时候,勾选了Data Generation,否则你很有可能无法正常使用数据生成
介绍
数据生成(DataGen)是一个很方便的用于生成各类json文件的工具,可以生成包括模型文件、方块状态文件、语言文件、Tags文件、配方文件等等。
而当你有大量模组内容时,手动编写这些文件会非常繁琐,所以我们可以使用数据生成器来生成这些文件
Minecraft本身有数据生成器,我们在本期教程中也将使用原版的一些方法。
不过,因为要讲的内容比较多,所以源代码这个东西暂时不讲,我会提供相关的路径,可以自行研究
另外,数据生成这个东西,不仅仅可以使用这里教程中的方法,你可以使用其他的json生成器,也可以使用其他的语言来编写(比如Python)
Tags文件数据生成
虽然说我们的教程中,尚未真正讲到Tags,但是我们之前写战利品列表的时候,已经写了相关的Tags文件,所以这里我们也就讲了
Tags文件其实有很多种,我们这里就暂时只写方块和物品的Tags文件,其他的Tags文件可以自行研究(比如画这种,也是有Tag的)
原版的Tag生成器可见TagProvider
方块Tags文件
创建ModBlockTagsProvider类,继承FabricTagProvider.BlockTagProvider,并实现super函数和addTags方法
(是的,我们继承的是Fabric API中的类,因为这样会相对方便一点)
注意,在实现super函数时,参数选择FabricDataOutput和CompletableFuture<HolderLookup.WrapperLookup>这两个,不要选择三个参数的,那第三个参数是给ItemTag的
1 | public class ModBlockTagsProvider extends FabricTagProvider.BlockTagProvider { |
在addTags方法中,我们可以写入我们的Tags文件,这里我们写之前战利品列表中写过的文件
1 |
|
这里我们使用valueLookupBuilder方法来获取一个Tags文件,然后使用add方法来添加我们的方块
我们之前写的Tag是属于BlockTags,所以这里我们使用BlockTags中的相关字段来获取我们的Tags文件
物品Tags文件
创建ModItemTagsProvider类,继承FabricTagProvider.ItemTagProvider,并实现super函数和addTags方法
1 | public class ModItemTagsProvider extends FabricTagProvider.ItemTagProvider { |
我们暂时还没有写物品的Tags文件,所以就简单创建一下即可,后面我们会写的
语言文件数据生成
在之前我们也写了语言文件,这里我们也可以使用数据生成来生成语言文件。不过,原版是没有语言文件生成器的
en_us
创建ModENUSLanProvider类,继承FabricLanguageProvider,并实现super函数和generateTranslations方法
1 | public class ModENUSLanProvider extends FabricLanguageProvider { |
这里的super可以在中间加入一个"en_us",这个是语言文件的文件名,如果你要写其他语言文件,可以在这里修改。
不写的话,默认就是en_us
然后在generateTranslations方法中,我们可以写入我们的语言文件
1 |
|
这里我们使用add方法来添加我们的语言文件,第一个参数是id,第二个参数是翻译的值。add能够接受的参数很多,比如Item、Block、String等等
zh_cn
中文的语言文件也是一样的,只是文件名不一样,我们创建ModZHCNLanProvider类,继承FabricLanguageProvider,并实现super函数和generateTranslations方法。
然后super中的参数改为"zh_cn",这个是中文的文件名
1 | public class ModZHCNLanProvider extends FabricLanguageProvider { |
翻译的东西这里就不再赘述了,和上面的en_us一样
战利品列表数据生成
创建ModBlockLootTablesProvider类,继承FabricBlockLootTableProvider(注意中间有Block),并实现super函数和generate方法
原版生成器可见BlockLootTableGenerator,不过注意,我们这里只讲了方块的战利品列表
1 | public class ModBlockLootTablesProvider extends FabricBlockLootTableProvider { |
在generate方法中,我们可以写入我们的战利品列表
1 |
|
这里我们使用addDrop方法来添加我们的战利品列表,这里单个参数的话是默认掉落其本身
而矿石的掉落是需要额外再使用oreDrops方法来添加的,这个方法接受两个参数,第一个是矿石,第二个是掉落物品
值得一提的是,这个oreDrops方法是只掉落一个物品的,如果你要掉落多个物品,那么我们就要去看铜矿石的掉落列表(青金石、红石亦可),然后自己写了
我们到BlockLootSubProvider中可以看到原版的战利品列表的各种生成方法,我们找到createCopperOreDrops方法
1 | public LootTable.Builder createCopperOreDrops(final Block block) { |
这里我们可以看到,掉落的是Items.RAW_COPPER,掉落的数量是2-5,而且还有FORTUNE附魔(时运)的加成(当然,还有SilkTouch(精准采集)的附魔)。
这个方法我们并不能直接拿来用,因为我们的矿石和掉落物品不一样,所以我们要自己写
其实搬过来,自己改写一下即可
1 | public LootTable.Builder createCopperOreLikeDrops(final Block block, Item item) { |
这里我们只是将Items.RAW_COPPER改为了dropItem,并添加了一个形参Item item,这样我们就可以自定义掉落物品了。
而附魔相关的东西,你可以自行探究
随后,我们就可以使用这个方法来添加我们的掉落物品了
1 | add(ModBlocks.ICE_ETHER_ORE, createCopperOreLikeDrops(ModBlocks.ICE_ETHER_ORE, ModItems.RAW_ICE_ETHER)); |
这样,我们的矿石也就可以掉落多个物品了
模型文件数据生成
创建ModModelsProvider类,继承FabricModelProvider,并实现super函数、generateBlockStateModels和generateItemModels方法
原版模型生成器可见ModelProvider
1 | public class ModModelsProvider extends FabricModelProvider { |
这里我们重写的两个方法,一个是生成方块状态模型,一个是生成物品模型。
生成方块状态文件的同时,对应的物品模型也会生成,所以我们只需要写方块状态文件即可
在generateBlockStateModels方法中,我们可以写入我们的方块状态文件
1 |
|
我们之前写的都是最简单的方块状态文件,也就是立方体的六个面都一样,所以我们使用createTrivialCube方法来注册我们的方块状态文件
这里的createTrivialCube方法接受一个参数,就是我们的方块
在generateItemModels方法中,我们可以写入我们的物品模型文件
1 |
|
这里我们使用generateFlatItem方法来注册我们的物品模型文件,第一个参数是我们的物品,第二个参数是我们的模型的渲染模式,这里我们使用了FLAT_ITEM,这个是一般物品的渲染模式(后面讲到工具时,我们还将采用另外的)
配方文件数据生成
创建ModRecipesProvider类,继承FabricRecipeProvider,并实现super函数和createRecipeProvider、getName方法
原版配方生成器可见RecipeProvider
1 | public class ModRecipesProvider extends FabricRecipeProvider { |
在createRecipeProvider方法中,我们需要先实例化一个RecipeProvider,并重写buildRecipes方法
这是相比较之前1.21.1的教程不一样的地方
1 |
|
合成配方
这里我们先写之前写的1个方块合成9个物品的配方及其逆配方,这个用一个方法即可完成两个配方
1 | nineBlockStorageRecipes(RecipeCategory.MISC, ModItems.ICE_ETHER, RecipeCategory.BUILDING_BLOCKS, ModBlocks.ICE_ETHER_BLOCK); |
这里我们使用nineBlockStorageRecipes方法来添加我们的配方,
这个方法有很多参数,注意不要漏
第一个是是配方的类型,是后者(Block/Item)合成该参数的后一个产物(Item/Block)的分类
第二个是合成的物品(或方块)
第三个是配方的类型,是前者(Item/Block)合成该参数的后一个产物(Block/Item)的分类
第四个是合成的方块(或物品)
有点小绕,不过你可以看看相关的源代码,就能理解了
熔炼配方
我们写之前的熔炉和高炉配方,但在此之前,我们需要一个List类型的字段来存放我们需要燃烧的物品
1 | private static final List<ItemLike> ICE_ETHER_LIST = List.of(ModItems.RAW_ICE_ETHER, ModBlocks.ICE_ETHER_ORE); |
这里我们使用List.of方法来创建一个List,这个方法接受一个或多个参数,这里我们传入了两个参数,一个是我们的物品,一个是原版的物品。
后面你像继续加更多的物品,也是可以的
注意,一个List对应一个熔炼后的产物,也就是说这里的RAW_ICE_ETHER和ICE_ETHER_ORE熔炼后会得到同一个产物
如果你要得到不同的产物,那么你就要创建多个List,然后在generate方法中写入多个熔炼配方
现在我们来写熔炼配方
1 | oreSmelting(ICE_ETHER_LIST, RecipeCategory.MISC, CookingBookCategory.MISC, ModItems.ICE_ETHER, 0.7f, 200, "ice_ether"); |
这里我们使用oreSmelting方法来添加我们的熔炉配方,
而oreBlasting方法来添加我们的高炉配方
这两个方法的参数本质上是一样的,其中它们的第一个参数是我们的List,
第二个参数是配方的分类,第三个参数是配方的产物,
第四个参数是熔炼经验,第五个参数是熔炼时间,
第六个参数是配方所属的组
营火配方
营火配方也很简单
1 | simpleCookingRecipe("campfire_cooking", CampfireCookingRecipe::new, |
这里面的参数也显而易见了吧,就不解释了
举一反三
前面的教程举了另外两个有序合成和无序合成的例子,这里我们也来写一下
1 | shaped(RecipeCategory.MISC, Items.SUGAR, 3) |
这里我们使用shaped来创建有序合成配方
pattern是合成表格,这里只有一行,如果是一个完整的九宫格,那么就是像下面这样
1 | .pattern("###") |
define是定义输入字符为何物,也就是你九宫格中单字符对应的物品,这里我们使用#来代表BEETROOT;
而有多个字符时,也是多写几个define即可
unlockedBy是配方的解锁条件,这个属于隐形进度,也就是不同于“石器时代”、“获得升级”这种有类似于任务书一样可见进度。
配方在游戏中是按照你游戏时触发的条件解锁的,比如你入水了,会解锁和船有关系的配方;获得了原木,则会解锁和木头相关的配方等等。
相关内容会生成在advancement文件夹中
注意!数据生成中是必须写这个解锁条件的
save是输出方法
1 | shapeless(RecipeCategory.BUILDING_BLOCKS, ModBlocks.ICE_ETHER_ORE) |
这里我们使用shapeless来创建无序合成配方
requires是输入,这里我们有两个输入,一个是RAW_ICE_ETHER,一个是STONE,最多也不能超过9个
unlockedBy是条件,这里我们有两个条件,一个是RAW_ICE_ETHER,一个是STONE,这里的条件和有序合成一样,也是必须写的
不过是写一个也行,写多个也行,这里的话,只要有一个条件满足,那么这个配方就会解锁
save是输出方法,不再赘述
注册数据生成器
噼里啪啦写了一堆,一共是7个类(实际上视频教程只有6个,中文的语言文件生成器没写),但现在我们还要注册这些数据生成器,不然它们是不会生效的
我们找到TutorialModDataGenerator,在它的onInitializeDataGenerator注册我们的数据生成器
1 | public class TutorialModDataGenerator implements DataGeneratorEntrypoint { |
当然了,这里我们把fabricDataGenerator.createPack()拿出来了,单独赋给了一个Pack,这样我们就可以使用pack来添加我们的数据生成器了
然后确保我们的所有数据生成器都是public的,不然是无法正确引用的
运行Data Generation
找到右上角Minecraft Client的下拉框,选择Data Generation,然后等待数据生成完成即可
或者你也可以找Gradle中Tasks/fabric中的runDatagen,也是可以的
数据生成的速度很快,基本上几十毫秒就可以搞定,不过如果你的模组内容很多,那么可能会慢一点
这样想想,假设你和我一样有着千百个模型,纯手写这些数据文件,那得写到猴年马月去了
注意
另外,请在正式运行游戏之前,将原本你写的这些数据文件删除,不然会出现重复的数据文件,导致游戏无法正常运行
当然你不放心的话,可以先找个地方放一下,然后运行游戏,看看是否正常
之后的教程除了特殊情况,基本上会采用数据生成来写数据文件,这样不仅方便,而且还能够减少出错的概率











