Home and Learn: Games Programming Course


Endless Runner - Infinite Scrolling in 3D games

You're no doubt aware of the endless runner genre of games. In these games, the player tends to have limited movement and it's the background that moves. In this lesson, you'll see how to implement an Infinite Scrolling background.

If you watch the short video below, you'll get an idea of what we'll be doing. The top half is the Scene view and shows you what's happening behind the curtain. The bottom half is the game itself and just shows you the view from the runner's point of view.

 

 

Infinite Scroller Setup

Start a new Unity project. Any pipeline will do, but we used the default pipeline, which in Unity 6 is the Universal Render Pipeline.

Let's create a simple scene with cubes for the ground and walls.

In the Hierarchy on the left of Unity, right-click and select Create Empty. Call your empty game object SEGMENT. Make sure the X, Y, Z for Position are all 0. This object will hold all the various game objects and, a little later, a move script. We'll create variants of this first segment and then turn them into prefabs.

Right-click your SEGMENT empty game object. From the menu, select 3D Object > Plane. Rename the plane object to Ground. Make it longer by changing the Z Scale from 1 to 8. Make sure the Position transforms for X, Y, Z are all 0:

Unity Inspector with some Transform positions set

And here's the Hierarchy:

Unity Hierarchy showing some game objects set up

You can create a material and apply it to the ground. We went with a yellow color.

A gorund plane in Unity Scene view with arrows pointing in the forward direction

(In Scene view, make sure the blue arrow (Z direction) is pointing forward and the red arrow (X direction) is pointing to the right.)

Let's add some walls.

 

Walls

Right-click SEGMENT again in the Hierarchy. From the menu, select 3D Object > Cube. Rename it to Wall_1. Set the following values for your Wall_1 in the Inspector:

Position

X: -5
Y: 2
Z: 0

Scale

X: 0.1
Y: 4
Z: 80

Here's what your Inspector should look like:

Unity Inspector showing some values set for a wall game object

Again, create a new material and apply it to Wall_1.

Now select Wall_1 in the Hierarchy on the left of Unity. Press CTRL + D to duplicate it (or right-click and select Duplicate from the menu). Call the duplicate Wall_2:

Unity Hierarchy showing a duplicate of a game object

With Wall_2 selected, in the Inspector on the right, change the X position from -5 to 5. You should now have a long corridor:

Unity Game view with a ground and two walls

 

Adding the Player

One thing missing is the player. We'll keep it simple and just add a cylinder, even though the game in the video above has a character with a running animation set up. (We won't be doing that in this lesson as we've tackled characters in other tutorials.)

Right click an empty area in the Hierarchy. From the menu, select 3D Object > Cylinder (or Capsule, whichever you prefer). Rename the Cylinder to Player. Drag and drop Main Camera onto your Player:

Unity Hierarchy showing a Main Camera as the child of a Player game object

With the Player selected in the Hierarchy, set the following values in the Inspector:

Position

X: 0
Y: 1
Z: -38

Setting Z to negative 38 should position the player at the start of the corridor.

Now click on the Main Camera. Set the following values in the Inspector:

Position

X: 0
Y: 2
Z: -5

Your Game view should look like this:

A Player prototype game object in a Unity 3D scene

Don't worry that everything looks a little basic - the only thing you're doing here is learning the mechanics of infinite runner games. And it's easier to see what's going on if we have a simple scene.

 

Infinite Runner Mechanics

The trick to infinite runners is to move the segment the player is on. Then, when the segment gets so far, you spawn a new segment. Then you delete the old segment to prevent it hanging around in the scene.

To indicate just when a new segment needs to be spawned, we can create a trigger. The easiest way to do this is to add a cube, then turn its mesh off. We did this in our very first came, the basic driving game.

So, right-click your SEGMENT item in the Hierarchy. From the menu, select 3D Object > Cube. Call the Cube SegmentTrigger. In the Inspector on the right, set the following values for your cube:

Position

X: 0
Y: 2
Z: -25

Scale

X: 20
Y: 4
Z: 1

Expand the Box Collider item and check Is Trigger. Uncheck the box next to Mesh Render. This will make the cube invisible:

A cube game object in Unity with Is Trigger as false and the Mesh Renderer deactivated

