Video icon 64
Build your coding skills with practical video courses from Tuts+. Start your free trial today.
Advertisement

How to Procedurally Customize Your Unity Game Assets With Code

by

In this tutorial I'll show you the basics of using procedural object-customization to your advantage, in Unity. It basically boils down to using randomness within certain rules. If you employ some sort of procedural system (even a simple one), you can add a wide variety of diversity and detail to your game, without having to create it all manually.

We'll start by creating a simple tree manually, and then use procedural customization techniques to automate the creation of a range of similar yet notably different trees. Then, we'll place these trees in a simple game, to show how they look in "action".

Here's what our procedurally-generated trees will look like:


Click the button to generate a new set of trees.

Setup

As in my previous tutorial, you'll need to have Unity installed, and have a basic knowledge thereof. (See How to Learn Unity for a good place to get started.)

Create a new project for this tutorial, and tick the checkbox labeled character controllers; we might need them later.

I have prepared some trees which we will use, which you can find in the 3d-files folder of the source download. The entire project files, in case you want to use them, can also be found in the source download, but are not necessary for this tutorial.

Ready? Let's go!


Creating a Tree

Let's create a simple tree first since, in order to add procedural touches, we need a basic template to start from.

Put the 3D files I mentioned above in the asset folder. Remember to set the scaling in the importer to 1 rather than 0.001, so that the sizes line up.

procedural_elements_01

Now create a cube and name it Tree. Go to the Mesh Filter component and swap out the cube mesh for the tree_01 mesh. It should show up if you have the 3D files in your asset folder.

procedural_elements_17

The tree might not fully appear yet. This is because the 3D model for the tree supports two materials (bark and leaves), but right now it is still set to one. In order to fix this, open the Materials array in the Mesh Renderer component of the cube, and set the Size to 2. It should look like this:

procedural_elements_02

Now we need materials for the bark and leaves. Create two new materials and give them a basic brown and green color. Assign those two materials to the materials slot in the Mesh Renderer of the tree, and it should appear with those materials displayed, like this:

procedural_elements_03
procedural_elements_04

In order to finish this basic tree, add a capsule collider by clicking Component > Physics > Capsule Collider. This is so that we won't move "through" the tree when we test it out later. Unity will ask whether you want to replace the current box collider on the tree, which you should confirm. Once the new collider is added, alter the values so that the shape of the capsule roughly overlaps the trunk of the tree, like so:

procedural_elements_16

These values will work:

procedural_elements_15

Now, pull the tree object into the project folder to turn it into a prefab. Our basic tree is done!


Randomly Rotating the Trees

Create a new script called tree.js and add it to the tree prefab. Now, add the following lines to the Start() function:

transform.localEulerAngles.y = Random.Range(0, 360);

The tree will now be rotated a random amount on the y-axis when it is created. This means that if you place a row of trees, like this:

procedural_elements_05

...then they all turn in their own direction upon starting the game. Put a bunch of trees in your level and try it out!

procedural_elements_06

Adjusting the Size

In a similar way, we can change the size of any object. Add the following code to the tree.js script:

transform.localScale = transform.localScale * Random.Range(0.5, 1.5);

This will either reduce or increase the size of the tree. If you start with a scale of 1 (which should be the case if everything is set up correctly), the new scale will be between 0.5 (half as big) and 1.5 (50% bigger). Now your trees should look even more diverse:

procedural_elements_07

Using Different 3D Models

You might have noticed that there are actually three trees in the files I gave you earlier. That is because we are now going to randomly switch out the 3D model for another one, creating even more trees from the template.

In order to do so, add the following code to the tree.js script:

var treeMeshes: Mesh[];
function Start()
{
	gameObject.GetComponent(MeshFilter).mesh = treeMeshes[Random.Range(0, treeMeshes.Length)];
}

Before starting the scene, though, assign the three meshes we have to that array, like this:

procedural_elements_09

If you play your scene, you'll notice that you now get three different tree-meshes, randomly assigned.

procedural_elements_08

You can test all this out in the demo below. If you press the button, all the trees will be replaced with new and unique ones.


Changing the Color

This one might be a bit tricky. In addition to all the previous features, we'll give each tree a unique shade of green and brown.

First, we need to know how to access the colors. The material of an object can usually be accessed via renderer.material.color. In our case, however, we have two colors, which means we need to access them via renderer.materials[0].color and renderer.materials[1].color. Check the mesh renderer of the tree to see which material (leaves or bark) is in slot 0, and which one is in slot 1.

