Minecraft Modding: Custom Tree With Wood, Leaves and Sapling

Background


Note: This tutorial is part of Jabelar's Custom Dimension Tutorial.

Warning: Tree generation requires intermediate level of modding and Java experience. You should already be comfortable making custom blocks that have variant properties with corresponding models and be fairly solid in Java concepts such as overriding methods and abstract and anonymous classes.

Tree generation occurs in two ways: natural generation as part of a biome or through cultivation using a sapling. There are actually quite a few different tree types in Minecraft, with varying height and complexity (some have branches some don't). In this tutorial I base the code off of a simple oak-like tree generation; however it should be fairly easy to adapt to the other types if you wish.

Key Point: In modding, you often have to decide whether you want to extend an existing class or to copy it. Extending can be nice if the vanilla code then handles your child class like the parent. However, unfortunately in most cases Minecraft was not coded in a very object-oriented friendly way and so there are many places where vanilla code uses == rather than instanceof for comparisons. Therefore, in many cases you need to copy the class instead. That is what I do in this tutorial.

Important: in 1.12 and earlier saplings, wood ("logs") and leaves all have a property called VARIANT that uses the BlockPlanks enumerated type values. E.g. oak, acacia, and such. However, it is difficult to extend a vanilla enum so you will not be able to use the same type. Rather you need to either create your own type property or you can choose to use a "flat" method of creating separate blocks for each type. In 1.13, they are moving to a flat approach (this effort is even called "The Flattening") and that is what I use here. This implies that I remove the use of the VARIANT property from my copy of the related blocks and adjust all the related methods accordingly.

The Recommended Approach


Generally to make a custom tree you need to do the following steps:
  1. Create log: Create a custom log block class that is properly registered with model, textures, lang file, associated ItemBlock etc. See Jabelar's Custom Log Tutorial.
  2. Create leaves: Create a custom leaves block class that is properly registered with model, textures, lang file, associated ItemBlock etc. See Jabelar's Custom Leaves Tutorial.
  3. Create sapling: Create a custom sapling block class that is properly registered with model, textures, lang file, associated ItemBlock etc. See below.
  4. Create tree generator: Create a custom world generator class that extends WorldGenAbstractTree to generate your custom tree that uses your custom log from Step #1 and your custom leaves from Step #2. See below.
  5. Tie it all together:
    • Make sure your custom sapling uses the world generator from Step #4.
    • Make sure your custom leaves class drops the sapling from Step #3.
  6. Associate with biome(s). Add your world generator to either vanilla or your custom biome's (not implemented here) tree feature methods.

Example Code


Everything I describe here is implemented for my "cloud" biome in Jabelar's Example Mod on GitHub. You are free to copy my code; however I strongly request you try to understand it thoroughly.

Create Custom Sapling Block


A sapling is a block that is IPlantable (extenda BlockBush) and IGrowable. It has two stages of growth represented by a STAGE property. These stages do not change the model or texture so there is no visible difference. 

Tip: Look at the vanilla BlockSapling class to understand how to implement equivalent methods. However, I suggest you do NOT implement the TYPE property like vanilla but instead move to the flat approach with a separate class per type.

Basically, the approach to simply create a class that extends BlockBush and implements IGrowable while handling the STAGE property. The recommended steps for creating custom blocks are:
  1. Create class: Create a custom class that extends BlockBush and implements IGrowable and:
    • Implement all the IGrowable methods such as grow(), canGrow() and canUseBoneMeal();
    • Implement a PropertyInteger for STAGE, including the getMetaFromState(), getStateFromMeta() and grow() methods.
    • Do the other block stuff such as setting the hardness, creative tab, step sound, etc. Copy the values from other saplings unless you want to change them for some reason.
    • Example code
  2. Registration Of Block, ItemBlock and Related Models: Refer to official Forge documentation on registry with object holder approach.
    • Instantiate block singleton instance: Instantiate your block using @ObjectHolder annotation.
    • Register block: Handle the RegistryEvent.Register event to register your block instance.
    • Register block model: Handle the ModelRegistryEvent to register your block model.
    • Instantiate ItemBlock singleton instance: Instantiate your related ItemBlock using the @ObjectHolder annotation.
    • Register ItemBlock: Handle the RegistryEvent.Register event to register your ItemBlock instance.
    • Register ItemBlock model: Handle the ModelRegistryEvent to register your ItemBlock model.
    • Example code
  3. Create and Organize Resource Assets:
    • Blockstate JSON: Make sure you have a proper blockstates JSON file in proper location and with name that matches the registry name. It needs to handle the variant values of the STATE property. 
    • Block model JSON: Make sure you have a proper model JSON file for your block in proper location and with name that matches the references in your blockstate file. 
    • ItemBlock model JSON: Make sure you have a proper model JSON file for your item in proper location and with name that matches the registry name in your assets.
    • Texture asset: Make sure you have a texture PNG asset in the proper location that matches the references in your model JSON files.
    • .lang files: Make sure your lang file(s) have localization for your block name.
    • Example asset files
Warning: You probably want your custom sapling to be usable as a fuel, just like vanilla saplings. To do this, you need to @Override the getItemBurnTime() method as an anonymous class when you instantiate the ItemBlock. For example, when you create the new instance it should be something like this:

     new ItemBlock(cloud_sapling) {
          @Override
          public int getItemBurnTime(ItemStack itemStack)
          {
               return 100; // same value as vanilla sapling
          }
     }

Create Custom World Generator Class


In vanilla there are a lot of different tree shapes. You may want to copy one or create an entirely new one. In my example, I copy a simple tree but it should be fairly straightforward to inspect the other vanilla tree gen source code and figure out how to apply it to other shapes.

Your custom world generator class can extend the WorldGenAbstractTree class and @Override the generate() method. I personally also break up the trunk and leaves generation into separate methods to make it more readable.

See example code.

Tie It All Together


Since a tree's leaves can drop a sapling and a sapling can grow a tree, you need to double-check that is all tied together:

  • Make sure in your custom leaves class to @Override the getItemDropped() method to drop your custom sapling.
  • Make sure in your custom sapling class to @Override the generateTree() method to:
    • Call the TerrainGen.SaplingGrowTree() method to fire the event.
    • Instantiate the world generator.
    • Call the world generator's generate() method

Associate Your Tree With A Biome


Next, you probably want your tree to grow in a biome. You can either:
  • Add it to an existing biome by handling the DecorateBiomeEvent.Decorate and checking for EventType.TREE and invoking your tree generation (and optionally canceling the vanilla tree generation by returning Result.DENY).
  • Add to a custom biome by adding a @Override to the getRandomTreeFeature() method.

Testing


Reminder to always test your code thoroughly. I suggest you test all of the following:
  • Check that your log block, leaves block and sapling block all show up properly in the Creative tabs.
  • Check that your log item, leaves item and sapling item all look correct when held in your player's hand.
  • Check that you can place the sapling block in plantable areas.
  • Check that your log block, leaves block and sapling all look correct when placed in the world.
  • Check that bonemeal can grow your sapling.
  • Check that the tree generated from your sapling generates correctly.
  • Check that breaking your leaves can drop the sapling and any other item you wanted.
  • Check that both your sapling and log can be placed in a furnace fuel slot.
  • If you added your tree generator to a biome, check that it naturally generates as expected.

Conclusion


Tree generation is a fairly involved process, but is an important part of any custom biome. So I hope this tutorial helped you achieve this advanced goal. As always, I enjoy your comments and happy modding!

1 comment:

  1. Thanks so much for the effort of sharing all this. I've been trying to understand how to make custom trees work and struggling with variants until I found your blog. Kudos, and thanks again!

    ReplyDelete