How Create Endless Runner Game In Unity

How Create Endless Runner Game In Unity

Unity is a powerful and widely-used game development platform and engine known for its versatilityease of use, and ability to create stunning interactive experiences across various platforms. Developed by Unity Technologies, Unity provides developers with a comprehensive set of tools and features to bring their creative visions to life.

With Unity, developers can build games, interactive applications, simulations, and more for a wide range of platforms, including mobile devices, consoles,  desktops, and even virtual reality (VR) and augmented reality (AR) devices. Its cross-platform capabilities make it a popular choice for game developers, indie creators, and large studios alike.

In the realm of video games, even expansive worlds must eventually come to an end. However, certain games endeavor to simulate a boundless expanse, fitting into a category known as "Endless Runner".

An Endless Runner constitutes a genre of game where the player maintains a perpetual forward motion, all the while gathering points and skillfully evading obstacles. The central goal is to progress through the level unscathed by avoiding falls or collisions with the obstacles. Frequently, these levels perpetually repeat, progressively intensifying in challenge until the player inevitably encounters an obstacle.

To create a project in Unity you will need to have Unity and Unity Hub installed. You can download it from Unity's website. To complete the project, follow this tutorial from start to end.

1. Create Project:

First, we open the Unity Hub and Create a 3D Project.

Given the inherent limitations of processing power even in contemporary computers and gaming devices, achieving a genuinely infinite world remains an unattainable feat.

However, some games ingeniously cultivate the perception of boundlessness through a technique known as "object pooling". In essence, this involves recycling building blocks, such as elements of the game environment, by relocating them to the forefront once they pass behind or exit the camera view. This creates the illusion of an infinite expanse.

In the realm of Unity, crafting an endless-runner game necessitates the creation of a platform that encompasses obstacles, coupled with the implementation of a player controller.

2. Create Platform:

We initiate by crafting a tiled platform structure that will subsequently be stored in a Prefab:

Generate a new GameObject and name it "TilePrefab".

Fashion a fresh Cube (GameObject -> 3D Object -> Cube).

Relocate the Cube within the "TilePrefab" object, adjusting its position to (0, 0, 0), and scaling it to (8, 0.4, 20).

Platform of game

Optionally, consider incorporating Rails to the sides by adding extra Cubes, as demonstrated:

To introduce obstacles, I intend to incorporate three distinct obstacle variations, although you can develop more as necessary:

Integrate three GameObjects within the "TilePrefab" object, labeling them "Obstacle1", "Obstacle2" and "Obstacle3".

For the initial obstacle (Obstacle1), generate a new Cube and situate it within the "Obstacle1" object. Scale this Cube to approximate the platform's width and then reduce its height (requiring players to jump over it). Generate a new Material named "RedMaterial" color it red, and assign it to the Cube (to distinguish the obstacle from the main platform).

The player

You will see a screen like this. Change the colour of the material as required.

For Obstacle2, configure several cubes to form a triangle shape, leaving an opening at the bottom (players will need to crouch to evade this obstacle).

For Obstacle3, duplicate Obstacle1 and Obstacle2, combining them.

Obstacles

The obstacles can be of any type. You can design obstacles according to need. This image just show how obstacles work.

Tag all the objects within Obstacles with "Finish". This is vital for detecting collisions between the Player and Obstacles.

To spawn an infinite platform, we necessitate a couple of scripts to handle Object Pooling and obstacle activation:

Produce a new script named "SC_PlatformTile" and input the provided code.

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

public class SC_PlatformTile : MonoBehaviour
{
    public Transform startPoint;
    public Transform endPoint;
    public GameObject[] obstacles; //Objects that contains different obstacle types which will be randomly activated

    public void ActivateRandomObstacle()
    {
        DeactivateAllObstacles();

        System.Random random = new System.Random();
        int randomNumber = random.Next(0, obstacles.Length);
        obstacles[randomNumber].SetActive(true);
    }

