Creating an Infinite Runner



In this post, we will start by creating an infinite runner game, a classic game genre on mobile devices; this game will have the following features:

  • The player will have to avoid obstacles by jumping above them.
  • Jumps will be performed when the player presses a key (for the web versions) or when s/he taps once on the right side of the device’s screen (for the Android version).
  • The obstacles will come from the right side of the screen.
  • If the player hits one of the obstacles, the game will be restarted.

 

So, after completing this post, you will be able to:

  • Create a simple infinite runner.
  • Add simple controls to the main character.
  • Generate obstacles randomly.
  • Detect collisions.
  • Create a simple environment with basic shapes.

Figure 1: The final game

Adding movement to the character

So, in this section, we will start to create the core mechanics of the game; the environment will consist of a box for the player in addition to the ground.

 

So, let’s get started:

  • Please launch Unity and create a new Project (File | New Project).

Figure 2: Creating a new project

  • In the new window, you can specify the name of your project, its location, as well as the 2D mode (as this game will be in 2D).

 

Figure 3: Specifying the name and location of your new project

  • Once this is done, you can click on the button labelled Create project (i.e., the button located at the bottom of the window), and a new project should open in Unity.
  • Once this is done, you can check that the 2D mode is activated, based on the 2D logo located in the top-left corner of the Scene view, as illustrated in the next figure.

Figure 4: Activating the 2D mode

First, we will remove the background image for our Scene. If you look at your Game view, it may look like the following figure.

Figure 5: The initial background

If it is the case, then please do the following:

  • From the top menu, select: Window | Lighting | Settings.
  • Then delete the Default Skybox that is set for the attribute called SkyBox (i.e., click on the attribute to the right of the label Skybox and press DELETE on your keyboard).

Figure 6: Lighting properties

  • Once this is done, your Game view should look like the following.

Figure 7: The Game view after deleting the SkyBox

We will now create sprites that will be used for the ground and the player.

  • From the Project window, select Create | Sprites | Square. This will create a sprite object called Square in the Project window.
  • Please rename this asset ground (i.e., right-click + Rename).

Figure 8: Creating a new sprite

  • Drag and drop this asset (i.e., ground) from the Project window to the Scene view.
  • This will create a new object called ground in the Hierarchy.
  • Select this new object in the Hierarchy, and, using the Inspector, change its scale attribute to (100, 1, 100) and its position to (0, -4, 0).
  • Add a BoxCollider2D component to the object called ground (i.e., select Component | Physics2D | BoxCollider2D from the top menu); this is so that we can detect collisions between the player and the ground later-on.

Once this is done, we can then create the player.

  • Please duplicate the object called ground in the Hierarchy (i.e., select the object + press the keys CTRL and D).
  • Rename the duplicate player.
  • Using the Inspector, change the scale of this new object to (1, 1, 1) and its position to (0, -3, 0).
  • Add a 2DRigidBody component to this object (i.e., select Component | Physics2D | RigidBody2D from the top menu).
  • Using the Sprite Renderer component of the object called player, change its color to a light blue.

By now, your scene should look like the following figure:

Figure 9: Drafting the scene

At this stage, we can start to add some code to be able to control our character. To do so we will do the following:

  • Check when the player is on the ground.
  • If this is the case and if the player presses the jump key (this key will be defined later), then the character will jump.
  • It will be necessary to check whether the player is on the ground so that the character can’t jump while in the air.

So let’s proceed:

  • Please create a new C# script called ControlPlayer: from the Project window, select Create | C# Script.
  • Please add the following code at the beginning of the class.

bool isOnGround;

This variable will be used to check whether the player character is on the ground.

  • Please add the following function to the class:

void OnCollisionEnter2D(Collision2D coll)

{

if (coll.collider.tag == “ground”)

isOnGround = true;

else

isOnGround = false;

}

In the previous code:

  • We check whether a collision has been detected between the player and other objects using the built-in function OnCollisionEnter2D.
  • If the other object has a tag called ground then the variable called isOnground is set to true; otherwise, this variable is set to false.
  • Note that we yet have to create a tag for the ground and this will be done in the next sections.

We can now deal with the key inputs from the player:

  • Please add the following function to the class:

void jump()

{

GetComponent<Rigidbody2D> ().AddForce (new Vector2 (0, 400.0f));

isOnGround = false;

}

In the previous code:

  • We declare a function called jump.
  • This function, when called, adds a vertical force to the player to simulate a jump.
  • It also sets the variable isOnGround to false.

 

Last but not least, please add the following code to the Update function (new code in bold):

void Update ()

{

if (Input.GetKeyDown (KeyCode.Space) && isOnGround) jump();

}

In the previous code, we detect when the player has pressed the Space Bar; in this case, we check that the player is on the ground and we then call the function jump accordingly.

Please save your script, check that it is error-free, and drag and drop it on the object called player in the Hierarchy window.

At this stage, we just need to create a tag for the ground.

  • Please select the object called ground in the Hierarchy.
  • In the Inspector window, click on the drop-down menu called Untagged, to the right of the label called Tag, as illustrated in the next figure.

 

