User Tools

Site Tools


tutorial:features

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
tutorial:features [2020/04/23 18:53]
2xsaiko [Adding a Feature to a Biome] Update feature configuration
tutorial:features [2020/09/06 10:42] (current)
siglong minor improvement
Line 1: Line 1:
-===== Generating ​Features ​in your World ===== +===== 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'll look at generating a simple stone spiral feature in our world randomly.+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'll look at generating a simple stone spiral feature in the plain biomes ​randomly.
  
-==== Creating a Feature class ====+There are 3 steps that are required to add a feature to a biome. 
 +  * Create a feature 
 +  * Configure a feature 
 +  * Add a configured feature to a biome 
 + 
 +==== Creating a feature ​====
 A simple Feature looks like this: A simple Feature looks like this:
-<code java [enable_line_numbers="​true"​]>+ 
 +<code java>
 public class StoneSpiralFeature extends Feature<​DefaultFeatureConfig>​ { public class StoneSpiralFeature extends Feature<​DefaultFeatureConfig>​ {
 +  public StoneSpiralFeature(Codec<​DefaultFeatureConfig>​ config) {
 +    super(config);​
 +  }
  
-    ​public ​StoneSpiralFeature(Function<​Dynamic<?>​? extends ​DefaultFeatureConfigconfig) { +  @Override 
-        super(config); +  ​public ​boolean generate(StructureWorldAccess worldChunkGenerator generator, Random random, BlockPos pos, 
-    ​}+      ​DefaultFeatureConfig config) { 
 +    ​BlockPos topPos = world.getTopPosition(Heightmap.Type.WORLD_SURFACE,​ pos); 
 +    ​Direction offset = Direction.NORTH;​
  
-    ​@Override +    for (int y = 1; y <= 15; y++) { 
-    public boolean generate(IWorld world, ChunkGenerator<?​ extends ChunkGeneratorConfig>​ chunkGenerator,​ Random random, BlockPos pos, DefaultFeatureConfig config) { +      offset = offset.rotateYClockwise();​ 
-        BlockPos topPos = world.getTopPosition(Heightmap.Type.WORLD_SURFACE,​ pos); +      world.setBlockState(topPos.up(y).offset(offset),​ Blocks.STONE.getDefaultState(),​ 3);
-        Direction offset = Direction.NORTH;​ +
- +
-        ​for (int y = 1; y < 16; y++) { +
-            offset = offset.rotateYClockwise();​ +
-            world.setBlockState(topPos.up(y).offset(offset),​ Blocks.STONE.getDefaultState(),​ 3)+
-        } +
- +
-        return true;+
     }     }
 +
 +    return true;
 +  }
 } }
 </​code>​ </​code>​
  
-The constructor takes in a ''​Function<Dynamic<?​ extends ​DefaultFeatureConfig>>''​, which is a factory for data fixer config instances. You can pass in ''​DefaultFeatureConfig::​deserialize''​ for default config features, either directly in the super call or when you instantiate the feature.+The ''​Feature<​DefaultFeatureConfig>'' ​constructor takes in a ''​Codec<​DefaultFeatureConfig>''​. 
 +You can pass in ''​DefaultFeatureConfig.CODEC''​ for default config features, either directly in the super call in the constructor ​or when you instantiate the feature.
  
-`generateis called when the chunk decides to generate the feature. 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, ​`generatewould only be called in instances where the world wants to spawn the structure. ​+''​generate'' ​is called when the chunk decides to generate the feature. ​ 
 +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, ​''​generate'' ​would only be called in instances where the world wants to spawn the structure. ​
  
-In our implementation,​ we'll build a simple ​16-block tall spiral of stone starting at the top block in the world:+In our implementation,​ we'll build a simple ​15-block tall spiral of stone starting at the top block in the world
 + 
 +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>
-@Override +public class ExampleMod implements ModInitializer { 
-public boolean generate(IWorld world, ChunkGenerator<? extends ChunkGeneratorConfigchunkGenerator,​ Random random, BlockPos pos, DefaultFeatureConfig config) { +  ​private static final Feature<DefaultFeatureConfigSTONE_SPIRAL ​new StoneSpiralFeature(DefaultFeatureConfig.CODEC);
-    BlockPos topPos ​world.getTopPosition(Heightmap.Type.WORLD_SURFACE,​ pos)+
-    Direction offset = Direction.NORTH;+
  
-    for (int y = 1; y < 16; y++) { +  @Override 
-        ​offset = offset.rotateYClockwise(); +  public void onInitialize() { 
-        world.setBlockState(topPos.up(y).offset(offset)Blocks.STONE.getDefaultState(), 3); +    ​Registry.register(Registry.FEATURE, new Identifier("​tutorial"​"​stone_spiral"​), STONE_SPIRAL); 
-    } +  }
- +
-    return true;+
 } }
 </​code>​ </​code>​
  