    public void DeactivateAllObstacles()
    {
        for (int i = 0; i < obstacles.Length; i++)
        {
            obstacles[i].SetActive(false);
        }
    }
}        

  1. Create another script titled "SC_GroundGenerator" and incorporate the given code.

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

public class SC_GroundGenerator : MonoBehaviour
{
    public Camera mainCamera;
    public Transform startPoint; //Point from where ground tiles will start
    public SC_PlatformTile tilePrefab;
    public float movingSpeed = 12;
    public int tilesToPreSpawn = 15; //How many tiles should be pre-spawned
    public int tilesWithoutObstacles = 3; //How many tiles at the beginning should not have obstacles, good for warm-up

    List<SC_PlatformTile> spawnedTiles = new List<SC_PlatformTile>();
    int nextTileToActivate = -1;
    [HideInInspector]
    public bool gameOver = false;
    static bool gameStarted = false;
    float score = 0;

    public static SC_GroundGenerator instance;

    // Start is called before the first frame update
    void Start()
    {
        instance = this;

        Vector3 spawnPosition = startPoint.position;
        int tilesWithNoObstaclesTmp = tilesWithoutObstacles;
        for (int i = 0; i < tilesToPreSpawn; i++)
        {
            spawnPosition -= tilePrefab.startPoint.localPosition;
            SC_PlatformTile spawnedTile = Instantiate(tilePrefab, spawnPosition, Quaternion.identity) as SC_PlatformTile;
            if(tilesWithNoObstaclesTmp > 0)
            {
                spawnedTile.DeactivateAllObstacles();
                tilesWithNoObstaclesTmp--;
            }
            else
            {
                spawnedTile.ActivateRandomObstacle();
            }
            
            spawnPosition = spawnedTile.endPoint.position;
            spawnedTile.transform.SetParent(transform);
            spawnedTiles.Add(spawnedTile);
        }
    }

    // Update is called once per frame
    void Update()
    {
        // Move the object upward in world space x unit/second.
        //Increase speed the higher score we get
        if (!gameOver && gameStarted)
        {
            transform.Translate(-spawnedTiles[0].transform.forward  Time.deltaTime  (movingSpeed + (score/500)), Space.World);
            score += Time.deltaTime * movingSpeed;
        }

        if (mainCamera.WorldToViewportPoint(spawnedTiles[0].endPoint.position).z < 0)
        {
            //Move the tile to the front if it's behind the Camera
            SC_PlatformTile tileTmp = spawnedTiles[0];
            spawnedTiles.RemoveAt(0);
            tileTmp.transform.position = spawnedTiles[spawnedTiles.Count - 1].endPoint.position - tileTmp.startPoint.localPosition;
            tileTmp.ActivateRandomObstacle();
            spawnedTiles.Add(tileTmp);
        }

        if (gameOver || !gameStarted)
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                if (gameOver)
                {
                    //Restart current scene
                    Scene scene = SceneManager.GetActiveScene();
                    SceneManager.LoadScene(scene.name);
                }
                else
                {
                    //Start the game
                    gameStarted = true;
                }
            }
        }
    }
    void OnGUI()
    {
        if (gameOver)
        {
            GUI.color = Color.red;
            GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200), "Game Over\nYour score is: " + ((int)score) + "\nPress 'Space' to restart");
        }
        else
        {
            if (!gameStarted)
            {
                GUI.color = Color.red;
                GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 100, 200, 200), "Press 'Space' to start");
            }
        }
        GUI.color = Color.green;
        GUI.Label(new Rect(5, 5, 200, 25), "Score: " + ((int)score));
    }
}        

  1. Attach the SC_PlatformTile script to the "TilePrefab" object.
  2. Allocate "Obstacle1", "Obstacle2" and "Obstacle3" objects to the Obstacles array.
  3. Establish Start Point and End Point by designating GameObjects at the platform's start and end positions.
  4. Attach the SC_GroundGenerator script to a new GameObject named "_GroundGenerator".
  5. Adjust the Main Camera's position to (10, 1, -9) and rotation to (0, -55, 0).
  6. Forge a GameObject labeled "StartPoint" with a position of (0, -2, -15).
  7. Within SC_GroundGenerator, associate Main Camera, Start Point, and Tile Prefab variables.