procedural_elements_03

Creating a random color works similarly to the previous steps. If you add these lines:

renderer.materials[0].color = Color(Random.Range(0, 1.1), Random.Range(0, 1.1), Random.Range(0, 1.1));
renderer.materials[1].color = Color(Random.Range(0, 1.1), Random.Range(0, 1.1), Random.Range(0, 1.1));

...then both the bark material and the leaf material will get a completely new color assigned. Let's check it out:

procedural_elements_10

Okay, that's not what we're actually going for. These are random colors from the entire available spectrum! For artificial items, like cars, this might be enough. But we have trees, so we need greens and browns.

Substitute the color code we just added with this:

renderer.materials[0].color = Color(Random.Range(0.8, 1.1), Random.Range(0.4, 0.6), Random.Range(0, 0.2));
renderer.materials[1].color = Color(Random.Range(0, 0.4), Random.Range(0.6, 1.1), Random.Range(0, 0.4));

And try it out:

procedural_elements_11

Much better. (If suddenly the leaves are brown and the trunk is green, swap the 1 and 0 in renderer.materials[0].)

The code produces a new color at the beginning of the program color. The three random values go from 0 (minimum) to 1 (maximum), which then correspond to the red, green, and blue values in the color. By limiting the range of the randomness, like by saying Random.Range(0.3, 0.6), the values are limited to a certain range. This allows us to create a range of new colors that are still "green", or whatever color we might specify.


Tilting the Tree (Optional)

This is just a small tweak, but a nice one nonetheless. We can give the tree a slight slant to one side, which eliminates that "cleanly placed" feeling that might have come up previously

transform.localEulerAngles.x = Random.Range(-10, 10);

This time a very small rotation is applied to the x-axis, making the tree lean in that direction. To make sure there is no "gap" between the tree's roots and the ground, the "pivot" (or the center) of the tree-meshes is slightly above the roots, which means there is enough wiggle-room available.

The entire tree-script should look like this:

var treeMeshes: Mesh[];
function Start()
{
    transform.localEulerAngles.y = Random.Range(0, 360);
    transform.localEulerAngles.x = Random.Range(-10, 10);
    transform.localScale = transform.localScale * Random.Range(0.5, 1.5);
    gameObject.GetComponent(MeshFilter).mesh = treeMeshes[Random.Range(0, treeMeshes.Length)];
    renderer.materials[0].color = Color(Random.Range(0.8, 1.1), Random.Range(0.4, 0.6), Random.Range(0, 0.2));
    renderer.materials[1].color = Color(Random.Range(0, 0.4), Random.Range(0.6, 1.1), Random.Range(0, 0.4));
}

You can try it all out in this demo:


Creating a Simple Procedural Level

Let's see how these feel in an actual game.

Put a plane on the ground and scale it up. This will be our "floor" for the level. While you're at it, create a floor material and assign it to the floor. Set the scale factor of the plane to 50.1.50, so we have enough room.

Then, put a first-person-controller into the scene. It can be found in the Character Controllers that were imported at the start of the tutorial. (If you don't have them, click Assets > Import Package > Character Controllers). After you've placed the FPS-controllers, remove the main camera; the controller comes with its own, so we won't need the camera in the scene anymore. Also, add a directional light.

To automatically create trees, create a new JavaScript file and name it treeGenerator.js. Put the following code into it:

var treePrefab: GameObject;
var numberOfTrees: int = 20;
function Start()
{
    for(var i: int = 0; i < numberOfTrees; i++)
    Instantiate(treePrefab, Vector3(transform.position.x + Random.Range(-40.0, 40.0), 0, transform.position.z + Random.Range(-40.0, 40.0)), transform.rotation);
}

Put the treeGenerator.js script on the floor, assign the tree prefab to the treePrefab variable slot, and try it out:

procedural_elements_12
procedural_elements_13

And done! Now you have a simple exploratory game, which will create a unique level each run.

Further Improvements

The game you have can now be wildly extended. You could add more plants, like palm-trees or bushes. You could add other objects, like rocks or apples. Or you could add some kind of coin or pickups, which the player could run over to increase their score.


Conclusion

Procedural touches are a simple and efficient way to create detail automatically. Instead of having ten objects, you could have infinitely many.

You can use the elements we played with in your own game, or you can take them apart and re-write them to suit your own purposes. For instance, I made a system that creates different lengths of bamboo:

procedural_elements_14

You could make cars with variable elements, or automatically place enemies in a level. The possibilities are endless.

Advertisement