Minecraft Forestry

We can build very simple trees programmatically in Minecraft by stacking blocks together using wood-y and leaf-y materials.

simple tree

import mcpi.minecraft as minecraft
import mcpi.block as block
import random
import time


def build_trunk(world, x, y, z, trunk_height, trunk_radius, trunk_material):
  if trunk_radius == 0:
    world.setBlocks(x, y, z, x + 1, y + trunk_height, z + 1, trunk_material)
  else:
    world.setBlocks(x - trunk_radius, 
                    y,
                    z - trunk_radius,
                    x + trunk_radius,
                    y + trunk_height,
                    z + trunk_radius, 
                    trunk_material)


def generate_leaves(world, x, y, z, leaf_height, leaf_radius, leaf_material):
  world.setBlocks(x - leaf_radius, 
                  y, 
                  z - leaf_radius, 
                  x + leaf_radius, 
                  y + leaf_height, 
                  z + leaf_radius, 
                  leaf_material)


# function to build a simple tree using the given materials
def build_tree(world, x, y, z, trunk_height=5, trunk_radius = 1, leaf_height=3, leaf_radius=4, trunk_material=block.WOOD, leaf_material=block.LEAVES):
  build_trunk(world, x, y, z, trunk_height, trunk_radius, trunk_material)
  generate_leaves(world, x, y + trunk_height, z, leaf_height, leaf_radius, leaf_material)


world = minecraft.Minecraft.create()

# clear out the space
world.setBlocks(-128, 0, -128, 128, 40, 128, block.AIR)
world.setBlocks(-128, 1, -128, 128,  2, 128, block.GRASS)

playerTile = world.player.getTilePos()

x = playerTile.x + 5
z = playerTile.z + 5
y = world.getHeight(x, z)

build_tree(world, x, y, z)

Square blocks of leaves aren't very realistic so we can trim off the square edges like this.

better tree

def sand_off_corners(world, x1, y1, z1, x2, y2, z2):
  world.setBlock(x1, y1, z1, block.AIR)
  world.setBlock(x2, y1, z1, block.AIR)
  world.setBlock(x1, y1, z2, block.AIR)
  world.setBlock(x2, y1, z2, block.AIR)
  world.setBlock(x1, y2, z1, block.AIR)
  world.setBlock(x2, y2, z1, block.AIR)
  world.setBlock(x1, y2, z2, block.AIR)
  world.setBlock(x2, y2, z2, block.AIR)

def generate_leaves(world, x, y, z, leaf_height, leaf_radius, leaf_material):
  world.setBlocks(x - leaf_radius, 
                  y, 
                  z - leaf_radius, 
                  x + leaf_radius, 
                  y + leaf_height, 
                  z + leaf_radius, 
                  leaf_material)

  # knock out corners to make it slightly more realistic.
  sand_off_corners(world, x - leaf_radius, y, z - leaf_radius, x + leaf_radius, y + leaf_height, z + leaf_radius)


Finally, we can make ourselves a nice orchard or small forest using some random values for each tree:

forest of trees


for i in range(3):
  for j in range(2):
    tree_spacing = random.randint(8, 12)
    trunk_radius = random.randrange(1)
    trunk_height = random.randint(5, 20)
    leaf_height = random.randint(5, 15)
    leaf_radius = random.randint(trunk_radius * 4, trunk_radius * 10)
    
    build_tree(world, 
        x + (i * tree_spacing), 
        y,
        z + (j * tree_spacing), 
        trunk_height = trunk_height, 
        trunk_radius = trunk_radius, 
        leaf_height = leaf_height
        )

Note by using the ground value specific to each tree location, we can get a nice effect "planting" trees on a sloping hillside.

We can go further by changing the materials for each tree, the trunk and leaf materials, on a random basis. The setBlock function can take an extra argument for some block types. For wood and leaves, you are able to pick the exact kind of material you would like.

| value | Wood | |0 | Oak | |1 | Spruce | |2 | Birch |

| value | Leaves | | 1 | Oak | | 2 | Spruce | | 3 | Birch |

In the final version, the foliage still looks a bit too false as a single monolithic block of leaves. We can change the leaf generation to work on a probability of a leaf block being at any one position in the canopy.



def generate_leaves(world, x, y, z, leaf_height, leaf_radius, leaf_material):

  leaf_probability = random.randint(30, 65)
  
  for i in range(x - leaf_radius, x + leaf_radius + 1):
    for j in range(y, y + leaf_height):
      for k in range(z - leaf_radius, z + leaf_radius + 1):
        if random.randrange(100) < leaf_probability:
          world.setBlock(i, j, k, leaf_material)
  
  sand_off_corners(world, x - leaf_radius, y, z - leaf_radius, x + leaf_radius, y + leaf_height, z + leaf_radius)

random leaves