本篇教程的视频:

本篇教程源代码

GitHub地址:TutorialMod-TreeGen-1.21

介绍

本篇教程将讲解如何让树在世界中生成,不过前一个视频教程把要讲的内容讲了一点,视频教程可以两个一起看

这里还是从源代码一点点讲起

查看源代码

前面我们看的是TreeConfiguredFeatures类,这个类是写了树的构造特征的,现在我们就得来看TreePlacedFeatures类了

这个类是它的放置特征,也就是在世界中放置树的特征

1
public static final RegistryKey<PlacedFeature> OAK_CHECKED = PlacedFeatures.of("oak_checked");

它这里也是一个注册键,这个注册键是PlacedFeature的,这个类是放置特征的类

1
2
3
public static RegistryKey<PlacedFeature> of(String id) {
return RegistryKey.of(RegistryKeys.PLACED_FEATURE, Identifier.ofVanilla(id));
}

同样的,这个注册方法我们也要自己更改一下命名空间,不能直接调用

然后下面的就是bootstrap了,不过说实话,写的有点长

1
2
3
4
5
RegistryEntryLookup<ConfiguredFeature<?, ?>> registryEntryLookup = featureRegisterable.getRegistryLookup(RegistryKeys.CONFIGURED_FEATURE);

RegistryEntry<ConfiguredFeature<?, ?>> registryEntry3 = registryEntryLookup.getOrThrow(TreeConfiguredFeatures.OAK);

PlacedFeatures.register(featureRegisterable, OAK_CHECKED, registryEntry3, PlacedFeatures.wouldSurvive(Blocks.OAK_SAPLING));

我们可以看到这里的registryEntry3用到了TreeConfiguredFeatures.OAK,这个OAK是前面讲到过的注册树的键

然后下面的PlacedFeatures.register方法,我们可以看到,这个方法是注册放置特征的,调用OAK_CHECKED,也就是上面的注册键,然后传入registryEntry3,也就是树的构造特征,然后传入PlacedFeatures.wouldSurvive(Blocks.OAK_SAPLING),这个方法是判断树苗是否能存活的方法

不过,在我们实际编写中,是会用另外的方法来写最后一个放置特征的,不然你还得写其他的类(可以由OAK_CHECKED进一步挖掘源代码,在VegetationConfiguredFeatures中,也就是植被的构造特征类中)

树的世界生成

创建ModPlacedFeatures类

同样的,我们先创建一个ModPlacedFeatures类,这个类用来注册我们的放置特征,这里我也给它放在同一个类中,你也可以像原版那样分开来

先改写注册方法

1
2
3
public static RegistryKey<PlacedFeature> of(String id) {
return RegistryKey.of(RegistryKeys.PLACED_FEATURE, Identifier.of(TutorialMod.MOD_ID, id));
}

然后我们注册放置特征的注册键

1
public static final RegistryKey<PlacedFeature> ICE_ETHER_TREE_PLACED_KEY = of("ice_ether_tree_placed");

再接下来就是bootstrap方法了

1
2
3
4
5
6
7
8
9
public static void bootstrap(Registerable<PlacedFeature> featureRegisterable) {
RegistryEntryLookup<ConfiguredFeature<?, ?>> registryEntryLookup = featureRegisterable.getRegistryLookup(RegistryKeys.CONFIGURED_FEATURE);

PlacedFeatures.register(featureRegisterable, ICE_ETHER_TREE_PLACED_KEY,
registryEntryLookup.getOrThrow(ModConfiguredFeatures.ICE_ETHER_TREE_KEY),
VegetationPlacedFeatures.treeModifiersWithWouldSurvive(
PlacedFeatures.createCountExtraModifier(2, 0.1f, 2),
ModBlocks.ICE_ETHER_SAPLING));
}

和原版一样,我们先把registryEntryLookup拿出来

而后我们调用PlacedFeatures.register方法,传入我们的注册键,传入我们的树的构造特征

然后传入VegetationPlacedFeatures.treeModifiersWithWouldSurvive方法,这个方法是用来判断树苗是否能存活的

再传入PlacedFeatures.createCountExtraModifier方法,这个方法是用来生成每个区块树的数量

参数分别是基础数量 count额外概率 extraChance额外数量 extraCount,按照这里的参数设置,每个区块会生成2棵树,另外有10%的概率再额外生成2棵

最后一个参数是ModBlocks.ICE_ETHER_SAPLING,这个是树苗的方块

利用这里的方法,我们就不用多写一堆东西了(不过,虽然说它能跑,但不排除这种简化存在某些的问题,只是现在尚未发现罢了,要保险一点的话还是像原版那样勤勤恳恳地写一堆)

放置参数比构造参数要简单一点,这个类就这样好了

创建ModTreeGeneration类

我们再来创建一个ModTreeGeneration类,这个类是用于决定树在哪些生物群系中生成的

1
2
3
4
5
6
public class ModTreeGeneration {
public static void registerTrees() {
BiomeModifications.addFeature(BiomeSelectors.includeByKey(BiomeKeys.PLAINS, BiomeKeys.FOREST),
GenerationStep.Feature.VEGETAL_DECORATION, ModPlacedFeatures.ICE_ETHER_TREE_PLACED_KEY);
}
}

这里的registerTrees是初始化方法,待会要调用一下

然后使用BiomeModifications.addFeature方法,用BiomeSelectors.includeByKey来选择生物群系,这里选择了PLAINFOREST生物群系

然后使用GenerationStep.Feature.VEGETAL_DECORATION来选择生成的阶段,这里选择了植被装饰

最后传入我们的放置特征的注册键

这个BiomeModifications.addFeature也是Fabric API中的一部分,方便我们在生物群系中添加特征

创建ModWorldGeneration类

单独给世界生成开一个初始化类,当然你也可以直接在主类中初始化,本质是一样的

1
2
3
4
5
public class ModWorldGeneration {
public static void registerWorldGenerations() {
ModTreeGeneration.registerTrees();
}
}

并在主类中调用这个方法

1
ModWorldGeneration.registerWorldGenerations();

数据文件

另外就是关于数据生成的东西了

数据生成类

我们在数据生成类中的buildRegistry方法中调用我们写的bootstrap方法

1
registryBuilder.addRegistry(RegistryKeys.PLACED_FEATURE, ModPlacedFeatures::bootstrap);

ModWorldGen类

ModWorldGen类中,我们动态注册放置特征,和前面的构造特征一样

1
entries.addAll(registries.getWrapperOrThrow(RegistryKeys.PLACED_FEATURE));

在此之后,我们就可以跑数据生成了,它会在worldgen的放置特征文件夹中生成一个json文件,随后我们就可以进入游戏测试了

不过,虽然在旧存档未加载的区块可能会生成我们的树,保险起见可以直接新建存档,并TP到相应的生物群系即可