Figure 10: Creating a tag

  • Select the option Add Tag from the drop-down menu.

Figure 11: Selecting a new tag

  • In the new window, press on the + button that is just below the label “List is Empty”, as illustrated in the next figure.

Figure 12: Creating a new tag (part 1)

  • This will create a placeholder for the new tag that we want to create.
  • Create a new tag by entering ground to the right of the label called Tag 0 as illustrated in the next figure.

 

Figure 13: Creating a new tag (part 2)

If you are using Unity 5.6, you will only need to enter the name of the tag.

Next, once this tag has been created, we can apply it to the ground object.

  • Please select the object called ground in the Hierarchy.
  • Using the Inspector window, click to the right of the label called Tag and select the tag called ground from the drop-down menu, as illustrated on the next figure.

Figure 14: Selecting a tag for the ground (part 1)

Figure 15: Selecting a tag for the ground (part 2)

You can now test the scene (i.e., CTRL + P); as you press the Space Bar, you should see that the player is jumping.

Figure 16: The first jump

Adding random obstacles to the scene

At this stage, we can make the player character jump, and we just need to add obstacles that will be generated at regular intervals; this will consist in:

  • Creating an obstacle from a basic shape.
  • Creating a prefab from this object.
  • Creating an empty object, with an associated script, that will generate these prefabs at frequent intervals.
  • Each new obstacle will be instantiated to the right of the player (i.e., outside the field of view) and will then start to move to the left (i.e., towards the player).

First let’s create this obstacle:

  • Please duplicate the object called ground (i.e., CTRL + D).
  • Rename the duplicate obstacle.
  • Change its position to (3, -3, 0) and its scale attribute to (1, 1, 1).
  • Using this object’s Sprite Renderer component, change its color to red.
  • Finally, as we have done earlier, please create a tag called obstacle, and apply it to this object.

Figure 17: Adding a new tag for obstacles (part 1)

Figure 18: Adding a new tag for obstacles (part 2)

  • We can now create a prefab from this object by dragging and dropping it to the Project window, as illustrated in the next figure.

Figure 19: Creating a new prefab

 

  • Now that the prefab has been created, you can delete (or deactivate) the object called obstacle in the Hierarchy.

We will now create a new script for this object (i.e., obstacle), so that this objects starts to move to the left every time it has been instantiated.

  • Please create a new C# script called Obstacle (i.e., from the Project window select: Create | C# Script).
  • Add the following code to the Update function (new code in bold).

void Update ()

{

transform.Translate (Vector2.left * 4*Time.deltaTime);

if (transform.position.y < -5) Destroy (gameObject);

}

In the previous code:

  • We move the obstacle at the speed of 4 units per second to the left.
  • We also destroy this object once its y coordinate is less that -5; note that we could also use the function Destroy in the Start function, to delay the destruction of this object.

Please drag and drop this script (i.e., Obstacle) to the prefab called obstacle in the Project window.

Next, we need to create an object (and its associated script) that will generate the obstacle(s) at regular intervals.

  • Please create a new empty object called generateObjects (i.e., select GameObject | Create Empty).
  • Create a new C# script called GenerateObjects (i.e., from the Project window, select: Create | C# Script).
  • Open this script.
  • Add the following code at the beginning of the class.

public GameObject obstacle;

float timer;

In the previous code, we declare a GameObject variable called obstacle that is public; so that it will be accessible from the Inspector; it will be set with the prefab called obstacle later-on, using the Inspector. We also create a variable called timer that will be employed to time the instantiation of the obstacles.

  • Please add the following code to the Update function (new code in bold).

void Update ()

{

manageTimer ();

}

In the previous code, we call a function manageTimer that we yet have to create, and that will be in charge of checking when a new obstacle should be instantiated.

  • Please add the following function to the class:

void manageTimer()

{

timer += Time.deltaTime;

if (timer >= 2)

{

addObstacle ();

timer = 0;

}

}

In the previous code:

  • We define a function called manageTimer.
  • In this function, the timer’s value is increased by one every seconds.
  • If the timer reaches two seconds, then the function addObstacle (that we yet have to create) is called;
  • The timer is also reset to 0.

 

Please add the following function to the class:

void addObstacle()

{

Vector3 positionOfPlayer = GameObject.Find (“player”).GetComponent<ControlPlayer>().initialPosition;

GameObject t1;

t1 = (GameObject)(GameObject.Instantiate (obstacle, positionOfPlayer + Vector3.right * 20, Quaternion.identity));

}

In the previous code:

  • We define a function called addObstacle.
  • This function initially detects the position of the player at the start of the game; since the player is initially on the ground, this will ensure that the obstacle will be created just above the ground.
  • Note that we access a variable called initialPosition that we yet have to create in the script ControlPlayer.
  • This position, stored in the variable positionOfPlayer, is then used to determine the position of the obstacle that will be located to the right of the player.

Please save your script.

Last but not least, we need to initialize the variable initialPosition for the script ControlPlayer:

  • Please open the script ControlPlayer.
  • Add the following code to it (new code in bold):

public Vector3 initialPosition;

void Start ()

{

initialPosition = transform.position;

}