We're also going to need a tag for this object (we've already added it, in the image above). So, click the dropdown at the top for Tag, where it says Untagged. Select Add Tag from the menu:

Setting up a tag on a game object in Unity

Click the Plus button and add a new tag called SegmentTrigger:

The tag area of Unity with a new tag being set up

A new tag added to the list of tags

In the Hierarchy, click back on your SegmentTrigger item. From tag dropdown in the Inspector, select your new tag:

Select a tag from the available tags in Unity

One last thing to do is to add a script.

 

Segment Script

Select the SEGMENT item in the Hierarchy. In the Inspector, click the Add Component button. Add a New Script. Call it MoveSegment. Double click the script to open it up in your coding editor. Delete all the default code. replace it with this:

using UnityEngine;

public class MoveSegment : MonoBehaviour
{

    public float segementSpeed = -5;
	
    void Update()
    {
            transform.position += new Vector3(0, 0, segementSpeed) * Time.deltaTime;
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("DestroySegment"))
        {
            Destroy(gameObject);
        }
    }
}

There's not much to the code. At the top, we set up a float called segementSpeed. We set this to -5 (you can change this, if you want). We make it public so that it can be changed in the Inspector, something for you to test out and try different speeds.

The Update method only has one line:

transform.position += new Vector3(0, 0, segementSpeed) * Time.deltaTime;

All this does is to move the transform's position. (The transform is the segment itself, the one the script is attached to). The position needs a Vector3 (X, Y, Z). We only want to change the Z direction of the segment, so we move it along by referencing segementSpeed. This means the segment will move -5 on the Z. Multiplying by Time.deltaTime ensures that the frame rate doesn't get out of synch.

The only other code is for the OnTriggerEnter method. This destroys the segment once it collides with a game object that has the tag DestroySegment. We'll set this up soon.

Save your script and go back to Unity.

Still with SEGMENT selected in the Hierarchy, click the Add Component button in the Inspector on the right. Add a Rigidbody component. Uncheck Use Gravity and check Is Kinematic:

A rigidbody added to a Unity game object with Use Gravity set to false and Is Kinematic set to true

Now we can create more segments

 

Adding Segments

Once you have one segment set up, you can duplicate it to create variations. For example, if you had a desert scene you might want to add a cactus here, a skull there and a rock someplace else. In your variant, move these around, add more stuff, switch it up. Create as many variants as you need.

For us, let's just change the ground color. That way, we can see if a new segment has been added once we play the game.

So, create three new materials in the Project area of Unity. Use any colors you like:

Materials set up in the project area of Unity

Now, in the Hierarchy, select the SEGMENT item. Duplicate this. Call it SEGMENT_2. Use one of your new materials to change the ground color. Repeat the process twice more until you have four segments in the Hierarchy, each with a different color ground:

Four game segments in the Unity Hierarchy

(Because all the segments are on top of each other, you'll only see the ground color of the last segment you used.)

We can now prefab three of the segments.

Create a folder for yourself in the projects area of Unity. Call it MyPrefabs. Highlight segments 2, 3, and 4 in the Hierarchy:

Three game objects selected in the Unity Hierachy

Drag and drop all three items into your new folder:

Three prefab game objects set up in Unity

This is enough to create the prefabs.

The segments in the Hierarchy will turn blue. (Blue is the color of prefabs in Unity.) Now delete all these blue segments, segments 2, 3 and 4, from the Hierarchy, leaving only the original:

A segement game object in the Unity Hierarchy

 

The Player Script

Now we need a script on the player. The segments move towards the player. When the player hits the Segment Trigger, we can create new segments.

So select your Player in the Hierarchy. First, add a Rigidbody to the player. Again, uncheck Use Gravity. Now add a New Script. Call it SpawnSegments:

A C# script and a rigidbody added to a player game object

Double click the script to open it up in your coding editor. Delete all the default code. Replace it with this:

using UnityEngine;

public class SpawnSegments : MonoBehaviour
{
    public GameObject[] segement;
    private int segNum;
    public float segmentZPos = 0f;

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("SegmentTrigger"))
        {
            segNum = Random.Range(0, segement.Length);

            Instantiate(segement[segNum], new Vector3(0, 0, segmentZPos), Quaternion.identity);
        }
    }
}

