本篇教程的视频:

本篇教程源代码

GitHub地址:TutorialMod-Biome-1.21

介绍

本篇教程我们将为我们的模组添加生物群系,我们将使用第三方的API来添加生物群系

因为自MC的世界生成大改以后,直接按照原版的方法添加生物群系会相当麻烦,所以我们使用第三方的API来添加生物群系

这里我们使用TerraBlender来添加生物群系,它的官方GitHub地址是TerraBlender

它也有Wiki在那里,可以查看它的那些教程来写,仓库里面也有它的示例代码,可以参考

准备工作

那么常规的,因为是第三方的库,所有我们得加依赖

build.gradle中加入依赖,先加入maven仓库

1
2
3
repositories {
maven { url = 'https://maven.minecraftforge.net/' }
}

然后是具体的依赖

1
2
3
dependencies {
modImplementation 'com.github.glitchfiend:TerraBlender-fabric:1.21-4.0.0.1'
}

版本得改成和你开发的模组版本一致的,具体的版本号到相应的模组站去找

然后重载Gradle,让它下载依赖,运行完之后再跑一下genSources,让它下载源代码

添加生物群系

创建一堆类

Gradle在下载依赖的时候,我们先创建一些类

创建ModBiomes类

这个类用于生物群系的注册

1
2
3
public class ModBiomes {

}

创建ModOverworldRegion类

这个类用于注册Overworld的生物群系

1
2
3
public class ModOverworldRegion {

}

创建ModTerraBlenderAPI类

这个类用于TerraBlenderAPI的使用

1
2
3
public class ModTerraBlenderAPI {

}

创建ModMaterialRules类

这个类用于指定生物群系表面的方块类型

1
2
3
public class ModMaterialRules {

}

其实上面的这一堆东西,TerraBlender的仓库里面都有,可以参考它的示例代码

ModTerraBlenderAPI

那么当Gradle下载完依赖之后,我们就可以开始写代码了

首先是ModTerraBlenderAPI这个类,我们要实现TerraBlenderApi这个接口

1
2
3
4
5
6
7
public class ModTerraBlenderAPI implements TerraBlenderApi {

@Override
public void onTerraBlenderInitialized() {

}
}

这里是要重写onTerraBlenderInitialized这个方法,不过里面的东西我们待会再去写

ModBiomes

我们这里先来写ModBiomes这个类,这个类用于生物群系的注册

我们直接在这个类中定义新的生物群系(源代码这里就不讲解了,可以去看看原版的生物群系是怎么注册的)

1
2
public static final RegistryKey<Biome> DIAMOND_BIOME = RegistryKey.of(RegistryKeys.BIOME,
Identifier.of(TutorialMod.MOD_ID, "diamond_biome"));

那么首先是写一个注册键,这个键是用于注册生物群系的,这里我们写一个钻石大陆的生物群系

接下来我们写globalOverWorldGeneration方法,这个方法是用于编写生物群系的生成规则,一些构造

1
2
3
4
5
6
7
8
public static void globalOverWorldGeneration(GenerationSettings.LookupBackedBuilder builder) {
DefaultBiomeFeatures.addLandCarvers(builder);
DefaultBiomeFeatures.addAmethystGeodes(builder);
DefaultBiomeFeatures.addDungeons(builder);
DefaultBiomeFeatures.addMineables(builder);
DefaultBiomeFeatures.addSprings(builder);
DefaultBiomeFeatures.addFrozenTopLayer(builder);
}

这里我们添加了一些原版的生物群系的构造

addLandCarvers是添加地形的构造

addAmethystGeodes是添加紫晶石洞穴的构造

addDungeons是添加地牢的构造

addMineables是添加矿物的构造

addSprings是添加地下水的构造

addFrozenTopLayer是添加冰层的构造(如果有高山的话)

再接下来我们写diamondBiome方法,这个方法是用于注册钻石大陆的生物群系

1
2
3
4
5
6
private static Biome diamondBiome(Registerable<Biome> context) {
SpawnSettings.Builder spawnSettings = new SpawnSettings.Builder();
spawnSettings.spawn(SpawnGroup.CREATURE, new SpawnSettings.SpawnEntry(ModEntities.TIGER, 2, 3, 5));

...
}

因为东西比较多,这里我们分开来看

首先创建一个SpawnSettings.Builder,这个是用于设置生物群系的生成设置

