添加生物群系
这次我们使用上次添加的terrablender lib来给主世界添加生物群系,为什么不直接添加呢,因为我没找到方法,而且添加也比较麻烦,可能需要mixin,所以我们直接使用这个lib来给主世界添加生物群系,不过如果你是给自己的维度添加生物群系倒不是怎么麻烦了。好了然我们开始把。
对于我的世界中的生物群系同样是通过json来配置的,你也可以仅写json完成,不过既然我们能写代码还是通过代码来完成把。
首先生物群系是配置了一系列的feature,就是我们之前的写的矿物生成的那样的feature,不仅仅是矿物还包含了地下洞穴,各种矿物,以及该生物群系的中的怪物生成。这两个分别对应了,biomegenerationsettings和MobSpawnSettings这两个类,这两个类就是配置生成的内容和怪物的生成,并且这两个都是通过builder来构建的。
然后我们通过Biome.BiomeBuilder()这个构建大的方法,来获得对应的Biome实例,其中的BiomeBuilder指定我们的biomegenerationsettings和MobSpawnSettings这两个内容,最后和之前一样通过数据生成得到方式获得json文件。
不过这里要说大的一点就是我们添加了各种的Placeddfeature,你之前也写过placedfeature所以你应该明白这并不会改变生物群系的一些地表的方块什么的。这更像是添加新的内容,例如就是沙漠的地表是沙子,而平原的地表是草方块,这并不是通过placedfeature配置的。这也是我们使用terrablender的理由,据我找到的内容来看,neoforge没有提供直接往主世界中塞生物群系的方法,而对于地表方块的生成的内容则是放在了一个package net.minecraft.data.worldgen包下的SurfaceRuleData类中,你想添加只能修改这个类。
而对于生成你添加的生物群系则是在维度里面,你也需要通过mixin的方法去添加,不过我们使用了terrablender所以就不需要考虑这种事情了。
大概了解之后我们来看代码把。首先是添加生物群系得到代码。
// 原版的生物群系的配置位于OverworldBiomes类下。
public class ExampleOverworldBiomes{
// 一个辅助的函数用于根据温度判断段生物群系的天空的颜色
protected static int calculateSkyColor(float pTemperature) {
float $$1 = pTemperature / 3.0F;
$$1 = Mth.clamp($$1, -1.0F, 1.0F);
return Mth.hsvToRgb(0.62222224F - $$1 * 0.05F, 0.5F + $$1 * 0.1F, 1.0F);
}
// 一个构造方法 用于返回一个biome实例
private static Biome ExampleOverworldBiomes(
boolean pHasPercipitation,// 是否有降水
float pTemperature,// 温度
float pDownfall, // 降水量
MobSpawnSettings.Builder pMobSpawnSettings,// 生物群系生物生成设置
BiomeGenerationSettings.Builder pGenerationSettings,// 生物群系生成设置
@Nullable Music pBackgroundMusic // 生物群系背景音乐
) {
return ExampleOverworldBiomes(pHasPercipitation, pTemperature, pDownfall, 4159204, 329011, null, null, pMobSpawnSettings, pGenerationSettings, pBackgroundMusic);
}
// 另一个构造方法 同样返回bioome 能配置的更多
private static Biome ExampleOverworldBiomes(
boolean pHasPrecipitation, /
float pTemperature,// 温度
float pDownfall,
int pWaterColor, // 水的颜色
int pWaterFogColor, // 水的雾颜色
@Nullable Integer pGrassColorOverride, // 草方块颜色
@Nullable Integer pFoliageColorOverride, // 树叶颜色
MobSpawnSettings.Builder pMobSpawnSettings,
BiomeGenerationSettings.Builder pGenerationSettings,
@Nullable Music pBackgroundMusic
) {
BiomeSpecialEffects.Builder biomespecialeffects$builder = new BiomeSpecialEffects.Builder()
.waterColor(pWaterColor)
.waterFogColor(pWaterFogColor)
.fogColor(12638463)
.skyColor(calculateSkyColor(pTemperature))
.ambientMoodSound(AmbientMoodSettings.LEGACY_CAVE_SETTINGS)
.backgroundMusic(pBackgroundMusic);
if (pGrassColorOverride != null) {
biomespecialeffects$builder.grassColorOverride(pGrassColorOverride);
}
if (pFoliageColorOverride != null) {
biomespecialeffects$builder.foliageColorOverride(pFoliageColorOverride);
}
return new Biome.BiomeBuilder()
.hasPrecipitation(pHasPrecipitation)
.temperature(pTemperature)
.downfall(pDownfall)
.specialEffects(biomespecialeffects$builder.build())
.mobSpawnSettings(pMobSpawnSettings.build())
.generationSettings(pGenerationSettings.build())
.build();
}
// 添加一个自己的生物群系,使用了一些原版的方法。具体的内容自己点到方法里面看下把,不是很难,都是一些重复的配置的内容。
public static Biome exampleBiome(HolderGetter<PlacedFeature> pPlacedFeatures, HolderGetter<ConfiguredWorldCarver<?>> pWorldCarvers) {
// 创建一个MobSpawnSettings.Builder对象,用于配置生物生成设置。
MobSpawnSettings.Builder mobspawnsettings$builder = new MobSpawnSettings.Builder();
// 配置沙漠生成的生物。
BiomeDefaultFeatures.desertSpawns(mobspawnsettings$builder);
// 用于配置生物群系生成设置。
BiomeGenerationSettings.Builder biomegenerationsettings$builder = new BiomeGenerationSettings.Builder(pPlacedFeatures, pWorldCarvers);
// 添加化石装饰物到生物群系生成设置中
BiomeDefaultFeatures.addFossilDecoration(biomegenerationsettings$builder);
// 一些通用的配置
globalOverworldGeneration(biomegenerationsettings$builder);
// 矿物
BiomeDefaultFeatures.addDefaultOres(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDefaultSoftDisks(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDefaultFlowers(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDefaultGrass(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDesertVegetation(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDefaultMushrooms(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDesertExtraVegetation(biomegenerationsettings$builder);
BiomeDefaultFeatures.addDesertExtraDecoration(biomegenerationsettings$builder);
// 调用构造返回biome
return ExampleOverworldBiomes(false, 2.0F, 0.0F, mobspawnsettings$builder, biomegenerationsettings$builder, Musics.createGameMusic(SoundEvents.MUSIC_BIOME_DESERT));
}
private static void globalOverworldGeneration(BiomeGenerationSettings.Builder pGenerationSettings) {
BiomeDefaultFeatures.addDefaultCarversAndLakes(pGenerationSettings);
BiomeDefaultFeatures.addDefaultCrystalFormations(pGenerationSettings);
BiomeDefaultFeatures.addDefaultMonsterRoom(pGenerationSettings);
BiomeDefaultFeatures.addDefaultUndergroundVariety(pGenerationSettings);
BiomeDefaultFeatures.addDefaultSprings(pGenerationSettings);
BiomeDefaultFeatures.addSurfaceFreezing(pGenerationSettings);
}
}
下面让我们使用我们的配置的类,进行json数据的生成
// 原版的内容位于BiomeData类下
public class ModBiomeData {
// 依旧添加一个对应biome的key
public static final ResourceKey<Biome> EXAMPLE_BIOME = register("example_biome");
// 对应大的create方法
private static ResourceKey<Biome> register(String pKey) {
return ResourceKey.create(Registries.BIOME, new ResourceLocation(ExampleMod.MODID,pKey));
}
// bootstrap和之前的一样
public static void bootstrap(BootstapContext<Biome> pContext) {
// 获得placefeature的holdgetter
HolderGetter<PlacedFeature> holdergetter = pContext.lookup(Registries.PLACED_FEATURE);
HolderGetter<ConfiguredWorldCarver<?>> holdergetter1 = pContext.lookup(Registries.CONFIGURED_CARVER);
// 给数据生成注册我们的biome
pContext.register(ModBiomeData.EXAMPLE_BIOME, ExampleOverworldBiomes.exampleBiome(holdergetter, holdergetter1));
}
}
添加到provider中,和之前一样。不介绍了。
public class ModWorldGen extends DatapackBuiltinEntriesProvider {
public static final RegistrySetBuilder BUILDER = new RegistrySetBuilder()
.add(Registries.CONFIGURED_FEATURE, ModOreFeatures::bootstrap)
.add(Registries.PLACED_FEATURE, ModOrePlacements::bootstrap)
.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, ModBiomeModifiers::bootstrap)
.add(Registries.STRUCTURE_SET, ModStructureSets::bootstrap)
.add(Registries.STRUCTURE, ModStructures::bootstrap)
.add(Registries.BIOME, ModBiomeData::bootstrap);
public ModWorldGen(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries, BUILDER,Set.of(ExampleMod.MODID));
}
}
@Mod.EventBusSubscriber(modid = ExampleMod.MODID,bus = Mod.EventBusSubscriber.Bus.MOD)
public class ModDataGeneratorHandler {
@SubscribeEvent
public static void gatherData(GatherDataEvent event){
ExistingFileHelper efh = event.getExistingFileHelper();
var lp = event.getLookupProvider();
//world gen
event.getGenerator().addProvider(event.includeServer(), (DataProvider.Factory<ModWorldGen>) pOutput -> new ModWorldGen(pOutput,lp));
}
}
好了,这里你点击data gen之后就会生成你的biome.json文件了,但是你会发现没有在主世界中生成对应的biome,甚至你会发现我们上面的代码都没指定过在那个维度生成。而对于这部分内容,我并没有在neoforge中找到提供的方法, 同样如上,这部分在维度生成相关大的代码中,你或许需要mixin,不过这里我们使用terrablender lib帮帮助我们,在上一次的视频中我们已经配置过了这个lib。
所以我们直接使用。
// 我们使用terrablender的Region类给主世界添加生物群系
public class ModOverworldRegion extends Region {
public ModOverworldRegion(ResourceLocation name, int weight) {
// name,维度,权重
super(name, RegionType.OVERWORLD, weight);
}
// 重写addbiome方法,这个方法修改原版的主世界的维度中生成生物群系的方法
@Override
public void addBiomes(Registry<Biome> registry, Consumer<Pair<Climate.ParameterPoint, ResourceKey<Biome>>> mapper) {
this.addModifiedVanillaOverworldBiomes(mapper,modifiedVanillaOverworldBuilder -> {
modifiedVanillaOverworldBuilder.replaceBiome(Biomes.FOREST,ModBiomeData.EXAMPLE_BIOME);
});
}
}
注册我们写的这个region
public class ModTerrablender {
public static void registerBiome(){
// 第一个参数我们的region,第二个参数是维度的定位符,第三个是权重。
// 其中权重的数值设置你可以参考原版进行设置
Regions.register(new ModOverworldRegion(new ResourceLocation(ExampleMod.MODID,"overworld"),5));
}
}
然后,接下来我们说下怎么利用Terrablender来对生物群系的表面的方块进行修改。
教程只是对其中的一部分内容进行了介绍,并没有涵盖所有的内容,所有内容还是很多且有些复杂的,这说一部分,另一部分放在了维度的,剩下的就大家自己探索了。
其实surface规则的定义是放在维度的json中的。
// 对我们的生物群系的表面进行修改
// 首先说明的一点是surfaceRule是针对维度的,通过定义一系列的sequence来指定生成,如果具有一些条件判断是可以使用ConditionSource类来判断的,RuleSource通常作为一个结果。
public class ModSurfaceRules {
// SurfaceRules.RuleSource 表面生成的规则,这里我们使用makeStateRule来创建一个SurfaceRules.RuleSource
private static final SurfaceRules.RuleSource DIRT = makeStateRule(Blocks.DIRT);
private static final SurfaceRules.RuleSource GRASS_BLOCK = makeStateRule(Blocks.GRASS_BLOCK);
private static final SurfaceRules.RuleSource RUBY_BLOCK = makeStateRule(ModBlocks.RUBY_BLOCK.get());
private static final SurfaceRules.RuleSource DIAMOND_ORE = makeStateRule(Blocks.DIAMOND_ORE);
// 静态方法,用于产生我们的surface规则。
public static SurfaceRules.RuleSource makeRules() {
// 第一个规则是一个条件规则,表示水面上的得的位置
SurfaceRules.ConditionSource isAtOrAboveWaterLevel = SurfaceRules.waterBlockCheck(-1, 0);
// 使用第一个规则表示如果isAtOrAboveWaterLevel满足条件则使用GRASS_BLOCK方块。否则使用DIRT
SurfaceRules.RuleSource = SurfaceRules.sequence(SurfaceRules.ifTrue(isAtOrAboveWaterLevel, GRASS_BLOCK), DIRT);
// 返回我们的SurfaceRules.RuleSource
return SurfaceRules.sequence(
// 满足生物群系情况下,在地板使用RUBY_BLOCK和在天花板使用DIAMOND_ORE
// 这里的地板是指在生成地表的时候的地板和天花板
SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.isBiome(ModBiomeData.EXAMPLE_BIOME),
SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, RUBY_BLOCK)),
SurfaceRules.ifTrue(SurfaceRules.ON_CEILING, DIAMOND_ORE)),
// 这是一条原版的默认规则,如果不是这个生物群系,那么使用默认的草方块
SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, grassSurface)
);
}
// 这个makeStateRule也很简单,就是返回对应方块默认状态的 SurfaceRules.RuleSource
private static SurfaceRules.RuleSource makeStateRule(Block block) {
return SurfaceRules.state(block.defaultBlockState());
}
}
注册相关的几个类和方法
// The value here should match an entry in the META-INF/mods.toml file
@Mod(ExampleMod.MODID)
public class ExampleMod
{
public static final String MODID = "examplemod";
private static final Logger LOGGER = LogUtils.getLogger();
public ExampleMod(IEventBus modEventBus)
{
// 你的region写在这里
ModTerrablender.registerBiome();
NeoForge.EVENT_BUS.register(this);
}
private void commonSetup(final FMLCommonSetupEvent event)
{
// 给主世界添加新的surfaceRule
event.enqueueWork(()->{
SurfaceRuleManager.addSurfaceRules(SurfaceRuleManager.RuleCategory.OVERWORLD, ExampleMod.MODID, ModSurfaceRules.makeRules());
});
}
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event)
{
}
}
关于surfaceRule的教程,可以找一些教程,去看看具体得到相关内容,同样wiki上也有解释,你可以到wiki上查看.
但对于surfaceRule的原版代码内容在SurfaceRuleData这个类下面
到世界中看看你的生物群系吧.