How Create Endless Runner Game In Unity
Unity is a powerful and widely-used game development platform and engine known for its versatility, ease 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).
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).
You will see a screen like this. Change the colour of the material as required.
Recommended by LinkedIn
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.
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);
}
}
}
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));
}
}
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.
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;
}
}
}
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