Running the scene will demonstrate the moving platform. When the platform tile moves beyond the camera view, it returns to the end while activating a random obstacle, thereby creating the illusion of an infinite level. The Camera should be situated as in the video for the platforms to cycle behind and towards the camera, enabling the repetition effect.

3. Create Player:

The player character will be represented by a basic Sphere, controlled through a mechanism incorporating jumping and crouching.

  1. Create a fresh Sphere (GameObject -> 3D Object -> Sphere) and eliminate its Sphere Collider component.
  2. Assign the previously established "RedMaterial" to this Sphere.
  3. Generate a new GameObject named "Player".
  4. Move the Sphere within the "Player" object, adjusting its position to (0, 0, 0).
  5. Formulate a new script, labeled "SC_IRPlayer", and insert the provided code from SC_IRPlayer.cs.

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

[RequireComponent(typeof(Rigidbody))]

public class SC_IRPlayer : MonoBehaviour
{
    public float gravity = 20.0f;
    public float jumpHeight = 2.5f;

    Rigidbody r;
    bool grounded = false;
    Vector3 defaultScale;
    bool crouch = false;

    // Start is called before the first frame update
    void Start()
    {
        r = GetComponent<Rigidbody>();
        r.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ;
        r.freezeRotation = true;
        r.useGravity = false;
        defaultScale = transform.localScale;
    }

    void Update()
    {
        // Jump
        if (Input.GetKeyDown(KeyCode.W) && grounded)
        {
            r.velocity = new Vector3(r.velocity.x, CalculateJumpVerticalSpeed(), r.velocity.z);
        }

        //Crouch
        crouch = Input.GetKey(KeyCode.S);
        if (crouch)
        {
            transform.localScale = Vector3.Lerp(transform.localScale, new Vector3(defaultScale.x, defaultScale.y  0.4f, defaultScale.z), Time.deltaTime  7);
        }
        else
        {
            transform.localScale = Vector3.Lerp(transform.localScale, defaultScale, Time.deltaTime  7);
        }
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        // We apply gravity manually for more tuning control
        r.AddForce(new Vector3(0, -gravity  r.mass, 0));

        grounded = false;
    }

    void OnCollisionStay()
    {
        grounded = true;
    }

    float CalculateJumpVerticalSpeed()
    {
        // From the jump height and gravity we deduce the upwards speed 
        // for the character to reach at the apex.
        return Mathf.Sqrt(2  jumpHeight  gravity);
    }

    void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.tag == "Finish")
        {
            //print("GameOver!");
            SC_GroundGenerator.instance.gameOver = true;
        }
    }
}        

  1. Attach the SC_IRPlayer script to the "Player" object (you'll observe the addition of an additional component, namely Rigidbody).
  2. Append the BoxCollider component to the "Player" object.
  3. Position the "Player" object marginally above the "StartPoint" object, directly in front of the Camera.
  4. Launch the scene in Play mode and employ the W key for jumping and the S key for crouching. The goal is to evade the red obstacles.

This configuration enables the player to control the Sphere character, navigate through the level, and respond to obstacles by employing the jump and crouch commands.

If you like the article please 👍 it, wants to refer somebody 📤 with him/her. We also provide Services of 2D/3D Game Development, 2D/3D Animations,Video Editing, UI/UX Designing.

If you have questions or suggestions about the game or want something to build from us, Feel free to reach out to us anytime!

📱 Mobile: +971 544 614 238

📧 Email: wahhab_mirza@vectorlabzlimited.com


To view or add a comment, sign in

Insights from the community

Others also viewed

Explore topics