A Very Simple Random Level Generation Script for Unity

Randomness is a useful tool in game development to get a lot of content with less work, if utilized correctly. A common use for randomness is level generation.

Random level generation can be implemented in several ways. The following example is kind of faux random level generation, as it uses pre-made “blocks”, akin to rooms, to create one continuous level. For more “true” random generation, I recommend looking into the Unity tutorial on using Cellular Automata for procedural cave generation.

Let’s get to the actual script:

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

public class LevelGenerationController : MonoBehaviour {

    public Transform player;
    public GameObject floor;
    public GameObject[] parts; // level parts used
    public float allowedDistanceUntilEnd = 10f; // if distance from end is lower than this value, moving is triggered 
    public GameObject currentPart;
    private ConnectionPointManager currentConnectionPoints;
    public GameObject nextPart;
    private ConnectionPointManager nextConnectionPoints;

    private int currentPartIndex; // for preventing the use of the same part twice in a row

	// Use this for initialization
	void Start () {
        // find player and floor
        player = GameObject.FindGameObjectWithTag("Player").transform;
        floor = GameObject.FindGameObjectWithTag("Floor");

        // assign the origin level part or block as the current part
        currentPart = floor.transform.FindChild("Origin").gameObject;
        currentConnectionPoints = currentPart.GetComponent<ConnectionPointManager>();

        // pick randomly the next part to be moved to the end of the level
        int nextPartIndex = Random.Range(0, parts.Length);
        nextPart = parts[nextPartIndex];
        nextConnectionPoints = nextPart.GetComponent<ConnectionPointManager>();
        currentPartIndex = nextPartIndex; // cycle indexes
	}
	
	// Update is called once per frame
	void Update () {
        ObservePlayerDistance();
	}

    // move the randomly chosen next part to the end of the level
    // the next part is placed so that it seamlessly joins the previous part
    void MoveNextPart()
    {
        float nextY = currentConnectionPoints.endPoint.position.y - nextConnectionPoints.beginPoint.localPosition.y;
        nextPart.transform.position = new Vector3(currentPart.transform.position.x + 50, nextY, 0);
    }

    void ObservePlayerDistance()
    {
        // if the player is close to the end of the level (or more than allowed)
        if (currentConnectionPoints.endPoint.position.x - player.transform.position.x < allowedDistanceUntilEnd)
        {
            MoveNextPart(); // move next part to the end of the level
            CycleParts(); // assign the moved part to the current part and choose a new next part
        } 
    }

    void CycleParts()
    {
        currentPart = nextPart;
        currentConnectionPoints = nextConnectionPoints;

        // choose a random number and use it as index to pick a new part
        int nextPartIndex = Random.Range(0, parts.Length);
        // prevent the same part from being used twice
        while (nextPartIndex == currentPartIndex)
        {
            nextPartIndex = Random.Range(0, parts.Length);
        }
        nextPart = parts[nextPartIndex];
        currentPartIndex = nextPartIndex; // cycle indexes
        nextConnectionPoints = nextPart.GetComponent<ConnectionPointManager>();
    }
}

So what is actually going on there? The process is rather simple (hence the name of the post).

  1. During Start(), the script looks for the player and floor (requires a GameObject with tag “Floor” to exist), after which the current part is assigned and next part is chosen randomly from the collection of given parts, from which to build the level from.
  2. During Update(), the player character’s position is observed. If the player character is closer to end of the level than allowedDistanceUntilEnd specifies, MoveNextPart() is run. This moves the randomly chosen part to the end of the level, to a position in which it will seamlessly connect to the previous part. Determining what that position is what I will discuss later in this post.
  3. In Update(), after MoveNextPart() has been run, the parts are cycled in CycleParts(). Here the script assigns the previously moved “next part” as the current part and picks a new next part.

Now, to answer how the next position is determined: the controller is told where the beginning points and end points of the current and next parts are. These beginning points and end points are GameObjects that are placed to the end and beginning coordinates of each part or “block”. Each part or “block” also has a script called ConnectionPointManager, which search for the start and end points when the scene is loaded. Basically, the functioning of this script depends on the correct placement of these beginning and end points.

Example of the correct placement of the Beginning Point.
Example of the correct placement of the Beginning Point.

Here is what the ConnectionPointManager looks like:

using UnityEngine;
using System.Collections;

public class ConnectionPointManager : MonoBehaviour {

    public Transform beginPoint;
    public Transform endPoint;

	// Use this for initialization
	void Start () {
        Transform[] points = GetComponentsInChildren<Transform>();
        foreach (Transform point in points)
        {
            if (point.tag.Equals("Begin Point"))
            {
                beginPoint = point;
            }
            if (point.tag.Equals("End Point"))
            {
                endPoint = point;
            }
        }
	}
}

In Start(), all Transform components are collected from the child GameObjects of the part or “block”, after which they are looped through. Depending on the tag of the GameObject, the Transform is assigned to either beginPoint or endPoint.

Beginning and End Points as child objects of "Blocks".
Beginning and End Points as child objects of “Blocks”.

If you want to try out the code, you can download the UnityPackage with all the assets and a functioning test scene from here.

All the visual assets are the creations of Kenney. You can download the asset pack (Platformer Pack Redux) from here.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s