User Tools

Site Tools


tutorial:trees

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:trees [2021/08/28 23:06] – Changed word „Feature“ to link to the Feature article arbeetutorial:trees [2022/12/21 01:40] (current) – 1.19.3 haykam
Line 1: Line 1:
-===== Adding Trees [1.17] (Advanced) =====+===== Adding Trees [1.19.2] (Advanced) =====
 It is recommended that you learn how to create a [[tutorial:features|Feature]] in Minecraft first before reading this tutorial.\\ It is recommended that you learn how to create a [[tutorial:features|Feature]] in Minecraft first before reading this tutorial.\\
  
 Trees are a great way to expand Minecraft's world generation in your mod.\\ Trees are a great way to expand Minecraft's world generation in your mod.\\
 Beware that this topic is advanced and preferably you should have decent experience with modding world generation in Minecraft before starting. Beware that this topic is advanced and preferably you should have decent experience with modding world generation in Minecraft before starting.
 +
 +Firstly, you need to understand that a ''PlacedFeature'' is what gets placed in the world. There are a few steps to retrieve a tree placed feature:
 +You need a feature, in our case ''Feature.TREE'', we configure it with ''TreeFeatureConfig'', make a placed feature, make a configured feature and finally a placed feature (This is the placed feature that gets placed in the world)
  
 ===== Creating a Simple Tree ===== ===== Creating a Simple Tree =====
  
 ==== Architecture ==== ==== Architecture ====
-Minecraft's tree architecture is split into different classes to allow for very complex and beautiful trees.\\+Minecraft's tree configuration architecture is split into different classes to allow for very complex and beautiful trees.\\
 Here's an overview: Here's an overview:
  
Line 21: Line 24:
 ==== Creating the ConfiguredFeature ==== ==== Creating the ConfiguredFeature ====
 We don't need to create a new ''Feature'', as the vanilla ''TreeFeature'' is configurable. We don't need to create a new ''Feature'', as the vanilla ''TreeFeature'' is configurable.
-Add this into your ''ModInitializer'''s body:+Add this into your ''ModInitializer'''(This could be your content initializer if you prefer) body:
  
 <code java> <code java>
-public static final ConfiguredFeature<?, ?> TREE_RICH = Feature.TREE+public static final RegistryEntry<ConfiguredFeature<TreeFeatureConfig, ?>> TREE_RICH = ConfiguredFeatures.register("tutorial:tree_rich", Feature.TREE
   // Configure the feature using the builder   // Configure the feature using the builder
-  .configure(new TreeFeatureConfig.Builder( +  new TreeFeatureConfig.Builder( 
-    new SimpleBlockStateProvider(Blocks.NETHERITE_BLOCK.getDefaultState()), // Trunk block provider+    BlockStateProvider.of(Blocks.NETHERITE_BLOCK), // Trunk block provider
     new StraightTrunkPlacer(8, 3, 0), // places a straight trunk     new StraightTrunkPlacer(8, 3, 0), // places a straight trunk
-    new SimpleBlockStateProvider(Blocks.DIAMOND_BLOCK.getDefaultState()), // Foliage block provider +    BlockStateProvider.of(Blocks.DIAMOND_BLOCK), // Foliage block provider
-    new SimpleBlockStateProvider(RICH_SAPLING.getDefaultState()), // Sapling provider; used to determine what blocks the tree can generate on+
     new BlobFoliagePlacer(ConstantIntProvider.create(5), ConstantIntProvider.create(0), 3), // places leaves as a blob (radius, offset from trunk, height)     new BlobFoliagePlacer(ConstantIntProvider.create(5), ConstantIntProvider.create(0), 3), // places leaves as a blob (radius, offset from trunk, height)
     new TwoLayersFeatureSize(1, 0, 1) // The width of the tree at different layers; used to see how tall the tree can be without clipping into blocks     new TwoLayersFeatureSize(1, 0, 1) // The width of the tree at different layers; used to see how tall the tree can be without clipping into blocks
-  ).build()) +  ).build()));
-  .spreadHorizontally() +
-  .applyChance(3); // About a 33% chance to generate per chunk (1/x) +
-</code> +
- +
-Now we just register the ''ConfiguredFeature'' to the game like normal and make the biome modification using Fabric's API: +
- +
-<code java> +
-@Override +
-public void onInitialize() { +
-  RegistryKey<ConfiguredFeature<?, ?>> treeRich = RegistryKey.of(Registry.CONFIGURED_FEATURE_KEY, new Identifier("tutorial", "tree_rich")); +
- +
-  Registry.register(BuiltinRegistries.CONFIGURED_FEATURE, treeRich.getValue(), TREE_RICH); +
- +
-  // You should use the VEGETAL_DECORATION generation step for trees +
-  BiomeModifications.addFeature(BiomeSelectors.foundInOverworld(), GenerationStep.Feature.VEGETAL_DECORATION, treeRich); +
-}+
 </code> </code>
  
