Quantcast
Channel: Envato Tuts+ Game Development
Viewing all articles
Browse latest Browse all 728

Create 2D Tree Shapes With Code

$
0
0
Final product image
What You'll Be Creating

Creating organic shapes like trees can be an interesting side project for potential game developers. You can use the same logic to create levels or other complicated logic structures. In this tutorial, we will be creating 2D tree shapes in Unity using two different approaches: Fractal and L-System.

1. Creating 2D With 3D

Although we call these 2D tree shapes, they are essentially 3D mesh objects in Unity. The game object which has the tree script will need to have these 3D mesh components attached in order to create our tree shape. Those components are the MeshRenderer and the MeshFilter, as shown below.

The necessary mesh components

With these components attached, we will be creating a new mesh using the different algorithms for fractals and L-Systems.

Creating a Mesh

A 3D mesh is created using multiple vertices which combine to form faces. To make a single face, we will need a minimum of three vertices. When we connect three vertices in a clockwise sequence, we will get a face which has a normal pointing outwards. The visibility of a face is dependent on the direction of its normal, and hence the sequence in which the vertices are passed in to create a face matters. Please read the official Unity documentation for further details regarding the creation of a mesh.

With the power of mesh creation under our belt, let's proceed to our first method for creating a 2D tree: fractal.

2. Creating a Fractal Tree

A fractal is a shape created by repeating a pattern with varying scales. Theoretically a fractal can be an unending pattern, where the base pattern gets repeated indefinitely while its size gets reduced progressively. When it comes to a tree, the base fractal pattern can be a branch splitting into two branches. This base pattern can be repeated to create the symmetrical tree shape shown below.

Symmetrical fractal tree shape

We will need to stop the repetition after a certain number of iterations, and the result is obviously not a very realistic tree shape. Yet the beauty of this approach—and fractals in general—is that they can be easily created using simple recursive functions. The base pattern drawing method can recursively call itself while reducing the scale until a certain number of iterations are complete.

The Branch

The primary component in a tree shape is a branch. In our approach, we have a Branch class, which has a CreateBranch method as shown below.

A branch essentially is a shape (or a Quad) with four corner vertices: bottomLeft, topLeft, topRight, and bottomRight. The CreateBranch method does the proper positioning of the branch by translating, rotating, and scaling these four vertices based on the shape, position, and rotation of the branch. The tip of the branch is tapered using the widthDecreaseFactor value. The main tree method can call this method while passing in the position and rotation values for that branch.

Fractal Tree

The FractalTreeProper class has a recursive CreateBranch method, which in turn will create the Branch class's CreateBranch constructor method.

Each call to CreateBranch initiates two new calls to itself for its two child branches. For our example, we are using a branching angle of 30 degrees and a value of 8 as the number of branching iterations.

We use the points from these branches to create the necessary vertices, which are then used to create faces for our tree mesh.

The baseVertexPointer value is used to reuse existing vertices so that we avoid creating duplicate vertices, as each branch can have four vertices but only two of those are new ones.

By adding some randomness to the branching angle, we can create asymmetrical variants of our fractal tree as well, which can look more realistic.

Asymmetrical fractal tree

3. Creating an L-System Tree

The second method, the L-system, is an entirely different beast. It is a very complicated system which can be used to create intricately complex organic shapes or to create complex rule sets or string sequences. It stands for Lindenmayer System, the details of which can be found on Wikipedia.

The applications of L-systems include robotics and AI, and we will only be touching the tip of the iceberg while using it for our purposes. With an L-system, it is possible to create very realistic looking tree or shrub shapes manually with precise control or using automation.

L-System tree sample

The Branch component remains the same as in the fractal example, but the way in which we create branches will change.

Dissecting the L-System

L-systems are used to create complicated fractals where the patterns are not easily evident. It becomes humanly impossible to find these repeating patterns visually, but L-systems make it easier to create them programmatically. L-systems consist of a set of alphabets which combine to form a string, along with a set of rules that mutate these strings in a single iteration. Applying these rules over multiple iterations creates a long, complicated string which can act as the basis for creating our tree.

The Alphabets

For our example, we will use this alphabet to create our tree string: F+-[, and ].

The Rules

For our example, we will only need one rule where the alphabet F changes into a sequence of alphabets, say F+[+FF-F-FF]-[-FF+F+F]. On every iteration, we will make this swap while keeping all the other alphabets unchanged.

The Axiom

The axiom, or the starting string, will be F. This essentially means that after the first iteration, the string will become F+[+FF-F-FF]-[-FF+F+F].

We will iterate three times to create a usable tree string as shown below.

Parsing the Tree String

Now that we have the tree string using the L-system, we need to parse it to create our tree. We will look at each character in the tree string and do specific actions based on them as listed below.

  • On finding F, we will create a branch with current parameters of length and rotation.
  • On finding +, we will add to the current rotation value.
  • On finding -, we will subtract from the current rotation value.
  • On finding [, we will store the current position, length, and rotation value.
  • On finding ], we will restore the above values from the stored state.

We use an angle value of 25 degrees for branch rotation for our example. The CreateTree method in the LSystemTree class does the parsing. For storing and restoring states, we will be using a LevelState class which stores the necessary values along with a BranchState struct.

The variable levelStates stores a list of LevelState instances, where a level can be considered as a branching point. As each such branching point can have multiple branches or just one branch, we store those branches in a list logicBranches holding multiple BranchState instances. The savedStatesQueue tracks the storing and restoring of different LevelStates. Once we have the logical structure for our tree in place, we can use the levelStates list to create visual branches and create the tree mesh.

The GetClosestVextexIndices method is used to find common vertices to avoid duplicates while creating the mesh.

By varying the rules slightly, we can get drastically different tree structures, as shown in the image below.

L system tree variants using slightly different rules

It is possible to manually create the tree string for designing a specific kind of tree, although this can be a very tedious task.

Conclusion

Playing with L-systems can be fun, and it could result in very unpredictable results as well. Try changing the rules or adding more rules to create other complicated shapes. The next logical thing to do would be to try and extend this to 3D space by replacing these 2D Quads with 3D cylinders for branches and adding the Z dimension for branching.

If you are serious about creating 2D trees, I would suggest that you look into space colonization algorithms as the next step.


Viewing all articles
Browse latest Browse all 728

Trending Articles