改变世界生成

  • 改变原版世界生成

  • 创建自定义地形

  • 创建自定义 GenerationPopulator

  • 创建自定义 Populator

  • 创建自定义生物群落

改变原版世界生成

備註

该部分假设读者已经对 WorldGeneratorModifier 足够熟悉,如果并非如此,请先阅读 世界生成修改器 部分。

Sponge 提供了大量的原版世界生成的接口用于进行各种操作。目前,世界生成过程中唯一 易于 操作的阶段是 Population 阶段。

作为一个简单的示例,我们来看看我们如何修改沙漠中的仙人掌(译者注:原文为 Cactii,怀疑其为笔误)以使其高一些。

import org.spongepowered.api.world.biome.BiomeGenerationSettings;
import org.spongepowered.api.world.biome.BiomeTypes;
import org.spongepowered.api.world.gen.Populator;
import org.spongepowered.api.world.gen.populator.Cactus;

@Override
public void modifyWorldGenerator(WorldCreationSettings world, DataContainer settings, WorldGenerator worldGenerator) {
    BiomeGenerationSettings desertSettings = worldGenerator.getBiomeSettings(BiomeTypes.DESERT);
    for (Cactus populator : desertSettings.getPopulators(Cactus.class)) {
        populator.setHeight(5);
    }
}

首先我们从获取沙漠生物群落对应的 BiomeGenerationSettings 开始。该对象保存了所有有关于该生物群落的世界生成的配置。然后我们需要找到用于仙人掌的世界生成,也就是所有的 Cactus ,并将其高度设置为五。换句话说,我们设置了所有世界生成中的仙人掌的高度为五。

備註

Cactus#setHeight(int) 以及众多类似的方法同时可以接受一个 VariableAmount 作为参数以允许开发者自定义高度为一个范围或其他自定义值。

这是一个如何修改现有世界生成的简单示例。让我们来看看我们如何添加一个新的世界生成。这一次,我们将在所有的世界生成中添加,这意味着它将不考虑特定生物群落而应用于所有地方。让我们在所有的世界生成中添加南瓜的生成,使南瓜散布到世界各地。

import org.spongepowered.api.world.gen.populator.Pumpkin;

@Override
public void modifyWorldGenerator(WorldCreationSettings world, DataContainer settings, WorldGenerator worldGenerator) {
    Pumpkin pumpkinPopulator = Pumpkin.builder().perChunk(12).build();
    worldGenerator.getPopulators().add(pumpkinPopulator);
}

和上面的示例恰恰相反,这一次你正在定义全新的世界生成。首先你需要一个生成器(Builder)。然后你把你想要的设置设置进去——这里我们会在一个区块生成一打,也就是十二个南瓜。最后我们让全世界都知道我们要生成南瓜了。

啊哈!现在遍地都是南瓜。

備註

在这个例子中,我们把把关于南瓜的世界生成添加到了列表的最后,需要注意的一点是,这个列表是有序的。所以如果你想要在 Population 阶段比其他世界生成更早进行,比如生成森林,你应该将你的世界生成添加到列表的开头。

这两个例子应该帮助了你熟悉原版的世界生成。不过这里我们只介绍了一点点皮毛。若要获取相关的完整列表,请参阅 JavaDocs。

创建自定义地形

通过修改世界的基础 GenerationPopulator 你可以自定义世界的地形生成。一个 GenerationPopulator 将粗略地遵循使用种子和生物群落信息来生成一系列噪声图的过程,由此形成地形。在修改的 GenerationPopulator 中创建的地形应当仅由石块组成,以允许生物群落适当地替换用于特定生物群落的覆盖方块。

public class SinusoidalGenerator implements GenerationPopulator {

    @Override
    public void populate(World world, MutableBlockVolume buffer, ImmutableBiomeArea biomes) {
        for(int x = buffer.getBlockMin().getX(); x < buffer.getBlockMax().getX(); x++) {
            for(int z = buffer.getBlockMin().getZ(); z < buffer.getBlockMax().getZ(); z++) {
                BiomeType biome = biomes.getBiome(x,z);
                int height = getHeight(x, z, world.getWorldGenerator().getBiomeSettings(biome));
                for(int y = 0; y < height || y < 64; y++) {
                    if(y < height) {
                        buffer.setBlockType(x, y, z, BlockTypes.STONE);
                    } else {
                        buffer.setBlockType(x, y, z, BlockTypes.WATER);
                    }
                }
            }
        }
    }

    private int getHeight(int x, int z, BiomeGenerationSettings biome) {
        double sx = Math.sin(x / 64d) + 1;
        double sz = Math.sin(z / 64d) + 1;
        double value = (sx + sz) / 4d;
        double heightRange = biome.getMaxHeight() - biome.getMinHeight();
        double height = heightRange * value / biome.getMinHeight();
        return GenericMath.floor(height * 256);
    }
}

这是一个相当简单的基础地形生成器的例子(如果你看一看计算高度的数学方法的话)。对于缓冲区域中的每一列,我们要计算一个高度值,然后用石头填充下面的所有内容,并将上面的所有内容作为空气(如果低于海平面,则为水)。

创建自定义 GenerationPopulator

備註

因为 Sponge 团队的拖延症,所以不仅这一节什么都没有,相关的 API 及其实现都没有完成。所以读者请耐心等待这一节的完成。

创建自定义 Populator

自定义的 Populator 可用于添加各种自定义功能。要创建自定义的 Populator 你只需创建一个实现 Populator 接口的类,并将其添加到附加到生物群组的对应列表中,或者添加到世界生成修改器(如果你希望应用于全局)中。

你的 Populator 将被传递一个地域(Extent)。一个地域是一个覆盖了你的 Populator 应该应用其上的区域的世界的视图。建议您不要对该范围的大小或位置做出任何假设,因为对于诸如重新生成区块的操作,它可能大于或小于你的预期。

備註

为了使你的 Populator 和区块边界重叠,你的 Populator 允许扩展到边界外的八个方块。

创建自定义生物群落

虽然在 Sponge 中并不能新添加生物群落,不过你可以通过实现 BiomeGenerator 接口来替代世界原有的生成系统同时将其设置成自己的。

下面是一个在 X 坐标和 Z 坐标均为零的地方生成一个大岛屿的示例。

public class IslandBiomeGen implements BiomeGenerator {

    private static final double ISLAND_SIZE = 200f;
    private static final double BEACH_RADIUS = ISLAND_SIZE * ISLAND_SIZE;
    private static final double FOREST_SIZE = ISLAND_SIZE - 7;
    private static final double FOREST_RADIUS = FOREST_SIZE * FOREST_SIZE;
    private static final double HILLS_SIZE = FOREST_SIZE - 120;
    private static final double HILLS_RADIUS = HILLS_SIZE * HILLS_SIZE;

    @Override
    public void generateBiomes(MutableBiomeArea buffer) {
        Vector2i min = buffer.getBiomeMin();
        Vector2i max = buffer.getBiomeMax();

        for (int x = min.getX(); x <= max.getX(); x++) {
            for (int y = min.getY(); y <= max.getY(); y++) {
                if (x * x + y * y < HILLS_RADIUS) {
                    buffer.setBiome(x, y, BiomeTypes.EXTREME_HILLS);
                } else if (x * x + y * y < FOREST_RADIUS) {
                    buffer.setBiome(x, y, BiomeTypes.FOREST);
                } else if (x * x + y * y < BEACH_RADIUS) {
                    buffer.setBiome(x, y, BiomeTypes.BEACH);
                } else {
                    buffer.setBiome(x, y, BiomeTypes.OCEAN);
                }
            }
        }
    }
}