Line 60: Line 46:
 <code java> <code java>
 public class RichSaplingGenerator extends SaplingGenerator { public class RichSaplingGenerator extends SaplingGenerator {
-  private final ConfiguredFeature<TreeFeatureConfig, ?> feature; 
- 
-  public RichSaplingGenerator(ConfiguredFeature<?, ?> feature) { 
-    this.feature = (ConfiguredFeature<TreeFeatureConfig, ?>) feature; 
-  } 
- 
   @Nullable   @Nullable
   @Override   @Override
-  protected ConfiguredFeature<TreeFeatureConfig, ?> getTreeFeature(Random random, boolean bees) { +  protected RegistryEntry<ConfiguredFeature<TreeFeatureConfig, ?>> getTreeFeature(Random random, boolean bees) { 
-    return feature;+    return Tutorial.TREE_RICH;
   }   }
 } }
Line 93: Line 73:
  
 <code java> <code java>
-public static final RICH_SAPLING = new RichSaplingBlock(new RichSaplingGenerator(TREE_RICH), FabricBlockSettings.copyOf(Blocks.OAK_SAPLING.getDefaultState()));+public static final RichSaplingBlock RICH_SAPLING = new RichSaplingBlock(new RichSaplingGenerator(TREE_RICH), FabricBlockSettings.copyOf(Blocks.OAK_SAPLING));
  
 public static void register() { public static void register() {
-  Registry.register(Registry.BLOCK, new Identifier("tutorial", "rich_sapling"), RICH_SAPLING); +  Registry.register(Registries.BLOCK, new Identifier("tutorial", "rich_sapling"), RICH_SAPLING); 
-  Registry.register(Registry.ITEM, new Identifier("tutorial", "rich_sapling"), new BlockItem(RICH_SAPLING, ItemGroup.MISC));+  Registry.register(Registries.ITEM, new Identifier("tutorial", "rich_sapling"), new BlockItem(RICH_SAPLING, new FabricItemSettings()));
 } }
  
