tutorial:features
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorial:features [2020/01/08 12:07] – lightcolour | tutorial:features [2023/12/18 01:19] (current) – [Adding a configured feature to a biome] update code solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== 在您的世界中生成Feature | + | ===== Adding 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. In this tutorial, we' | + | 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. | ||
+ | In this tutorial, we' | ||
- | ==== Creating a Feature class ==== | + | 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. |
- | A simple Feature looks like this: | + | In that case, skip the "Creating a feature" |
- | <code java [enable_line_numbers="true"]> | + | |
- | public class StoneSpiralFeature extends Feature< | + | |
- | public StoneSpiralFeature(Function< | + | There are 3 steps that are required to add a feature to a biome. |
- | | + | * Create a feature |
- | } | + | * Configure a feature |
+ | * Use [[https:// | ||
- | @Override | ||
- | public boolean generate(IWorld world, ChunkGenerator<? | ||
- | BlockPos topPos = world.getTopPosition(Heightmap.Type.WORLD_SURFACE, | ||
- | Direction offset = Direction.NORTH; | ||
- | | + | ==== Creating a feature ==== |
- | | + | 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, |
- | world.setBlockState(topPos.up(y).offset(offset), Blocks.STONE.getDefaultState(), 3); | + | |
+ | We'll first create a new class for our feature. This is where the generation logic will go. | ||
+ | |||
+ | < | ||
+ | public class ExampleFeature extends class_3031< | ||
+ | public ExampleFeature(Codec< | ||
+ | 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 | ||
+ | public boolean method_13151(class_5821< | ||
+ | class_5281 world = context.method_34383(); | ||
+ | // 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(); | ||
+ | ExampleFeatureConfig config = context.method_33656(); | ||
+ | |||
+ | // don't worry about where these come from-- we'll implement these methods soon | ||
+ | int number = config.number(); | ||
+ | class_2960 blockId = config.blockId(); | ||
+ | |||
+ | 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!" | ||
+ | |||
+ | // find the surface of the world | ||
+ | class_2338 testPos = new class_2338(origin); | ||
+ | | ||
+ | | ||
+ | | ||
+ | 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 a simple pillar of blocks | ||
+ | world.method_8501(testPos, blockState, 0x10); | ||
+ | testPos = testPos.method_10086(); | ||
+ | |||
+ | // ensure we don't try to place blocks outside the world | ||
+ | if (testPos.getY() >= world.method_8624()) break; | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
} | } | ||
+ | // the game couldn' | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
- | return true; | + | </ |
+ | Now, we need to implement that '' | ||
+ | < | ||
+ | public record ExampleFeatureConfig(int number, Identifier blockId) implements FeatureConfig { | ||
+ | public static final Codec< | ||
+ | instance -> instance.group( | ||
+ | // you can add as many of these as you want, one for each parameter | ||
+ | Codecs.POSITIVE_INT.fieldOf(" | ||
+ | Identifier.CODEC.fieldOf(" | ||
+ | .apply(instance, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 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> | ||
+ | public class ExampleMod implements ModInitializer { | ||
+ | public static final Identifier EXAMPLE_FEATURE_ID = new Identifier(" | ||
+ | public static final ExampleFeature EXAMPLE_FEATURE = new ExampleFeature(ExampleFeatureConfig.CODEC); | ||
+ | |||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | Registry.register(Registries.FEATURE, | ||
} | } | ||
} | } | ||
</ | </ | ||
- | The constructor takes in a '' | + | If you plan to configure and use your feature using datapacks, you can stop here. To implement it in code, read on. |
- | `generate` is called when the chunk decides | + | ==== 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 | ||
- | In our implementation, | + | < |
+ | public class FeatureExampleMod implements ModInitializer { | ||
- | <code java> | + | |
- | @Override | + | |
- | public | + | |
- | | + | |
- | Direction offset = Direction.NORTH; | + | |
- | | + | |
- | offset = offset.rotateYClockwise(); | + | |
- | | + | new ExampleFeatureConfig(10, new Identifier(" |
- | | + | |
- | return true; | + | |
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | Registry.register(Registry.FEATURE, | ||
+ | Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, | ||
+ | } | ||
} | } | ||
- | </code> | + | </yarncode> |
- | ==== 注册一个Feature ==== | + | FIXME The last line has to be updated |
- | 可以像注册游戏中的其他大多数内容一样注册Feature,而且您不必担心任何特殊的构建器或机制。 | + | |
- | <code java> | + | |
- | private static final Feature< | + | |
- | Registry.FEATURE, | + | |
- | new Identifier(" | + | |
- | new StoneSpiralFeature(DefaultFeatureConfig:: | + | |
- | ); | + | |
- | </ | + | |
- | ==== 向生物群落添加Feature | + | ==== Adding a configured feature to a biome ==== |
- | 生物群系有一种称为'' | + | We use the Biome Modification API. The final stage of feature configuration is creating a '' |
- | 我们可以遍历'' | + | Our final initializer class looks like this: |
- | <code java> | + | <yarncode |
- | Registry.BIOME.forEach(biome | + | public class ExampleMod implements ModInitializer { |
- | | + | |
- | Biome.configureFeature( | + | public static final Identifier EXAMPLE_FEATURE_ID = new Identifier(" |
- | LAVA_HOLE, | + | public static final ExampleFeature EXAMPLE_FEATURE = new ExampleFeature(ExampleFeatureConfig.CODEC); |
- | new DefaultFeatureConfig(), | + | public static final ConfiguredFeature< |
- | Decorator.CHANCE_HEIGHTMAP, | + | EXAMPLE_FEATURE, |
- | new ChanceDecoratorConfig(100) | + | new ExampleFeatureConfig(10, |
- | ) | + | ); |
- | )); | + | // our PlacedFeature. this is what gets passed to the biome modification API to add to the biome. |
- | </code> | + | 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()) | ||
+ | ); | ||
+ | |||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | | ||
+ | Registry.register(class_7923.field_41144, EXAMPLE_FEATURE_ID, | ||
+ | |||
+ | // add it to overworld biomes using FAPI | ||
+ | BiomeModifications.addFeature( | ||
+ | | ||
+ | // the feature is to be added while flowers and trees are being generated | ||
+ | GenerationStep.Feature.VEGETAL_DECORATION, | ||
+ | | ||
+ | } | ||
+ | } | ||
+ | </yarncode> | ||
+ | |||
+ | The first argument of '' | ||
+ | |||
+ | The second argument helps determine when the structure is generated. | ||
+ | For above-ground houses you may go with '' | ||
- | The first argument of '' | + | For more information, |
- | The second argument of '' | ||
- | The Decorator represents how the world chooses to place your Feature. '' | ||
- | === 结果 === | ||
- | {{https:// |
tutorial/features.txt · Last modified: 2023/12/18 01:19 by solidblock