然后我们设置生物群系的生物生成,这里我们设置生成TigerEntity,生成的权重是2,生成的种群数量最小是3,最大是5

1
2
DefaultBiomeFeatures.addFarmAnimals(spawnSettings);
DefaultBiomeFeatures.addBatsAndMonsters(spawnSettings);

这里我们添加了一些原版的生物的生成

addFarmAnimals是添加农场动物的生成,也就是可以蓄养的动物

addBatsAndMonsters是添加蝙蝠和怪物的生成

1
2
3
4
5
6
7
8
GenerationSettings.LookupBackedBuilder generationSettings =
new GenerationSettings.LookupBackedBuilder(context.getRegistryLookup(RegistryKeys.PLACED_FEATURE),
context.getRegistryLookup(RegistryKeys.CONFIGURED_CARVER));

globalOverWorldGeneration(generationSettings);
DefaultBiomeFeatures.addMossyRocks(generationSettings);
DefaultBiomeFeatures.addDefaultOres(generationSettings);
DefaultBiomeFeatures.addExtraGoldOre(generationSettings);

这里写的是生物群系的一些地物的构造及放置特征,也有我们刚才写的那个方法,这里我们添加了一些原版的地物的构造

addMossyRocks是添加苔石的构造

addDefaultOres是添加默认矿石

addExtraGoldOre是添加额外的金矿石

1
2
3
generationSettings.feature(GenerationStep.Feature.VEGETAL_DECORATION, VegetationPlacedFeatures.TREES_PLAINS);
DefaultBiomeFeatures.addForestFlowers(generationSettings);
DefaultBiomeFeatures.addLargeFerns(generationSettings);

然后是添加植被的构造,特征使用TREES_PLAINS,这个是原版的特征

addForestFlowers是添加繁花森林

addLargeFerns是添加大型蕨类植物

1
2
DefaultBiomeFeatures.addDefaultMushrooms(generationSettings);
DefaultBiomeFeatures.addDefaultVegetation(generationSettings);

addDefaultMushrooms是添加默认的蘑菇

addDefaultVegetation是添加默认的植被

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
return new Biome.Builder()
.precipitation(true)
.downfall(0.5f)
.temperature(0.7f)
.generationSettings(generationSettings.build())
.spawnSettings(spawnSettings.build())
.effects((new BiomeEffects.Builder())
.waterColor(0xe82e3b)
.waterFogColor(0xbf1b26)
.skyColor(0x78a7ff)
.grassColor(0x7c9b45)
.foliageColor(0x7c9b45)
.fogColor(0xc0d8ff)
.build())
.build();

那么最后一个就是这个生物群系的总体特征了,注册这个生物群系,这里我们设置了生物群系的一些特征

precipitation是降水,这里我们设置为true(有降水)

downfall是生物群系的降水量,这里我们设置为0.5f

temperature是生物群系的温度,这里我们设置为0.7f

spawnSettings是生物群系是生物生成设置

generationSettings是生物群系构造的生成设置

effects是生物群系的特效,确切来讲是一堆颜色,不设置的话它会根据前面的那些值自动设置。
waterColor是水的颜色,waterFogColor是水雾的颜色,skyColor是天空的颜色,grassColor是草方块的颜色,
foliageColor是叶子方块的颜色,fogColor是雾的颜色(因为是随机设置的,所以实际整个生物群系有点诡异)

另外,我也在这里说明一下,我这里写是写了一堆,但实际能否生成也得看这个生物群系的实际大小,有些特征可能会被覆盖

再一个,因为在后面的生物群系表面规则中,我们没有设置草方块,而是将原有的草方块代替成了钻石方块,所以这里的植被都不会生成(因为植被只能生成在土类方块上)

生物群系的生成会有很多因素制约它,区块的加载顺序也会影响到生物群系的生成,所以这里只是一个大概的设置

我这里罗列一堆只是教程演示,实际得根据你自己来

最后是用bootstrap方法注册生物群系

1
2
3
public static void bootstrap(Registerable<Biome> context) {
context.register(DIAMOND_BIOME, diamondBiome(context));
}

数据生成

那么同样的,先在数据生成类TutorialModDataGenerator中把这个bootstrap方法注册进去

1
registryBuilder.addRegistry(RegistryKeys.BIOME, ModBiomes::bootstrap);

