tutorial:features
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorial:features [2021/08/31 13:21] – Edited sentence about decorators mschae23 | tutorial:features [2024/10/28 17:58] (current) – Fix new identifier vs Identifier.of cassiancc | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Adding Features | + | ===== Adding Features ===== |
Rocks, trees, ores, and ponds are all examples of features. | Rocks, trees, ores, and ponds are all examples of features. | ||
They are simple generation additions to the world which generate depending on how they are configured. | They are simple generation additions to the world which generate depending on how they are configured. | ||
- | In this tutorial, we' | + | In this tutorial, we' |
- | If you want to do something similar to vanilla (like ores), you should first look for an existing feature you can use. | + | If you want to do something similar to vanilla (like ores or flower patches), you should first look for an existing feature you can use. |
- | In that case, skip the "Create | + | In that case, skip the "Creating |
There are 3 steps that are required to add a feature to a biome. | There are 3 steps that are required to add a feature to a biome. | ||
Line 12: | Line 12: | ||
* Use [[https:// | * Use [[https:// | ||
- | Note that the Biome Modification API is marked as experimental. If the API doesn' | ||
==== Creating a feature ==== | ==== Creating a feature ==== | ||
- | A simple Feature looks like this: | + | Let's create a feature that spawns a 1x1 pillar of blocks on the ground. As an added challenge, let's also make it configurable, |
+ | |||
+ | We'll first create a new class for our feature. This is where the generation logic will go. | ||
< | < | ||
- | public class StoneSpiralFeature | + | public class ExampleFeature |
- | public | + | public |
super(configCodec); | super(configCodec); | ||
} | } | ||
+ | // this method is what is called when the game tries to generate the feature. it is where the actual blocks get placed into the world. | ||
@Override | @Override | ||
- | public boolean method_13151(class_5821< | + | public boolean method_13151(class_5821< |
- | | + | |
- | | + | // the origin is the place where the game starts trying to place the feature |
+ | class_2338 origin = context.getOrigin(); | ||
+ | // we won't use the random here, but we could if we wanted to | ||
+ | class_5819 random = context.method_33654(); | ||
+ | | ||
- | for (int y = 0; y < 15; y++) { | + | // don't worry about where these come from-- we'll implement these methods soon |
- | offset = offset.method_10170(); | + | |
- | | + | |
- | } | + | |
- | return true; | + | class_2680 blockState = class_7923.field_41175.get(blockId).method_9564(); |
- | } | + | // ensure the ID is okay |
- | } | + | if (blockState == null) throw new IllegalStateException(blockId + " could not be parsed to a valid block identifier!" |
- | </yarncode> | + | |
- | In our implementation, we'll build a simple | + | // find the surface of the world |
+ | class_2338 testPos = new class_2338(origin); | ||
+ | for (int y = 0; y < world.method_31605(); | ||
+ | testPos = testPos.method_10086(); | ||
+ | // the tag name is dirt, but includes grass, mud, podzol, etc. | ||
+ | if (world.method_8320(testPos).isIn(class_3481.field_29822)) { | ||
+ | if (world.method_8320(testPos.method_10086()).isOf(class_2246.field_10124)) { | ||
+ | for (int i = 0; i < number; i++) { | ||
+ | // create | ||
+ | | ||
+ | testPos = testPos.method_10086(); | ||
- | The ''< | + | // ensure we don't try to place blocks outside the world |
- | You can pass in ''< | + | if (testPos.getY() |
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | // the game couldn't find a place to put the pillar | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
- | ''< | ||
- | If the feature is configured to spawn every chunk, this would be called for each chunk being generated as well. | ||
- | In the case of the feature being configured to spawn at a certain rate per biome, ''< | ||
- | |||
- | Our feature uses a ''< | ||
- | as that allows you to reuse it for different things, and also lets players change them through data packs if they want. | ||
- | A simple config for our feature could look like this: | ||
- | |||
- | < | ||
- | public record SpiralFeatureConfig(class_6017 height, class_4651 block) implements class_3037 { | ||
- | public static final Codec< | ||
- | class_6017.field_29946.fieldOf(" | ||
- | class_4651.field_24937.fieldOf(" | ||
- | ).apply(instance, | ||
- | } | ||
</ | </ | ||
- | + | Now, we need to implement that '' | |
- | Note that we use an '' | + | |
- | This is because they are more powerful | + | |
- | + | ||
- | Now, let's make our feature use the '' | + | |
< | < | ||
- | public | + | public |
- | public SpiralFeature(Codec< | + | public |
- | | + | |
- | } | + | // you can add as many of these as you want, one for each parameter |
- | + | | |
- | @Override | + | |
- | | + | .apply(instance, ExampleFeatureConfig:: |
- | class_2338 pos = context.method_33655(); | + | |
- | | + | |
- | + | ||
- | | + | |
- | int height = config.height().method_35008(context.method_33654()); | + | |
- | + | ||
- | for (int y = 0; y < height; y++) { | + | |
- | offset = offset.method_10170(); | + | |
- | class_2338 blockPos = pos.method_10086(y).method_10093(offset); | + | |
- | | + | |
- | | + | |
- | } | + | |
- | + | ||
- | return true; | + | |
- | } | + | |
} | } | ||
</ | </ | ||
- | Features can be registered like most other content in the game, and there aren't any special builders or mechanics you'll have to worry about. | + | Now that we have our config defined, the errors in our feature class will resolve. But we're not done yet-- now we need to add our feature to the game. Features can be registered like most other content in the game, and there aren't any special builders or mechanics you'll have to worry about. |
<code java> | <code java> | ||
public class ExampleMod implements ModInitializer { | public class ExampleMod implements ModInitializer { | ||
- | private | + | public |
+ | public static final ExampleFeature EXAMPLE_FEATURE | ||
- | | + | |
- | public void onInitialize() { | + | public void onInitialize() { |
- | Registry.register(Registry.FEATURE, | + | Registry.register(Registries.FEATURE, |
- | } | + | } |
} | } | ||
</ | </ | ||
+ | |||
+ | If you plan to configure and use your feature using datapacks, you can stop here. To implement it in code, read on. | ||
==== Configuring a feature ==== | ==== Configuring a feature ==== | ||
- | We need to give a configuration to a feature, before we can add it to biomes. Make sure to register configured features as well as features. | + | We need to give a configuration to a feature, before we can add it to biomes. Make sure to register configured features as well as features. Here is where we specify the parameters of our feature. We'll generate 10-block-high pillars out of netherite. In fact, we could register as many of these '' |
< | < | ||
- | public class ExampleMod | + | public class FeatureExampleMod |
- | public static final class_2975<?, | + | |
- | .method_30374(class_3284.HEIGHTMAP.method_23475(new HeightmapDecoratorConfig(Heightmap.Type.OCEAN_FLOOR_WG))) | + | |
- | .method_30371() | + | |
- | .method_30372(5); | + | |
- | @Override | + | |
- | | + | |
- | | + | |
- | + | | |
- | | + | EXAMPLE_FEATURE, |
- | new Identifier(" | + | new ExampleFeatureConfig(10, |
- | Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, stoneSpiral.getValue(), STONE_SPIRAL); | + | |
- | } | + | |
+ | |||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | | ||
+ | Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, EXAMPLE_FEATURE_ID, | ||
+ | } | ||
} | } | ||
- | </ | + | </ |
- | Note that the height of the spiral (15 blocks) and the block to place (stone) is now configurable here. | + | FIXME The last line has to be updated |
- | + | ||
- | The decorators (added with '' | + | |
- | To choose the correct decorators, check out vanilla features with a similar style to your own. | + | |
==== Adding a configured feature to a biome ==== | ==== Adding a configured feature to a biome ==== | ||
- | We use the Biome Modification API. | + | We use the Biome Modification API. The final stage of feature configuration is creating a '' |
- | <code java> | + | Our final initializer class looks like this: |
+ | <yarncode | ||
public class ExampleMod implements ModInitializer { | public class ExampleMod implements ModInitializer { | ||
- | [...] | ||
- | | + | public static final Identifier EXAMPLE_FEATURE_ID = Identifier.of(" |
- | public void onInitialize() { | + | public static final ExampleFeature EXAMPLE_FEATURE = new ExampleFeature(ExampleFeatureConfig.CODEC); |
- | [...] | + | public static final ConfiguredFeature< |
- | BiomeModifications.addFeature(BiomeSelectors.all(), GenerationStep.Feature.UNDERGROUND_ORES, stoneSpiral); | + | EXAMPLE_FEATURE, |
- | } | + | new ExampleFeatureConfig(10, |
+ | ); | ||
+ | // our PlacedFeature. this is what gets passed to the biome modification API to add to the biome. | ||
+ | public static PlacedFeature EXAMPLE_FEATURE_PLACED = new PlacedFeature( | ||
+ | RegistryEntry.of( | ||
+ | EXAMPLE_FEATURE_CONFIGURED | ||
+ | // the SquarePlacementModifier makes the feature generate a cluster of pillars each time | ||
+ | ), List.of(SquarePlacementModifier.of()) | ||
+ | ); | ||
+ | |||
+ | | ||
+ | public void onInitialize() { | ||
+ | // register the features | ||
+ | Registry.register(class_7923.field_41144, | ||
+ | |||
+ | // add it to overworld biomes using FAPI | ||
+ | | ||
+ | | ||
+ | // the feature is to be added while flowers and trees are being generated | ||
+ | | ||
+ | RegistryKey.of(RegistryKeys.PLACED_FEATURE, | ||
+ | } | ||
} | } | ||
- | </code> | + | </yarncode> |
The first argument of '' | The first argument of '' | ||
Line 150: | Line 161: | ||
For above-ground houses you may go with '' | For above-ground houses you may go with '' | ||
- | === Result === | + | For more information, |
- | {{https://i.imgur.com/Kr59o0B.png}} | + | |
tutorial/features.1630416062.txt.gz · Last modified: 2021/08/31 13:21 by mschae23