Line 128: Line 108:
 @Mixin(TrunkPlacerType.class) @Mixin(TrunkPlacerType.class)
 public interface TrunkPlacerTypeInvoker { public interface TrunkPlacerTypeInvoker {
-    @Invoker+    @Invoker("register")
     static <P extends TrunkPlacer> TrunkPlacerType<P> callRegister(String id, Codec<P> codec) {     static <P extends TrunkPlacer> TrunkPlacerType<P> callRegister(String id, Codec<P> codec) {
         throw new IllegalStateException();         throw new IllegalStateException();
Line 162: Line 142:
     public List<FoliagePlacer.TreeNode> generate(TestableWorld world, BiConsumer<BlockPos, BlockState> replacer, Random random, int height, BlockPos startPos, TreeFeatureConfig config) {     public List<FoliagePlacer.TreeNode> generate(TestableWorld world, BiConsumer<BlockPos, BlockState> replacer, Random random, int height, BlockPos startPos, TreeFeatureConfig config) {
         // Set the ground beneath the trunk to dirt         // Set the ground beneath the trunk to dirt
-        this.setToDirt(world, replacer, random, startPos.down(), config);+       setToDirt(world, replacer, random, startPos.down(), config);
                  
         // Iterate until the trunk height limit and place two blocks using the getAndSetState method from TrunkPlacer         // Iterate until the trunk height limit and place two blocks using the getAndSetState method from TrunkPlacer
Line 183: Line 163:
  
 <code java> <code java>
-public static final TrunkPlacerType<RichTrunkPlacer> RICH_TRUNK_PLACER = TrunkPlacerTypeInvoker.callRegister("rich_trunk_placer", RichTrunkPlacer.CODEC);+public static final TrunkPlacerType<RichTrunkPlacer> RICH_TRUNK_PLACER = TrunkPlacerTypeInvoker.callRegister("tutorial:rich_trunk_placer", RichTrunkPlacer.CODEC);
 </code> </code>
  
Line 240: Line 220:
     public static final Codec<RichFoliagePlacer> CODEC = RecordCodecBuilder.create(instance ->     public static final Codec<RichFoliagePlacer> CODEC = RecordCodecBuilder.create(instance ->
         fillFoliagePlacerFields(instance)         fillFoliagePlacerFields(instance)
-        .and(IntProvider.createValidatingCodec(1, 512).fieldOf("foliage_height").forGetter(RichFoliagePlacer::getFoliageHeight)+        .and(IntProvider.createValidatingCodec(1, 512).fieldOf("foliage_height").forGetter(RichFoliagePlacer::getFoliageHeight))
         .apply(instance, RichFoliagePlacer::new));         .apply(instance, RichFoliagePlacer::new));
  
Line 297: Line 277:
  
 <code java> <code java>
-public static final FoliagePlacerType<RichFoliagePlacer> RICH_FOLIAGE_PLACER = FoliagePlacerTypeInvoker.callRegister("rich_foliage_placer", RichFoliagePlacer.CODEC);+public static final FoliagePlacerType<RichFoliagePlacer> RICH_FOLIAGE_PLACER = FoliagePlacerTypeInvoker.callRegister("tutorial:rich_foliage_placer", RichFoliagePlacer.CODEC);
 </code> </code>
  
Line 349: Line 329:
     // Our constructor doesn't have any arguments, so we create a unit codec that returns the singleton instance     // Our constructor doesn't have any arguments, so we create a unit codec that returns the singleton instance
     public static final Codec<RichTreeDecorator> CODEC = Codec.unit(() -> INSTANCE);     public static final Codec<RichTreeDecorator> CODEC = Codec.unit(() -> INSTANCE);
 +    
 +    private RichTreeDecorator() {}
  
     @Override     @Override
Line 356: Line 338:
  
     @Override     @Override
-    public void generate(TestableWorld world, BiConsumer<BlockPos, BlockState> replacer, Random random, List<BlockPos> logPositions, List<BlockPos> leavesPositions) {+    public void generate(TreeDecorator.Generator generator) {
         // Iterate through block positions         // Iterate through block positions
-        for (BlockPos logPosition : logPositions) {+        generator.getLogPositions().forEach(pos -> { 
 +            Random random = generator.getRandom();
             // Pick a value from 0 (inclusive) to 4 (exclusive) and if it's 0, continue             // Pick a value from 0 (inclusive) to 4 (exclusive) and if it's 0, continue
             // This is the chance for spawning the gold block             // This is the chance for spawning the gold block
Line 379: Line 362:
                 replacer.accept(targetPosition, Blocks.GOLD_BLOCK.getDefaultState());                 replacer.accept(targetPosition, Blocks.GOLD_BLOCK.getDefaultState());
             }             }
-        }+        });
     }     }
 } }
Line 388: Line 371:
  
 <code java> <code java>
-public static final TreeDecoratorType<RichTreeDecorator> RICH_TREE_DECORATOR = TreeDecoratorTypeInvoker.callRegister("rich_tree_decorator", RichTreeDecorator.CODEC);+public static final TreeDecoratorType<RichTreeDecorator> RICH_TREE_DECORATOR = TreeDecoratorTypeInvoker.callRegister("tutorial:rich_tree_decorator", RichTreeDecorator.CODEC);
 </code> </code>
  
Line 405: Line 388:
 <code java> <code java>
 public class RichSaplingGenerator extends SaplingGenerator { public class RichSaplingGenerator extends SaplingGenerator {
-    private final ConfiguredFeature<TreeFeatureConfig, ?> feature; 
- 
-    public RichSaplingGenerator(ConfiguredFeature<?, ?> feature) { 
-        this.feature = (ConfiguredFeature<TreeFeatureConfig, ?>) feature; 
-    } 
- 
     @Nullable     @Nullable
     @Override     @Override
-    protected ConfiguredFeature<TreeFeatureConfig, ?> getTreeFeature(Random random, boolean bees) {+    protected RegistryEntry<ConfiguredFeature<TreeFeatureConfig, ?>> getTreeFeature(Random random, boolean bees) {
         int chance = random.nextInt(100);         int chance = random.nextInt(100);
                  
         // Each tree has a 10% chance         // Each tree has a 10% chance
-        if (chance < 10) { +        return switch (chance) { 
-            return ConfiguredFeatures.OAK; +          case 10 -> TreeConfiguredFeatures.OAK; 
-        } else if (chance < 20) { +          case 20 -> TreeConfiguredFeatures.BIRCH; 
-            return ConfiguredFeatures.BIRCH; +          case 30 -> TreeConfiguredFeatures.MEGA_SPRUCE
-        } else if (chance < 60) { +          case 40 -> TreeConfiguredFeatures.PINE
-            return ConfiguredFeatures.SPRUCE+          case 50 -> TreeConfiguredFeatures.MEGA_PINE
-        } else if (chance < 40) { +          case 60 -> TreeConfiguredFeatures.MEGA_JUNGLE_TREE
-            return ConfiguredFeatures.MEGA_SPRUCE+          default -> Tutorial.RICH
-        } else if (chance < 50) { +
-            return ConfiguredFeatures.PINE+
-        } else if (chance < 60) { +
-            return ConfiguredFeatures.MEGA_PINE+
-        } else if (chance < 70) { +
-            return ConfiguredFeatures.MEGA_JUNGLE_TREE;+
         }         }
-         +   }
-        // If none of that happened (the random value was between 70 and 100), create the actual tree +
-        return feature; +
-    }+
 } }
 </code> </code>
Line 450: Line 418:
 <code java> <code java>
 [...] [...]
-.dirtProvider(new SimpleBlockStateProvider(Blocks.IRON_BLOCK.getDefaultState()))+.dirtProvider(BlockStateProvider.of(Blocks.IRON_BLOCK))
 [...] [...]
 </code> </code>
tutorial/trees.1630191992.txt.gz · Last modified: 2021/08/28 23:06 by arbee