-==== Registering ​Feature ​==== +==== Configuring ​feature ​==== 
-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+We need to give a configuration to a featureMake sure to register configured feature as well as feature. 
 <code java> <code java>
-private ​static final Feature<DefaultFeatureConfigLAVA_HOLE ​Registry.register+public class ExampleMod implements ModInitializer { 
- Registry.FEATURE, +  public ​static final ConfiguredFeature<?, ?STONE_SPIRAL_CONFIGURED ​STONE_SPIRAL.configure(FeatureConfig.DEFAULT) 
- new Identifier("​tutorial",​ "​stone_spiral"​),​ +      ​.decorate(Decorator.CHANCE.configure(new ChanceDecoratorConfig(100)));​ 
- new StoneSpiralFeature(DefaultFeatureConfig::​deserialize) + 
-);+  @Override 
 +  public void onInitialize() { 
 +    [...] 
 +     
 +    ​Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, new Identifier("​tutorial",​ "​stone_spiral"​), ​STONE_SPIRAL_CONFIGURED); 
 +  } 
 +}
 </​code>​ </​code>​
  
-==== Adding a Feature ​to a Biome ==== +The Decorator represents how the world chooses to place your feature. 
-Biome has a method called ​''​addFeature''​, which is used to add Features ​to the biome'​s generation process. You can view more detailed usage of this method inside each Biome class (such as ''​ForestBiome''​ or ''​SavannaBiome''​).+To choose the correct Decorator, check out vanilla features with a similar style to your own. 
 +The decorator config branches off this; in the case of ''​CHANCE'',​ you would pass in an instance of ''​ChanceDecoratorConfig''​.  
 + 
 +==== Adding a configured feature ​to a biome ==== 
 +Vanilla features generated in the plain biomes are listed in ''​DefaultBiomeFeatures.addPlainsFeatures''​. We modify this method ​to add our feature ​to the plain biomes.
  
-We can iterate over ''​Registry.BIOME''​ to add our Feature to every Biome. 
 <code java> <code java>
-Registry.BIOME.forEach(biome -> biome.addFeature( +@Mixin(DefaultBiomeFeatures.class) 
-        ​GenerationStep.Feature.RAW_GENERATION,​ +public class DefaultBiomeFeaturesMixin { 
- LAVA_HOLE.configure(new DefaultFeatureConfig()) +  ​@Inject(method = "​addPlainsFeatures(Lnet/​minecraft/​world/​biome/​GenerationSettings$Builder;​)V",​ at = @At("​TAIL"​)) 
- .createDecoratedFeature(Decorator.CHANCE_HEIGHTMAP.configure(new ChanceDecoratorConfig(100))); +  ​private static void addPlainsFeatures(GenerationSettings.Builder builder, CallbackInfo ci) { 
-));+    builder.feature(GenerationStep.Feature.TOP_LAYER_MODIFICATION,​ ExampleMod.STONE_SPIRAL_CONFIGURED); 
 +  } 
 +}
 </​code>​ </​code>​
  
-The first argument of ''​addFeature''​ helps determine when the structure is generated. For above-ground houses you may go with ''​SURFACE_STRUCTURES'',​ and for caves, you might go with ''​RAW_GENERATION''​.+The first argument of ''​feature''​ helps determine when the structure is generated. 
 +For above-ground houses you may go with ''​SURFACE_STRUCTURES'',​ and for caves, you might go with ''​RAW_GENERATION''​.
  
-The second argument of ''​addFeature''​ is a ConfiguredFeature,​ which you can create through ''​Biome.configureFeature''​The latter takes in an instance of your feature, an instance of your feature'​s config class, a decorator, and a decorator config.+=== Result === 
 +{{https://i.imgur.com/Kr59o0B.png}}
  
-The Decorator represents how the world chooses to place your Feature. ''​CHANCE_HEIGHTMAP''​ works by looking at a heightmap, whereas ''​NOISE_HEIGHTMAP_32''​ works with noise. To choose the correct Decorator, check out vanilla Features with a similar style to your own. The decorator config branches off this; in the case of ''​CHANCE_HEIGHTMAP'',​ you would pass in an instance of ''​ChanceHeightmapDecorator''​. ​ 
- 
-=== Results === 
-{{https://​i.imgur.com/​Kr59o0B.png}} 
tutorial/features.1587668001.txt.gz · Last modified: 2020/04/23 18:53 by 2xsaiko