top of page
  • Facebook
  • Twitter
  • Instagram

Tree Generator

Video on this: https://youtu.be/Jsdn2Vx-Elk

​

Feel free to implement this code into your projects or use the results in a game or video as long as you follow these rules. Feel free to feature this in videos.

I programmed all of this myself in the unity game engine as I am accustomed to it and it allowed me to render stuff on the screen easily especially given that I am only 13 at the time of making the project and only 2 proper years of coding experience, it really helps.

​

​

DO NOT: Publish a commercial or paid application that has the tree generation code without permission

DO NOT: Publish a free, non commercial application where the main or only focus is on the tree generation from this code without credits or with permission

DO NOT: Claim you made the tree generator code

DO NOT: Sell any of the un-edited results of this code for any money

​

YOU MAY: Give out un-edited results as long as it is for free

YOU MAY: Sell SIGNIFICANTLY EDITED results

YOU MAY: Make an application that uses this code if it is non-commercial and is free with permission

You May: Use the RESULTS of this code in any project as long as there are credits to this page or to 'Gaming Tardigrade'

​

Enough said, here is the unity code:

TreeGenerator:

​

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TreeGenerator : MonoBehaviour
{
    [Header("ScriptableObject")]
    public TreeGeneratorPrefab prefab;

    [Header("Other Settings")]
    public bool generateOnStart;
    public float sizeTimeser;

    public enum TreeNodeState {Null, Trunk, Branch, BranchEnd, Leaf};
    [Header("other")]
    public Renderer rendererObject;

    private Texture2D texture;

    [Header("Quantities")]

    private float pixelLeafChance;
    private float waterGrowthChance;
    private int waterItterations = 1;
    private Gradient leafColours;
    private Gradient TrunkColours;
    private int TreePixelSizeX, TreePixelSizeY;

    private TreeGeneratorNode[,] Nodes;
    private TreeGeneratorNode[] BranchEndNodes;

    private TreeData currentTree;

    private void DecodeScriptabeObject()
    {
        pixelLeafChance = prefab.pixelLeafChance;
        TreePixelSizeX = prefab.TreePixelSizeX;
        TreePixelSizeY = prefab.TreePixelSizeY;
        waterGrowthChance = prefab.waterGrowthChance;
        waterItterations = prefab.waterItterations;
        leafColours = prefab.leafColours;
        TrunkColours = prefab.TrunkColours;
    }

    void Start()
    {

        if(generateOnStart)
        GenerateTree();
    }

    public void GenerateTree()
    {
        DecodeScriptabeObject();

        float[] ratio = TScriptUt.GetRatio(TreePixelSizeX, TreePixelSizeY);
        rendererObject.transform.localScale = new Vector3(ratio[0] * sizeTimeser, 1f, ratio[1] * sizeTimeser);


        texture = new Texture2D(TreePixelSizeX, TreePixelSizeY);
        Nodes = new TreeGeneratorNode[TreePixelSizeX,TreePixelSizeY];


        for (int x = 0; x < TreePixelSizeX; x++)
        {
            for (int y = 0; y < TreePixelSizeY; y++)
            {
               // if (x == TreePixelSizeX / 2 && y == TreePixelSizeY / 2) continue;
                
                Nodes[x,y] = new TreeGeneratorNode();
                Nodes[x, y].colour = new Color(0, 0, 0, 0);
                Nodes[x, y].state = TreeNodeState.Null;
                Nodes[x,y].positionX = x;
                Nodes[x,y].positionY = y;
            }
        }

        for (int i = 0; i < TreePixelSizeY / 3; i++)
        {
            Nodes[TreePixelSizeX / 2, i / 2].state = TreeNodeState.Trunk;
            Nodes[TreePixelSizeX / 2, i / 2].colour = TrunkColours.Evaluate(Random.Range(0f, 1f)); ;
        }         

        for (int i = 0; i < waterItterations; i++)
        {
            AddWater();
        }

        AddLeaves();
        ApplyTexture();
    }

    public void AddLeaves()
    {
        foreach (TreeGeneratorNode node in Nodes)
        {        

            List<TreeGeneratorNode> Neighbours = GetNeighbours(node);
            int neighbourCount = 0;
            foreach (TreeGeneratorNode neighbour in Neighbours)
            {
                if (neighbour.state != TreeNodeState.Null && neighbour.state != TreeNodeState.Trunk)
                {
                    neighbourCount++;
                }
            }

            if (neighbourCount >= 1 && Random.Range(0f,1f) <= pixelLeafChance)
            {
                node.colour = leafColours.Evaluate(Random.Range(0f, 1f));
            }
        }       
         
        
    }

    public void AddWater()
    {
        bool[] isWater = new bool[TreePixelSizeX];
        bool[] waterToClear = new bool[TreePixelSizeX];

        for (int i = 0; i < TreePixelSizeX; i++)
        {
            float value = 1;
            if (value >= TreePixelSizeX / 2 && value <= TreePixelSizeX)
            {
                value = TreePixelSizeX;
            }
            isWater[i] = Random.Range(0f,1f * value) >= 0.7f; 
            waterToClear[i] = false;
        }

        for (int y = TreePixelSizeY -1; y > 0; y--)
        {
            bool exists = false;
            foreach (bool b in isWater)
            {
                if (b)
                {
                    exists = true;
                    break;
                }
            }

            if (!exists)
                break;

            List<TreeGeneratorNode> nodesForLogging = new List<TreeGeneratorNode>();

            for (int x = 0; x < TreePixelSizeX; x++)
            {
                if (Nodes[x, y].state != TreeNodeState.Trunk && isWater[x])
                {
                    if (Random.Range(0f, 1f) >= waterGrowthChance)
                        continue;

                    List<TreeGeneratorNode> neighbours = GetNeighbours(Nodes[x, y]);

                    bool HasNeighbour = false;
                    foreach (TreeGeneratorNode node in neighbours)
                    {
                        if (node.state != TreeNodeState.Null)
                        {
                            HasNeighbour = true;
                            break;
                        }
                    }

                    if (HasNeighbour)
                    {
                        nodesForLogging.Add(Nodes[x, y]);
                        Nodes[x, y].colour = TrunkColours.Evaluate(Random.Range(0f, 1f));
                        waterToClear[x] = true;
                    }
                }
                else
                {
                    waterToClear[x] = true;
                }
            }

            if (nodesForLogging.Count > 0)
            {

                foreach (TreeGeneratorNode n in nodesForLogging)
                {
                    n.state = TreeNodeState.Branch;
                }
            }


            for (int i = 0; i < TreePixelSizeX; i++)
            {
                if (waterToClear[i])
                    isWater[i] = false;
            }
        }       
    }

    public void ApplyTexture()
    {
        for (int x = 0; x < TreePixelSizeX; x++)
        {
            for (int y = 0; y < TreePixelSizeY; y++)
            {
                TreeGeneratorNode targetNode = Nodes[x, y];
                texture.SetPixel(x,y, targetNode.colour);
            }
        }

        texture.filterMode = FilterMode.Point;
        texture.wrapMode = TextureWrapMode.Clamp;
        texture.Apply();

        rendererObject.sharedMaterial.mainTexture = texture;
    }

    public List<TreeGeneratorNode> GetNeighbours(TreeGeneratorNode node)
    {
        List<TreeGeneratorNode> neighbours = new List<TreeGeneratorNode>();

        for (int x = -1; x <= 1; x++)
        {
            for (int y = -1; y <= 1; y++)
            {
                if (x == 0 && y == 0)
                    continue;

                int checkX = node.positionX + x;
                int checkY = node.positionY + y;

                if (checkX >= 0 && checkX < TreePixelSizeX && checkY >= 0 && checkY < TreePixelSizeY)
                {
                    neighbours.Add(Nodes[checkX, checkY]);
                }
            }
        }

        return neighbours;
    }
}

​

​

TreeGeneratorPrefab:

​

using UnityEngine;

[CreateAssetMenu()]
public class TreeGeneratorPrefab : ScriptableObject
{
    public Gradient leafColours;
    public Gradient TrunkColours;

    public int TreePixelSizeX, TreePixelSizeY;
    public int waterItterations;

    [Range(0f, 1f)]
    public float pixelLeafChance;
    [Range(0f, 1f)]
    public float waterGrowthChance;

}

​

​

TreeGeneratorNode:

​

using UnityEngine;

public class TreeGeneratorNode
{
    public Color colour;
    public TreeGenerator.TreeNodeState state;
    public int positionX;
    public int positionY;
}
 

bottom of page