Home and Learn: Games Programming Course
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.
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:
And here's the Hierarchy:
You can create a material and apply it to the ground. We went with a yellow color.
(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.
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:
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:
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:
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:
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:
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.
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:
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:
Click the Plus button and add a new tag called SegmentTrigger:
In the Hierarchy, click back on your SegmentTrigger item. From tag dropdown in the Inspector, select your new tag:
One last thing to do is to add a 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:
Now we can create more 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:
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:
(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:
Drag and drop all three items into your new folder:
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:
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:
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:
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.
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:
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:
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.):
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:
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:
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):
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