In the previous code:

  • We declare a variable called initialPosition.
  • We then set this variable to the position of the player at the start of the game.
  • Note that the variable initialPosition is public, so it will be accessible from outside the script, including from the script called GenerateObjects.

 

Please save your scripts; check that they are error-free and drag and drop the script GenerateObjects from the Project window to the object called generateObjects in the Hierarchy.

Once this is done, you can select the object called generateObjects in the Hierarchy; if you look at the Inspector window, you should see that this object has a component called GenerateObjects, along with an empty placeholder (i.e., a public variable) called obstacle.

Figure 20: Initializing the obstacle (part 1)

  • Please drag and drop the prefab called obstacle from the Project window to this empty field, as illustrated in the next figure.

Figure 21: Initializing the obstacle (part 2)

Figure 22: Initializing the obstacle (part 3)

You can now test your scene, and you should see that new obstacles are being instantiated every two seconds, as in the next figure.

Figure 23: Instantiating obstacles

At this stage, we can instantiate the obstacles easily; however, we could improve the game by adding the following features:

  • Instantiate double or triple obstacles so that the player has to adjust its jump, hence adding more challenge to the game.
  • Instantiate the objects randomly so that the player never knows whether s/he will have to jump over one, two or three obstacles.

So let’s go ahead:

  • Please modify the function addObstacle as follows:

void addObstacle()

{

Vector3 positionOfPlayer = GameObject.Find (“player”).GetComponent<ControlPlayer>().initialPosition;

float randomNumber = Random.Range (1, 5);

GameObject t1, t2, t3, t4;

t1 = (GameObject)(GameObject.Instantiate (obstacle, positionOfPlayer + Vector3.right * 20, Quaternion.identity));

if (randomNumber >1) t2 = (GameObject)(GameObject.Instantiate (obstacle, positionOfPlayer + Vector3.right * 21, Quaternion.identity));

if (randomNumber >2) t3 = (GameObject)(GameObject.Instantiate (obstacle, positionOfPlayer + Vector3.right * 22, Quaternion.identity));

if (randomNumber >3) t4 = (GameObject)(GameObject.Instantiate (obstacle, positionOfPlayer + Vector3.right * 21 + Vector3.up, Quaternion.identity));

}

In the previous code:

  • We create a random number that will range between 1 and 3.
  • In all cases we add a first obstacle.
  • If the random number is more than one, then we add a second obstacle.
  • If the random number is more than two, then we add a third obstacle.
  • If the random number is more than three, then we add a fourth obstacle.

You can test your scene, and you should see that the game instantiates different types of obstacles.

Figure 24: Instantiating objects at random

Next, we need to detect collisions between the player and these obstacles; if the player collides with any of the red boxes, we should restart the level. For this purpose, we will modify the script called ControlPlayer as follows:

  • Please open the script ControlPlayer.
  • Add the following code at the beginning of the file.

using UnityEngine.SceneManagement;

  • Add the following code to the function OnCollisionEnter2D.

if (coll.collider.tag == “obstacle”) SceneManager.LoadScene (SceneManager.GetActiveScene ().name);

In the previous code, we detect whether we collide with an object that has a tag called obstacle; in this case, the current level (or the active scene) is restarted.

For this to work, we also need to add our current scene to the Build Settings:

  • Please open the Build Settings (File | Build Settings or CTRL + SHIFT + B).
  • Click on the button called Add Open Scene.

Figure 25: Updating the Build Settings

  • This should add the current scene to the settings, as illustrated in the previous figure.
  • You can then close the Build Settings.

Displaying the score

So at this stage, the core mechanics work relatively well, as our character can jump over boxes created at random; another interesting feature for this game could be to add a score based on the time; so the longer the player manages to play the game and avoid obstacles, the higher the score. So we will proceed as follows:

  • Please create a new UI Text object i.e., (GameObject | UI | Text).
  • Rename this object scoreUI.
  • Select this object and, using the Inspector, modify its attributes as follows:
  • Font color = white.
  • Width = 400; height = 200.
  • Alignment: centered both horizontally and vertically (see next figure).

Figure 26: Aligning the text

  • Font size = 68.
  • Position: PosX = 0; PosY = 228 (just at the top of the window)
  • Text: empty

After making these changes, your text field should be located as illustrated in the next figure.

Figure 27: Placing the text field for the score

Next, we will create the code to update this UI element.

  • Please open the script GenerateObjects.
  • Add the following code at the beginning of the script.

using UnityEngine.UI;

  • Add the following code at the beginning of the class.

float score;

  • Add the following code to the Update function:

score +=Time.deltaTime;

displayScore ();

  • Add the following function just before the end of the class (i.e., before the last closing curly bracket).

void displayScore()

{

GameObject.Find (“scoreUI”).GetComponent<Text> ().text = “” + (int)score;

}

You can now test your scene and check that the time is updated accordingly, as illustrated in the next figure.

Figure 28: Updating the time

 ** This post is an excerpt from the book “A Beginner’s Guide to web and mobile Games with Unity” **

abgt_web_and_mobile

Save

Save

Save

Save

Save

Save

Save

Related Articles:

Leave a Reply

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


*