Модифицирование генерации мира

  • Модификация ванильной генерации

  • Создание пользовательской базовой местности

  • Создание пользовательских GenerationPopulators

  • Создание пользовательских популяторов

  • Создание пользовательских биомов

Модификация ванильной генерации

Примечание

На этой странице предполагается, что вы знакомы с настройкой WorldGeneratorModifier. Если нет, то, пожалуйста, прочитайте статью о настройке своего модификатора в WorldGeneratorModifiers.

Sponge предоставляет большое количество генераций ванильного мира, которыми можно манипулировать с помощью различных интерфейсов. В настоящее время единственными элементами процесса генерации, которые легко подвергаются манипуляции, являются популяторы.

В качестве примера давайте взглянем на то, как можно изменить высоту спавнящихся в пустыне кактусов.

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 популяторов и установим высоту на 5, что позволит генерировать только кактусы высотой 5 блоков.

Примечание

Cactus#setHeight(int) и многие другие аналогичные методы для других популяторов также принимают VariableAmount, который можно использовать для указания высоты как диапазона или другого настраиваемого значения.

Это был простой пример того, как можно изменять существующий популятор. Давайте рассмотрим, как можно добавить новый экземпляр ванильного популятора. В этот раз популятор будет добавлен глобально, он будет применяться ко всем чанкам, независимо от биома. Давайте добавим популятор Pumpkin глобально, в результате чего тыквы будут разбросаны по всему миру.

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 для этого популятора. Затем установите нужные вам настройки популятора, в этом случае нам надо, чтобы на каждом тыквенном поле появлялась дюжина тыкв. Наконец, добавьте новый популятор в список популяторов, применяемых к миру глобально.

Вуаля, теперь тыквы у нас повсюду!

Примечание

В этом примере мы добавили популятор тыкв в конец списка популяторов, но стоит заметить, что этот элементы списка зависит от положения в нём. Так что, если Вам нужно вызвать популятор раньше других, что будет полезно для популятора Forest, то его надо добавить в начало списка.

These two examples should serve to help you get familiar with the realm of working with vanilla populators. This only touches the surface of what is possible. See the javadocs for a complete listing of available populators and their properties.

Создание пользовательской базовой местности

Changing the base GenerationPopulator of a world generator allows you to change the base terrain shape generation of the world. A generator populator will roughly follow the procedure of using the seed and biome information to seed and modify a series of noise maps, from which the terrain is formed. The terrain created in a modified base generator populator should only consist of stone blocks, to allow the biomes to properly replace blocks for biome-specific ground cover.

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);
    }
}

This is a fairly simple example of a base terrain generation populator (at least, if you look past the math to calculate the height). For each column in the buffered area we want to calculate a height value, and then fill in everything below that with stone and leave everything above it as air (or water if we’re still below sea-level).

Создание пользовательских GenerationPopulators

Примечание

API для пользовательских GenerationPopulators ещё в разработке. В будущем этот раздел будет расширен.

Создание пользовательских популяторов

Custom populators can be used to add a great variety of custom features. To create a custom populator you need only create a class implementing the Populator interface and add it to the list of populators attached to a biome, or to a world generator if you want it applied globally.

Your custom populator will be passed an Extent which is a view onto the world covering the area that you should be applying your populator. It is advised that you do not make any assumptions as to the expected size or position of this extent, as it may be larger or smaller for operations such as regenerating a chunk.

Примечание

To allow your populator to overlap chunk boundaries your populator is allowed to extend up to 8 blocks outside of the boundaries of the extent.

Создание пользовательских биомов

While it is currently not possible to create entirely new biomes from within sponge, you can replace the system by which they are arranged in the world be implementing the BiomeGenerator interface and setting your custom biome generator onto a WorldGenerator.

Below is an example of a biome generator which creates one large island centered around (0, 0).

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);
                }
            }
        }
    }
}