Again, there's not much to the script. We start with three variables at the top of the code. The first is an array to hold GameObjects called segment. This array will hold our prefabs. Then we have an int variable called segNum and a float called segmentZPos. The segmentZPos variable is a public one. You'll see why soon. But it holds a value for the Z position of the new segment.

All this script does is to create the segments when the player hits the trigger. If the tag that the player collides with is SegmentTrigger then we generate a random number:

segNum = Random.Range(0, segement.Length);

Next, we instantiate a segment:

Instantiate(segement[segNum], new Vector3(0, 0, segmentZPos), Quaternion.identity);

The segment we Instantiate is segement[segNum]. After a comma, we need to specify where we want this segment to go. We want it at position 0, 0, segmentZPos. We don't want any rotation, so we use identity after Quaternion.

Save your script and go back to Unity.

Make sure your Player is still selected in the Hierarchy. Lock the Inspector in the top right. Select all three of your prefabs from your prefab folder. Drag and drop the prefabs onto Segment in the Inspector:

Locking the Unity Inspector so we can drag and drop prefabs to use as list

We now need to add the DestroySegment trigger. But first, unlock the Inspector again, otherwise, you'll wonder why the Player is always showing in the Inspector.

 

Destroy Segments

In the Hierarchy, right-click a blank area. Add another Cube game object. Call it BackCull. In the Inspector, check Is Trigger and uncheck Mesh Renderer. Set the Transform values to these:

Position

X: 0
Y: 0
Z: -120

Scale

X: 20
Y: 4
Z: 1

We need to set a tag for this BackCull game object. If you remember, our MoveSegment script has this:

private void OnTriggerEnter(Collider other) {

if (other.gameObject.CompareTag("DestroySegment")) {

Destroy(gameObject);

}

}

We're using CompareTag in the OnTriggerEnter method. In between the round brackets of CompareTag, we've set up a tag called "DestroySegment".

So, set up a new tag, just like you did before. Call this DestroySegment. Once the tag is added, select the BackCull game object in the Hierarchy. In the Inspector on the right, select your new tag from the dropdown at the top:

A Unity game object used as a trigger

Almost there. If you play your game now, you'll notice a problem. Although the new segments are added, they are added in the wrong place, as the short video below shows:

 

The reason we have one segment on top of another is that we haven't set a value in the Player script for SegmentZPos.

Select Player in the Hierarchy. In the Inspector on the right, you'll see the Segment Z Pos variable we set up. We have left it on the default value of 0:

Player game object in Unity with a C# script attached

Setting a value here can be tricky. It's the position you want the next segment to appear at when the player hits the segment trigger. To get that value, play your game and the then click on your Scene view. Pause the game as soon as the player hits the trigger. (You can click on SegmentTrigger in the Hierarchy.):

A Unity game being pause

With your game paused, have a look in the Hierarchy on the left. You should see a game object called SEGMENT_X, where the X stands in for whatever number prefab has been instantiated. In the image below, SEGMENT_4 has been instantiated:

A prefab highlighted in the Unity Hierarchy

Click on the new segment to select it. In the Scene, drag the blue Z arrow back until the new segment is at the end of the old one:

Scene view with a paused game, the Inspector showing a highlighed Z position value

Now note the Z position in the Inspector, which is 67.5 in the image above.

Stop your game and click back on the Player. Enter 67.5 in the Segment Z Pos box (or whatever value you got in your game):

The Unity Inspector showing a value highlighted for a C# script

If you're not happy with a value of 67.5, play around with it until you get the precise value that works for you.

Play your game again. You should find that the segments now scroll nicely, as in the video at the top of this page.

 

But that's it, that's how infinite scrollers work: move the scenery under the player and keep the player where it is, running in place. Once you know how they work, you can design your own segments, giving each clone segment a bit of variety. As for the player, we've covered player animations and movements elsewhere in this course. You're not doing anything different. (Except maybe clamping to the left and right so the player doesn't go out of bounds. Here, Mathf.Clamp is usually used. If you want a tutorial on the topic of the player's movement, please get in touch.)

And we'll leave it there. Have fun!

<--Back to the Unity 3D Course Contents Page

 


Email us: enquiry at homeandlearn.co.uk