Генерация структур 26.1.2
Руководство по генерации структур в мире с помощью datagen.
ТРЕБОВАНИЯ
Сначала убедитесь, что вы установили datagen.
Структуры (Features) в Minecraft — это природные или генерируемые объекты в мире, такие как деревья, цветы, руды или озера. Структуры отличаются от строений (Structures) (например, деревень, храмов...), которые можно найти с помощью команды /locate.
Генерация структур в мирах Minecraft делится на 3 части:
- Конфигурируемые структуры: определяют, чем является объект; например, одиночное дерево
- Правила размещения: определяют, как объекты должны располагаться, в каком направлении, их относительное положение и т. д.; например, размещение деревьев в лесу
- Модификации биомов: определяют, где именно структуры размещаются в мире; например, координаты всего леса
Настройка
Сначала необходимо создать наш провайдер. Создайте класс, который расширяет FabricDynamicRegistryProvider, внутри основного пакета (main) и заполните базовые методы:
java
public class ExampleModWorldgenProvider extends FabricDynamicRegistryProvider {
public ExampleModWorldgenProvider(FabricPackOutput output, CompletableFuture<HolderLookup.Provider> registriesFuture) {
super(output, registriesFuture);
}
@Override
protected void configure(HolderLookup.Provider registries, Entries entries) {
entries.addAll(registries.lookupOrThrow(Registries.CONFIGURED_FEATURE));
entries.addAll(registries.lookupOrThrow(Registries.PLACED_FEATURE));
}
@Override
public String getName() {
return "World Generation";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
В методе configure мы вызовем addAll, чтобы гарантировать генерацию всех файлов для наших структур.
java
entries.addAll(registries.lookupOrThrow(Registries.CONFIGURED_FEATURE));
entries.addAll(registries.lookupOrThrow(Registries.PLACED_FEATURE));1
2
2
Затем добавьте этот провайдер в ваш класс DataGeneratorEntrypoint внутри метода onInitializeDataGenerator:
java
pack.addProvider(ExampleModWorldgenProvider::new);1
Далее создайте класс для конфигурируемых структур и класс для правил размещения. Им не нужно ничего расширять.
Класс конфигурируемых структур и класс правил размещения должны иметь публичный метод для регистрации и определения ваших структур. Его аргумент (который мы назвали context) должен иметь тип BootstrapContext<ConfiguredFeature<?, ?>> для конфигурируемой структуры или BootstrapContext<PlacedFeature> для правила размещения.
В вашем классе DataGeneratorEntrypoint добавьте приведенные ниже строки в метод buildRegistry, заменив имя метода на выбранное вами:
java
registryBuilder.add(Registries.CONFIGURED_FEATURE, ExampleModWorldConfiguredFeatures::configure);
registryBuilder.add(Registries.PLACED_FEATURE, ExampleModWorldPlacedFeatures::configure);1
2
2
Если у вас еще нет метода buildRegistry, создайте его и добавьте аннотацию @Override.
Конфигурируемые структуры
Чтобы структура естественно появлялась в нашем мире, мы должны начать с определения конфигурируемой структуры в нашем классе конфигурируемых структур.
Прежде чем что-либо делать, давайте создадим класс конфигурируемых структур внутри основного пакета и объявим метод configure:
java
public class ExampleModWorldConfiguredFeatures {
public static void configure(BootstrapContext<ConfiguredFeature<?, ?>> context) {
}
}1
2
3
4
2
3
4
Теперь давайте добавим кастомную конфигурируемую структуру для алмазной жилы. Сначала зарегистрируйте ключ для ConfiguredFeature в вашем классе конфигурируемых структур:
java
public static final ResourceKey<ConfiguredFeature<?, ?>> DIAMOND_BLOCK_VEIN_CONFIGURED_KEY =
ResourceKey.create(
Registries.CONFIGURED_FEATURE,
Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, "diamond_block_vein")
);1
2
3
4
5
2
3
4
5
TIP
Второй аргумент в Identifier (в данном примере diamond_block_vein) — это то, что вы будете использовать для призыва объекта с помощью команды /place, что очень полезно при отладке.
Руды
Затем внутри метода configure мы создадим RuleTest, который определяет, какие блоки может заменять ваша структура. Например, этот RuleTest разрешает замену каждого блока с тегом DEEPSLATE_ORE_REPLACEABLES:
java
RuleTest deepslateReplaceableRule = new TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES);1
Далее, также внутри метода configure, нам нужно создать OreConfiguration, который указывает игре, на что именно заменять блоки.
java
List<OreConfiguration.TargetBlockState> diamondBlockOreConfig =
List.of(
OreConfiguration.target(deepslateReplaceableRule, Blocks.DIAMOND_BLOCK.defaultBlockState())
);1
2
3
4
2
3
4
Вы можете указать несколько вариантов в списке для разных типов поверхностей. Например, давайте настроим разные варианты для камня и глубинного сланца:
java
List<OreConfiguration.TargetBlockState> ironAndDiamondBlockOreConfig =
List.of(
OreConfiguration.target(deepslateReplaceableRule, Blocks.DIAMOND_BLOCK.defaultBlockState()),
OreConfiguration.target(stoneReplaceableRule, Blocks.IRON_BLOCK.defaultBlockState())
);1
2
3
4
5
2
3
4
5
Наконец, нам нужно зарегистрировать нашу конфигурируемую структуру в игре внутри метода configure!
java
context.register(
DIAMOND_BLOCK_VEIN_CONFIGURED_KEY,
new ConfiguredFeature<>(
Feature.ORE,
new OreConfiguration(diamondBlockOreConfig, 10)) // 10 is the blocks per vein
);1
2
3
4
5
6
2
3
4
5
6
Деревья
Чтобы создать дерево, вам сначала нужно создать TreeConfiguration внутри метода configure:
java
TreeConfiguration diamondTree = new TreeConfiguration.TreeConfigurationBuilder(
// Trunk / Logs
BlockStateProvider.simple(Blocks.DIAMOND_BLOCK),
new StraightTrunkPlacer(4, 2, 0),
// Leaves
BlockStateProvider.simple(Blocks.GOLD_BLOCK),
new BlobFoliagePlacer(ConstantInt.of(2), ConstantInt.of(0), 3),
new TwoLayersFeatureSize(0, 0, 0)
).build();1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Вот за что отвечает каждый аргумент:
- Задает тип блока для ствола дерева; например, алмазные блоки
- Настраивает форму ствола и алгоритм его высоты с помощью генератора ствола (
TrunkPlacer) - Задает тип блока для листвы дерева; например, золотые блоки
- Определяет форму и размер листвы с помощью генератора листвы (
FoliagePlacer) - Управляет тем, как ствол дерева сужается на разной высоте (используется преимущественно для больших стволов)
TIP
Мы настоятельно рекомендуем поэкспериментировать с этими значениями, чтобы создать уникальное дерево, которое понравится именно вам!
В качестве ориентира вы можете использовать встроенные генераторы стволов (TrunkPlacer) и листвы (FoliagePlacer) из ванильных деревьев.
Затем нам нужно зарегистрировать наше дерево, добавив следующую строку в метод configure класса ExampleModWorldConfiguredFeatures.
java
context.register(DIAMOND_TREE_CONFIGURED_KEY, new ConfiguredFeature<>(Feature.TREE, diamondTree));1
Правила размещения структур
Следующим шагом для добавления структуры в игру является создание правила её размещения (Placement Feature).
Давайте создадим класс для размещения структур внутри основного пакета и добавим в него метод configure, как мы делали ранее:
java
public class ExampleModWorldPlacedFeatures {
public static void configure(BootstrapContext<PlacedFeature> context) {
}
}1
2
3
4
2
3
4
В методе configure вашего класса размещения структур создайте переменную, аналогичную приведенной ниже:
java
HolderGetter<ConfiguredFeature<?, ?>> configuredFeatures = context.lookup(Registries.CONFIGURED_FEATURE);1
В вашем классе размещения структур определите ключ для вашей структуры:
java
public static final ResourceKey<PlacedFeature> DIAMOND_BLOCK_ORE_PLACED_KEY =
ResourceKey.create(
Registries.PLACED_FEATURE,
Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, "diamond_block_ore_placed")
);1
2
3
4
5
2
3
4
5
Модификаторы размещения
Далее внутри метода configure нам нужно определить модификаторы размещения (Placement Modifiers). Это параметры, которые вы задаете для настройки генерации структуры. Они могут быть какими угодно: от частоты появления до начальной высоты по координате Y. Вы можете использовать столько модификаторов, сколько вам необходимо.
java
List<PlacementModifier> diamondBlockVeinModifiers = List.of(
CountPlacement.of(6),
BiomeFilter.biome(),
InSquarePlacement.spread(),
HeightRangePlacement.of(BiasedToBottomHeight.of(VerticalAnchor.BOTTOM, VerticalAnchor.absolute(0), 3))
);1
2
3
4
5
6
2
3
4
5
6
Функции каждого из перечисленных модификаторов заключаются в следующем:
CountPlacement: примерно определяет количество экземпляров этой структуры (в данном случае жил) на один чанкBiomeFilter: позволяет нам контролировать, в каких биомах/измерениях появляется структура (мы подробнее разберем это далее)InSquarePlacement: распределяет структуры по площади чанка более псевдослучайным образомHeightRangePlacement: задает диапазон координат Y, в котором может генерироваться структура. Он поддерживает три основных типа распределения:Равномерное (Uniform): все значения Y в пределах диапазона имеют одинаковую вероятность появления структуры. Если вы сомневаетесь, используйте этот вариант.
Трапециевидное (Trapezoid): значения Y, расположенные ближе к середине диапазона, имеют более высокую вероятность появления структуры.
Смещенное к низу (Biased-Bottom):
Использует логарифмическую шкалу, при которой более низкие значения Y имеют большую вероятность получить структуру. Принимает начальную координату Y, ниже которой структура никогда не появится. Второй аргумент — максимальная высота генерации структуры. Третий аргумент определяет диапазон в блоках, на который распространяется максимальная вероятность.
TIP
Деревья и другие наземные структуры должны включать модификатор PlacedFeatures.WORLD_SURFACE_WG_HEIGHTMAP вместо HeightRangePlacement, чтобы гарантировать появление дерева именно на поверхности земли.
Теперь, когда у нас есть модификаторы, мы можем зарегистрировать наше правило размещения структуры в методе configure:
java
context.register(
DIAMOND_BLOCK_ORE_PLACED_KEY,
new PlacedFeature(
configuredFeatures.getOrThrow(ExampleModWorldConfiguredFeatures.DIAMOND_BLOCK_VEIN_CONFIGURED_KEY),
diamondBlockVeinModifiers
)
);1
2
3
4
5
6
7
2
3
4
5
6
7
Модификации биомов
Ну и напоследок, нужно добавить наше правило размещения структуры в BiomeModifications во время инициализации мода. Мы можем сделать это, добавив следующий код в наш инициализатор мода:
java
// Spawns everywhere in the overworld
BiomeModifications.addFeature(
BiomeSelectors.foundInOverworld(),
GenerationStep.Decoration.UNDERGROUND_ORES,
ExampleModWorldPlacedFeatures.DIAMOND_BLOCK_ORE_PLACED_KEY
);1
2
3
4
5
6
2
3
4
5
6
TIP
Для деревьев, вторым параметром следует установить GenerationStep.Decoration.VEGETAL_DECORATION,
Генерация в конкретных биомах
Изменяя аргумент BiomeSelectors, мы можем сделать так, чтобы наша структура появлялась только в определенном типе биома:
java
// Spawns in forest biomes only
BiomeModifications.addFeature(
BiomeSelectors.tag(BiomeTags.IS_FOREST),
GenerationStep.Decoration.VEGETAL_DECORATION,
ExampleModWorldPlacedFeatures.DIAMOND_TREE_PLACED_KEY
);1
2
3
4
5
6
2
3
4
5
6
В это примере генерация будет происходить только в биомах, отмеченных тегом minecraft:is_forest.
Запуск Datagen
Теперь, когда вы запустите генерацию данных (datagen), вы увидите JSON-файл в папке src/main/generated/data/example-mod/worldgen/configured_feature для каждой добавленной конфигурируемой структуры, а также файл в папке src/main/generated/data/example-mod/worldgen/placed_feature для каждого правила размещения!
Сгенерированный файл конфигурируемой структуры
json
{
"type": "minecraft:ore",
"config": {
"discard_chance_on_air_exposure": 0.0,
"size": 10,
"targets": [
{
"state": {
"Name": "minecraft:diamond_block"
},
"target": {
"predicate_type": "minecraft:tag_match",
"tag": "minecraft:deepslate_ore_replaceables"
}
}
]
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Сгенерированный файл размещения структуры
json
{
"feature": "example-mod:diamond_block_vein",
"placement": [
{
"type": "minecraft:count",
"count": 6
},
{
"type": "minecraft:biome"
},
{
"type": "minecraft:in_square"
},
{
"type": "minecraft:height_range",
"height": {
"type": "minecraft:biased_to_bottom",
"inner": 3,
"max_inclusive": {
"absolute": 0
},
"min_inclusive": {
"above_bottom": 0
}
}
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28