以及在ModWorldGen进行注册

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

这样我们的生物群系就注册好了

ModOverworldRegion

我们接下来写ModOverworldRegion这个类,这个类用于将我们的生物群系注册添加到世界中,不过这里我们限定为主世界

1
2
3
4
5
6
public class ModOverworldRegion extends Region {
public ModOverworldRegion(Identifier name, int weight) {
super(name, RegionType.OVERWORLD, weight);
}

}

这个类继承Region(记得是TerraBlender的类),这里的构造方法我们也是要改写的,去掉原有的RegionType,直接写RegionType.OVERWORLD,这样就限定了这个生物群系只能在主世界生成

接下来我们重写addBiomes方法

1
2
3
4
5
6
@Override
public void addBiomes(Registry<Biome> registry, Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> mapper) {
this.addModifiedVanillaOverworldBiomes(mapper,modifiedVanillaOverworldBuilder -> {
modifiedVanillaOverworldBuilder.replaceBiome(BiomeKeys.FOREST,ModBiomes.DIAMOND_BIOME);
});
}

通过这个方法,我们可以将原版的生物群系替换成我们自己的生物群系,这里我们将原版的森林生物群系替换成我们的钻石大陆生物群系

ModMaterialRules

随后我们来写ModMaterialRules这个类,这个类用于指定生物群系表面的方块类型

首先指定一些材料规则

1
2
3
4
5
6
7
private static final MaterialRules.MaterialRule DIRT = makeRule(Blocks.DIRT);

private static final MaterialRules.MaterialRule GRASS_BLOCK = makeRule(Blocks.GRASS_BLOCK);

private static final MaterialRules.MaterialRule ICE_ETHER = makeRule(ModBlocks.ICE_ETHER_BLOCK);

private static final MaterialRules.MaterialRule DIAMOND = makeRule(Blocks.DIAMOND_BLOCK);

这里我们指定了一些方块的规则,这里我们指定了DIRTGRASS_BLOCKICE_ETHER_BLOCKDIAMOND

然后我们写makeRules方法

1
2
3
4
5
6
7
8
9
10
11
12
public static MaterialRules.MaterialRule makeRules() {
MaterialRules.MaterialCondition isAtOrAboveWaterLevel = MaterialRules.water(-1,0);
MaterialRules.MaterialRule grassSurface = MaterialRules.sequence(MaterialRules.condition(isAtOrAboveWaterLevel,GRASS_BLOCK),DIRT);

return MaterialRules.sequence(
MaterialRules.sequence(MaterialRules.condition(MaterialRules.biome(ModBiomes.DIAMOND_BIOME),
MaterialRules.condition(MaterialRules.STONE_DEPTH_FLOOR, DIAMOND)),
MaterialRules.condition(MaterialRules.STONE_DEPTH_CEILING, ICE_ETHER)),

MaterialRules.condition(MaterialRules.STONE_DEPTH_FLOOR, grassSurface)
);
}

这里我们写了一堆规则,这里我们指定了生物群系表面的方块类型

isAtOrAboveWaterLevel是指定在水面以上的方块

grassSurface是指定草地表面的方块

在返回值语句中,我们引入生物群系,然后设置生物群系的表面方块,这里我们将DIAMOND_BIOME的表面方块设置为DIAMONDICE_ETHER,然后将GRASS_BLOCKDIRT设置为grassSurface

ModTerraBlenderAPI

那么最后就是ModTerraBlenderAPI这个类了,我们在onTerraBlenderInitialized方法中注册我们的生物群系和生物群系表面规则

1
2
3
4
5
@Override
public void onTerraBlenderInitialized() {
Regions.register(new ModOverworldRegion(Identifier.of(TutorialMod.MOD_ID, "overworld"), 4));
SurfaceRuleManager.addSurfaceRules(SurfaceRuleManager.RuleCategory.OVERWORLD, TutorialMod.MOD_ID, ModMaterialRules.makeRules());
}

通过Regions.register方法注册我们的生物群系,这里我们注册了位于主世界的生物群系,权重为4

然后通过SurfaceRuleManager.addSurfaceRules方法注册我们的生物群系表面规则,这里我们注册了我们的生物群系表面规则

在此之后,我们就可以跑数据生成了,生成我们的生物群系文件,之后就可以启动游戏

当然这里是得新建存档了,因为这个生物群系是新加入的