tutorial:structures
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
features [2019/02/16 20:21] – draylar | tutorial:structures [2020/02/22 20:05] – [Registering Features] Update feature configuring to 1.15 earthcomputer | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | **An example repo containing | + | ====== Generating Structures ====== |
+ | |||
+ | ===== Features ===== | ||
+ | |||
+ | All code used in this tutorial | ||
+ | |||
+ | ==== Introduction ==== | ||
We’re going to look at registering and placing structures in your world. | We’re going to look at registering and placing structures in your world. | ||
Line 7: | Line 13: | ||
You are going to need a Feature and Generator for the most basic structure. The feature handles the process of registering the structure and loading it in when the world is generating-- it answers questions such as ‘should I spawn here?’ and ‘what is my name?’ The generator handles the placement of blocks or loading in a structure file if you choose to do so. | You are going to need a Feature and Generator for the most basic structure. The feature handles the process of registering the structure and loading it in when the world is generating-- it answers questions such as ‘should I spawn here?’ and ‘what is my name?’ The generator handles the placement of blocks or loading in a structure file if you choose to do so. | ||
- | To create a basic feature, we recommend creating a class that extends AbstractTempleFeature< | + | ==== Creating a Feature ==== |
+ | |||
+ | To create a basic feature, we recommend creating a class that extends AbstractTempleFeature< | ||
* shouldStartAt: | * shouldStartAt: | ||
* getName: name of your structure | * getName: name of your structure | ||
- | * method_14021 [getRadius]: radius of your structure, used for placement | + | * getRadius: radius of your structure, used for placement |
- | * method_13774 [getSeed]: a seed to use for generation, put 0 for testing | + | * getSeeedModifier |
You can pass DefaultFeatureConfig:: | You can pass DefaultFeatureConfig:: | ||
Line 18: | Line 26: | ||
For getStructureStartFactory, | For getStructureStartFactory, | ||
- | | + | <code java [enable_line_numbers=" |
- | | + | public static class MyStructureStart extends StructureStart |
- | | + | public MyStructureStart (StructureFeature<?> |
- | | + | super(structureFeature_1, |
- | | + | |
- | } | + | |
- | @Override | + | |
- | public void initialize(ChunkGenerator<?> | + | |
- | { | + | |
- | DefaultFeatureConfig defaultFeatureConfig = chunkGenerator.getStructureConfig(biome, | + | |
- | int x = chunkX * 16; | + | |
- | int z = chunkZ * 16; | + | |
- | BlockPos startingPos = new BlockPos(x, 0, z); | + | |
- | Rotation rotation = Rotation.values()[this.random.nextInt(Rotation.values().length)]; | + | |
- | MyGenerator.addParts(structureManager, | + | |
- | this.setBoundingBoxFromChildren(); | + | |
- | } | + | |
} | } | ||
+ | @Override | ||
+ | public void initialize(ChunkGenerator<?> | ||
+ | DefaultFeatureConfig defaultFeatureConfig = chunkGenerator.getStructureConfig(biome, | ||
+ | int x = chunkX * 16; | ||
+ | int z = chunkZ * 16; | ||
+ | BlockPos startingPos = new BlockPos(x, 0, z); | ||
+ | Rotation rotation = Rotation.values()[this.random.nextInt(Rotation.values().length)]; | ||
+ | MyGenerator.addParts(structureManager, | ||
+ | this.setBoundingBoxFromChildren(); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
| | ||
This is called when the world attempts to spawn in a new structure, and is the gap between your Feature and Generator. The reference to the variable in your main class doesn' | This is called when the world attempts to spawn in a new structure, and is the gap between your Feature and Generator. The reference to the variable in your main class doesn' | ||
- | This is where structure files and generating straight from a generate method part ways. If you want, you can simply override generate in your Feature class and use setBlockState to place blocks directly in the world. This is a valid option and was popular pre-1.13, but structure files are simply more powerful at this point and are highly recommended. | + | This is where structure files and generating straight from a generate method part ways. There are two ways to go about this: |
+ | |||
+ | * If you want, you can simply override generate in your Feature class and use setBlockState to place blocks directly in the world. This is a valid option and was popular pre-1.13. | ||
+ | * Use structure files and a Generator. These are rather | ||
+ | |||
+ | ==== Creating a Generator ==== | ||
As you have probably noticed, we need to create a generator. We'll name it MyGenerator, | As you have probably noticed, we need to create a generator. We'll name it MyGenerator, | ||
* An Identifier that points to your structure file; use " | * An Identifier that points to your structure file; use " | ||
- | * Some sort of setup method-- addParts is a good name: | + | * Some sort of setup method - addParts is a good name: |
- | | + | <code java> |
- | List< | + | public static void addParts(StructureManager structureManager_1, |
+ | List< | ||
| | ||
- | | + | } |
+ | </ | ||
| | ||
In your addParts method, you can choose which structure pieces are added to your generation process. You can add a piece like this: | In your addParts method, you can choose which structure pieces are added to your generation process. You can add a piece like this: | ||
- | | + | <code java> |
+ | list_1.add(new MyGenerator.Piece(structureManager_1, | ||
+ | </ | ||
| | ||
where the identifier is the path we created recently. | where the identifier is the path we created recently. | ||
Line 61: | Line 77: | ||
Override required methods, and add a constructor that takes in a StructureManager, | Override required methods, and add a constructor that takes in a StructureManager, | ||
- | | + | <code java [enable_line_numbers=" |
- | private Rotation rotation; | + | public static class Piece extends SimpleStructurePiece { |
- | private Identifier template; | + | private Rotation rotation; |
+ | private Identifier template; | ||
+ | |||
+ | public Piece(StructureManager structureManager_1, | ||
+ | super(MyModClass.myStructurePieceType, | ||
| | ||
- | | + | this.pos = blockPos_1; |
- | super(MyModClass.myStructurePieceType, | + | this.rotation = rotation_1; |
- | + | this.template = identifier_1; | |
- | | + | |
- | this.rotation = rotation_1; | + | |
- | this.template = identifier_1; | + | |
- | + | ||
- | this.setStructureData(structureManager_1); | + | |
- | } | + | |
| | ||
- | public Piece(StructureManager structureManager_1, | + | |
- | super(MyModClass.myStructurePieceType, | + | } |
- | this.identifier = new Identifier(compoundTag_1.getString(" | + | |
- | this.rotation = Rotation.valueOf(compoundTag_1.getString(" | + | |
- | this.setStructureData(structureManager_1); | + | super(MyModClass.myStructurePieceType, |
- | } | + | this.identifier = new Identifier(compoundTag_1.getString(" |
- | + | this.rotation = Rotation.valueOf(compoundTag_1.getString(" | |
- | @Override | + | this.setStructureData(structureManager_1); |
- | protected void toNbt(CompoundTag compoundTag_1) { | + | } |
- | super.toNbt(compoundTag_1); | + | |
- | compoundTag_1.putString(" | + | @Override |
- | compoundTag_1.putString(" | + | protected void toNbt(CompoundTag compoundTag_1) { |
- | } | + | super.toNbt(compoundTag_1); |
+ | compoundTag_1.putString(" | ||
+ | compoundTag_1.putString(" | ||
+ | } | ||
+ | |||
+ | public void setStructureData(StructureManager structureManager) { | ||
+ | | ||
+ | StructurePlacementData structurePlacementData_1 = (new StructurePlacementData()).setRotation(this.rotation).setMirrored(Mirror.NONE).setPosition(pos).addProcessor(BlockIgnoreStructureProcessor.IGNORE_STRUCTURE_BLOCKS); | ||
+ | this.setStructureData(structure_1, | ||
+ | | ||
+ | |||
+ | @Override | ||
+ | protected void handleMetadata(String string_1, BlockPos blockPos_1, IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1) { | ||
| | ||
- | public void setStructureData(StructureManager structureManager) | ||
- | { | ||
- | Structure structure_1 = structureManager.getStructureOrBlank(this.identifier); | ||
- | StructurePlacementData structurePlacementData_1 = (new StructurePlacementData()).setRotation(this.rotation).setMirrored(Mirror.NONE).setPosition(pos).addProcessor(BlockIgnoreStructureProcessor.IGNORE_STRUCTURE_BLOCKS); | ||
- | this.setStructureData(structure_1, | ||
- | } | ||
- | | ||
- | @Override | ||
- | protected void handleMetadata(String string_1, BlockPos blockPos_1, IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1) { | ||
- | } | ||
- | | ||
- | @Override | ||
- | public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, | ||
- | { | ||
- | | ||
- | } | ||
} | } | ||
+ | | ||
+ | @Override | ||
+ | public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </ | ||
| | ||
handleMetadata is where you look at data blocks within your structure and do tasks based on what you find. In vanilla structures, data blocks are placed above chests so they can be filled with loot in this method. | handleMetadata is where you look at data blocks within your structure and do tasks based on what you find. In vanilla structures, data blocks are placed above chests so they can be filled with loot in this method. | ||
Line 111: | Line 128: | ||
We set the StructurePieceType to MyModClass.myStructurePiece type; this is the variable that holds your registered structure piece. We'll handle that after we finish the generate function, which sets the position of your structure and generates it: | We set the StructurePieceType to MyModClass.myStructurePiece type; this is the variable that holds your registered structure piece. We'll handle that after we finish the generate function, which sets the position of your structure and generates it: | ||
- | | + | <code java [enable_line_numbers=" |
- | public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, | + | @Override |
- | | + | public boolean generate(IWorld iWorld_1, Random random_1, MutableIntBoundingBox mutableIntBoundingBox_1, |
- | int yHeight = iWorld_1.getTop(Heightmap.Type.WORLD_SURFACE_WG, | + | int yHeight = iWorld_1.getTop(Heightmap.Type.WORLD_SURFACE_WG, |
- | this.pos = this.pos.add(0, | + | this.pos = this.pos.add(0, |
- | return | + | return super.generate(iWorld_1, |
- | } | + | } |
+ | </ | ||
| | ||
In this case, we simply get the y position of the highest block in the middle of our chunk and generate the structure off that position. | In this case, we simply get the y position of the highest block in the middle of our chunk and generate the structure off that position. | ||
+ | ==== Registering Features ==== | ||
The last step is to register our features. We're going to need to register: | The last step is to register our features. We're going to need to register: | ||
Line 131: | Line 150: | ||
Registering piece type: | Registering piece type: | ||
- | | + | <code java> |
+ | public static final StructurePieceType myStructurePieceType = Registry.register(Registry.STRUCTURE_PIECE, | ||
+ | </ | ||
| | ||
Registering feature: | Registering feature: | ||
- | | + | <code java> |
+ | public static final StructureFeature< | ||
+ | </ | ||
| | ||
Registering structure: | Registering structure: | ||
- | | + | <code java> |
+ | public static final StructureFeature<?> | ||
+ | </ | ||
| | ||
To put your feature in the features list, you can use: | To put your feature in the features list, you can use: | ||
- | | + | <code java> |
+ | Feature.STRUCTURES.put(" | ||
+ | </ | ||
| | ||
For testing, it's a good idea to register your feature to every biome and set the spawn rate to 100% so you can be sure it's spawning and working. You probably don't want your structure floating in the water, so we'll also filter that out. Add it to every biome by iterating over the biome list and adding it as a feature and generation step: | For testing, it's a good idea to register your feature to every biome and set the spawn rate to 100% so you can be sure it's spawning and working. You probably don't want your structure floating in the water, so we'll also filter that out. Add it to every biome by iterating over the biome list and adding it as a feature and generation step: | ||
- | | + | <code java [enable_line_numbers=" |
- | { | + | for(Biome biome : Registry.BIOME) { |
- | if(biome.getCategory() != Biome.Category.OCEAN && biome.getCategory() != Biome.Category.RIVER) | + | if(biome.getCategory() != Biome.Category.OCEAN && biome.getCategory() != Biome.Category.RIVER) { |
- | { | + | biome.addStructureFeature(myFeature.configure(FeatureConfig.DEFAULT)); |
- | biome.addStructureFeature(myFeature, new DefaultFeatureConfig()); | + | biome.addFeature(GenerationStep.Feature.SURFACE_STRUCTURES, |
- | biome.addFeature(GenerationStep.Feature.SURFACE_STRUCTURES, | + | } |
- | } | + | } |
- | } | + | </ |
ChanceDecoratorConfig' | ChanceDecoratorConfig' | ||
Line 157: | Line 184: | ||
You need to add your structure as a feature so your biome knows it exists, and then as a generation step so it's actually generated. | You need to add your structure as a feature so your biome knows it exists, and then as a generation step so it's actually generated. | ||
- | Load into your world, and if all went well, you should be met with a //lot// of Igloos. | + | Load into your world, and if all went well, you should be met with a //lot// of Igloos. |
- | + | ||
- | + | ||
- | + |
tutorial/structures.txt · Last modified: 2022/11/05 12:06 by jab125