改变世界生成
警告
这些文档是为 SpongeAPI 7 编写的,可能已经过时。 如果你觉得你可以帮助更新它们,请提交一个 PR!
改变原版世界生成
创建自定义地形
创建自定义 GenerationPopulator
创建自定义 Populator
创建自定义生物群落
改变原版世界生成
備註
该部分假设读者已经对 WorldGeneratorModifier 足够熟悉,如果并非如此,请先阅读 世界生成修改器 部分。
Sponge 提供了大量的原版世界生成的接口用于进行各种操作。目前,世界生成过程中唯一 易于 操作的阶段是 Population 阶段。
作为一个简单的示例,我们来看看我们如何修改沙漠中生成的仙人掌以使其高一些。
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(WorldProperties 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(WorldProperties 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, ImmutableBiomeVolume 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, 64, 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(MutableBiomeVolume buffer) {
Vector3i min = buffer.getBiomeMin();
Vector3i max = buffer.getBiomeMax();
for (int x = min.getX(); x <= max.getX(); x++) {
for (int z = min.getZ(); z <= max.getZ(); z++) {
if (x * x + z * z < HILLS_RADIUS) {
buffer.setBiome(x, 64, z, BiomeTypes.EXTREME_HILLS);
} else if (x * x + z * z < FOREST_RADIUS) {
buffer.setBiome(x, 64, z, BiomeTypes.FOREST);
} else if (x * x + z * z < BEACH_RADIUS) {
buffer.setBiome(x, 64, z, BiomeTypes.BEACH);
} else {
buffer.setBiome(x, 64, z, BiomeTypes.OCEAN);
}
}
}
}
}