Generierung von Merkmalen
VORAUSSETZUNGEN
Stelle sicher, dass du den Prozess der Einrichtung des Datengenerators zuerst abgeschlossen hast.
Die Generierung für Merkmale in Minecraft-Welten gliedert sich in drei Teile:
- Konfigurierte Merkmale: Diese definieren was ein Merkmal ist; zum Beispiel ein einzelner Baum
- Platzierte Merkmale: Diese definieren, wie die Merkmale ausgelegt werden sollen, in welche Richtung, an welcher relativen Position usw.; zum Beispiel die Platzierung von Bäumen in einem Wald
- Biom-Anpassungen: Diese defineiren, wo die Merkmale in der Welt platziert werden; zum Beispiel die Koordinaten des gesamten Waldes
INFO
Merkmale in Minecraft sind natürliche oder generierte Muster in der Welt, wie Bäume, Blumen, Erze oder Seen. Merkmale unterscheiden sich von Strukturen (z. B. Dörfern, Tempeln…), die mit dem Befehl /locate gefunden werden können.
Einrichtung
Zuerst müssen wir unseren Provider erstellen. Erstelle eine Klasse, die von FabricDynamicRegistryProvider erbt und fülle die Basismethoden aus:
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) {
}
@Override
public String getName() {
return "";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Füge dann den Provider zu deinem DataGeneratorEntrypoint in der onInitializeDataGenerator Methode hinzu:
java
1
Erstelle als Nächstes eine Klasse für deine konfigurierten Merkmale und eine Klasse für deine platzierten Merkmale. Diese müssen nichts erweitern.
Sowohl die Klasse für die konfigurierten Merkmale als auch die platzierten Merkmale sollten über eine öffentliche Methode verfügen, mit der du deine Merkmale registrieren und definieren kannst. Sein Argument, das wir context genannt haben, sollte für das konfigurierte Merkmal ein BootstrapContext<ConfiguredFeature<?, ?>> sein oder für das platzierte Merkmal ein BootstrapContext<PlacedFeature>.
Füge in deiner Klasse DataGeneratorEntrypoint die folgenden Zeilen in deine Methode buildRegistry ein und ersetze dabei den Methodennamen durch den von dir gewählten:
java
registryBuilder.add(Registries.CONFIGURED_FEATURE, ExampleModWorldConfiguredFeatures::configure);
registryBuilder.add(Registries.PLACED_FEATURE, ExampleModWorldPlacedFeatures::configure);1
2
2
Wenn du die Methode buildRegistry noch nicht hast, erstelle sie und versehe sie mit der Annotation @Override.
Konfigurierte Merkmale
Damit ein Merkmal in unserer Welt auf natürliche Weise erscheint, sollten wir zunächst ein konfiguriertes Merkmal in unserer Klasse für konfigurierte Merkmale definieren. Lasst uns ein benutzerdefiniertes konfiguriertes Merkmal für eine Diamanterz-Ader hinzufügen.
Registriere zunächst den Schlüssel für das ConfiguredFeature in deiner konfigurierten Merkmal-Klasse:
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
Das zweite Argument für den Identifier (in diesem Beispiel diamond_block_vein) ist das, was du verwenden würdest, um die Struktur mit dem Befehl /place zu platzieren, was beim Debuggen hilfreich ist.
Erze
Als Nächstes erstellen wir einen RuleTest, der festlegt, welche Blöcke deine Funktion ersetzen kann. Dieser RuleTest ermöglicht beispielsweise das Ersetzen jedes Blocks mit dem Tag DEEPSLATE_ORE_REPLACEABLES:
java
RuleTest deepslateReplaceableRule = new TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES);1
Als Nächstes müssen wir eine OreConfiguration erstellen, die dem Spiel sagt, mit was Blöcke ersetzt werden sollen.
java
List<OreConfiguration.TargetBlockState> diamondBlockOreConfig =
List.of(
OreConfiguration.target(deepslateReplaceableRule, Blocks.DIAMOND_BLOCK.defaultBlockState())
);1
2
3
4
2
3
4
In der Liste können mehrere Einträge für verschiedene Varianten enthalten sein. Lasst und zum Beispiel für Stein und Tiefenschiefer jeweils eine andere Variante setzen:
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
Zuletzt müssen wir unser konfiguriertes Merkmal in unserem Spiel registrieren!
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
Bäume
Um einen benutzerdefinierten Baum zu erstellen, musst du zunächst eine TreeConfiguration anlegen:
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
Dies ist, was jedes Argument macht:
- Gibt den Blocktyp für den Baumstamm an; zum Beispiel Diamantblock
- Konfiguriert die Form und das Höhenverhalten des Stammes mithilfe eines Stammplatzierers
- Gibt den Blocktyp für die Blätter des Baums an; zum Beispiel Goldblöcke
- Definiert die Form und Größe des Laubs mithilfe eines Laubplatzierers
- Steuert, wie sich der Baumstamm in verschiedenen Höhen verjüngt, vor allem bei größeren Stämmen
TIP
Wir empfehlen dir dringend, mit diesen Werten zu experimentieren, um einen individuellen Baum zu erstellen, mit dem du zufrieden bist!
Du kannst die integrierten Platzierer für den Stamm und das Laub der Standardbäume als Orientierung verwenden.
Als Nächstes müssen wir unseren Baum registrieren, indem wir die folgende Zeile zur Methode configure von ExampleModWorldConfiguredFeatures hinzufügen.
java
context.register(DIAMOND_TREE_CONFIGURED_KEY, new ConfiguredFeature<>(Feature.TREE, diamondTree));1
Platzierte Merkmale
Der nächste Schritt beim Hinzufügen eines Merkmal zum Spiel ist die Erstellung der entsprechenden platzierten Merkmal.
Erstelle in der Methode configure deiner Klasse des platzierten Merkmal eine Variable wie die folgende:
java
HolderGetter<ConfiguredFeature<?, ?>> configuredFeatures = context.lookup(Registries.CONFIGURED_FEATURE);1
Definiere in deiner Klasse für platzierte Merkmale den Schlüssel für dein platziertes Merkmal.
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
Platzierungsmodifikatoren
Als Nächstes müssen wir unsere Platzierungsmodifikatoren definieren. Dabei handelt es sich um Attribute, die beim Erzeugen des Merkmal festgelegt werden. Das kann alles Mögliche sein: Von der Häufigkeit der Erzeugung bis hin zur Startposition auf der y-Achse. Du kannst so wenige oder so viele Modifikatoren verwenden, wie du möchtest.
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
Die einzelnen Modifikatoren haben folgende Funktionen:
- CountPlacement: Ungefähr die Anzahl der Vorkommen dieses Merkmals (in diesem Fall Adern) pro Chunk
- BiomeFilter: Erlaubt und die Kontrolle, in welchen Biomen/Dimensionnen es erschaffen wird (darauf gehen wir später noch näher ein)
- InSquarePlacement: Verteilt die Merkmale pseudozufälliger
- HeightRangePlacement: Gibt den Bereich der
y-Koordinaten an, in dem ein Merkmal erzeugt werden kann; es unterstützt drei Hauptverteilungsarten:Uniform: Alle
y-Werte innerhalb dieses Bereichs weisen mit gleicher Wahrscheinlichkeit das Merkmal auf. Wenn du dir unsicher bist, nimm einfach diese hier.Trapezoid:
y-Werte, die näher am Medianwert vonyliegen, weisen eine höhere Wahrscheinlichkeit auf, das Merkmal zu enthalten.Biased-Bottom: Verwendet eine logarithmische Skala, bei der niedrigere
y-Werte mit höherer Wahrscheinlichkeit das Merkmal erhalten. Es erhält eine Startkoordinatey, unterhalb derer das Markmal niemals erzeugt wird. Das zweite Argument ist die maximale Höhe, in der das Merkmal erscheinen kann. Das dritte Argument definiert einen Bereich in Blöcken, über den sich die maximale Wahrscheinlichkeit erstreckt.
TIP
Bäume und andere Oberflächenstrukturen sollten den Modifikator PlacedFeatures.WORLD_SURFACE_WG_HEIGHTMAP anstelle von HeightRangePlacement enthalten, um sicherzustellen, dass der Baum auf der Oberfläche erscheint.
Da wir nun die Modifikatoren haben, können wir unser platziertes Merkmal registrieren:
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
Biom Modifikationen
Zuletzt müssen wir unser platziertes Merkmal während der Mod-Initialisierung zu BiomeModifications hinzufügen. Das können wir tun, indem wir Folgendes in unseren Mod-Initialisierer einfügen:
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
Bei Bäumen sollte der zweite Parameter auf GenerationStep.Decoration.VEGETAL_DECORATION gesetzt werden.
Biomspeifische Generierung
Durch Ändern des Arguments BiomeSelectors können wir erreichen, dass unser Merkmal nur in einem bestimmten Biomtyp erscheint:
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
Dieses würde nur in Biomen erscheinen, die mit dem Biom-Tag minecraft:is_forest versehen sind.
Den Datengenerator ausführen
Wenn du jetzt den Datengenerator ausführst, solltest du für jedes konfigurierte Merkmal, das du hinzugefügt hast, eine .json-Datei unter src/main/generated/data/example-mod/worldgen/configured_feature finden, und für jedes platzierte Merkmal eine Datei unter src/main/generated/data/example-mod/worldgen/placed_feature!
Generierte Datei für das konfigurierte Merkmal
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
Generierte Datei für das platzierte Merkmal
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



