For my new project, code name Bumpy, to generate the first prototype I had to create infinite runner level. For the purpose of this prototype I just needed the car to go in forward direction (with increasing speed).

I downloaded bunch of assets for

  1. Car(s)
  2. Road

There are many tutorials on how to do this. I came across a neat technique described in this tutorial.

Here is the final result

Road Prefab

  • Create a game object with road sprite.
  • Attach a child game object called “finish”. This is the gameobject that indicates the end of the road

Here is how game object looks in the end

Moving the car in forward direction

  • Create a gameobject with car sprite attached.
  • Attach the below script to move the car in forward direction
using UnityEngine;

public class CarController : MonoBehaviour
{
    // timer controling the speed of the car
    float speedIncreaseTimer;

    // max speed
    private const float MAX_SPEED = 0.5f;
    // current speed of the car
    private float currentSpeed = 0.1f;

    // Update is called once per frame
    void Update()
    {
        IncreaseSpeed();
    }

    void IncreaseSpeed()
    {
        speedIncreaseTimer += Time.deltaTime;
        // increase speed  every half second
        if (speedIncreaseTimer >= 0.5f && currentSpeed < MAX_SPEED)
        {
            speedIncreaseTimer = 0;
            currentSpeed += 0.1f;
        }

        Vector3 endPosition = new Vector3(transform.position.x, transform.position.y + currentSpeed);

        float totalDistance = Vector2.Distance(transform.position, endPosition);

        // distance covered
        float distCovered = Time.time * currentSpeed;

        // fraction of journey to be completed
        float fractionOfJourney = distCovered / totalDistance;
        transform.position = Vector3.Lerp(transform.position, endPosition, fractionOfJourney);
    }
}

Car Controller

  • The script above basically moves the car on y-axis i.e. in forward direction.
  • The currentSpeed controls how fast the car moves.

Infinite Road Controller

Now we will add code to keep on adding more road objects as car gets closer to the end of current road

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

public class InfiniteRoadController : MonoBehaviour
{
    // refeence to player
    [SerializeField] private GameObject player;
    // reference to road prefab
    [SerializeField] private List<GameObject> roadPrefabs;

    // current road game object
    private GameObject currentRoadObject;

    // finish game object of current road
    private Transform currentRoadObjectFinish;

    // previous road game objects
    private Queue<GameObject> previousRoadsObject = new Queue<GameObject>();

    // next road game object
    private GameObject nextRoadObject;

    private int roadCount = 0;

    private static System.Random rand = new System.Random();

    // Start is called before the first frame update
    void Start()
    {
        // create the first road
        this.currentRoadObject = InitNewRoadWorldPosition(Vector2.zero);

        // init 0,0 wrt the parent instead of worldposition
        this.currentRoadObject.transform.localPosition = Vector3.zero;
        this.currentRoadObjectFinish = this.currentRoadObject.transform.Find("finish");

        if(this.currentRoadObjectFinish == null)
        {
            throw new InvalidProgramException("Cannot find finish object");
        }

        this.nextRoadObject = InitNewRoadWorldPosition(this.currentRoadObjectFinish.position);
    }

    // Update is called once per frame
    void Update()
    {
        // if the player is getting closer  to the end, lets start cleaning up previous road game objects
        if(Mathf.Abs(this.player.transform.position.y - this.currentRoadObject.transform.position.y) < 3)
        {
            if(this.previousRoadsObject != null && this.previousRoadsObject.Count >= 3)
            {
                for(int i = 0; i <= previousRoadsObject.Count - 3; i++)
                {
                    // delete the previous road
                    Destroy(this.previousRoadsObject.Dequeue());
                }
            }
        }

        // if the player has reached the end of current road, switch out the current and next game object
        if(Mathf.RoundToInt(Mathf.Abs(this.player.transform.position.y - this.currentRoadObjectFinish.position.y)) <= 0)
        {
            // move the next road to current road
            this.previousRoadsObject.Enqueue(this.currentRoadObject);
            this.currentRoadObject = this.nextRoadObject;
            this.currentRoadObjectFinish = this.currentRoadObject.transform.Find("finish");
            this.nextRoadObject = InitNewRoadWorldPosition(this.currentRoadObjectFinish.position);
        }
    }

    /// <summary>
    /// Create a new road in given world positions
    /// </summary>
    /// <param name="position"> World position for the current road</param>
    /// <returns></returns>
    GameObject InitNewRoadWorldPosition(Vector2 position)
    {
        // pick a random road prefab
        int roadIndex = rand.Next(roadPrefabs.Count);
        ++roadCount;

        GameObject selectedRoad = roadPrefabs[roadIndex];

        // alter the y position
        Vector2 actualPosition = new Vector2(position.x, position.y + selectedRoad.GetComponent<Renderer>().bounds.extents.y);

        GameObject newRoadSprite = Instantiate(selectedRoad, actualPosition, Quaternion.identity, this.transform);
        newRoadSprite.name = "road_" + roadCount;

        return newRoadSprite;
    }
}

In the above script, there are 3 main objects

  • current road game object – represents the road game object on which the player is
  • next road game object – represent the road game object on which the player will be
  • previous road game object – a queue representing the road objects that player has passed

We create “next” and “previous” game object because the road sprite in my project was smaller and not enough to cover a screen. It looked weird with just one road. You can avoid it if you want have a very long road.

The above code keeps only 5-6 road game objects and deletes others. This was tweaked based on what was in the camera. You could potentially just have “current” and “next” with a long enough road

The above script should generate infinite runner level. You could use the same technique for 3D as well.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>