Infinite Roller Tutorial Two – Prefabs and Terrain Creation

Welcome to Edge of Code’s series of Infinite Roller tutorials. If you’d prefer a video tutorial, you can find one here.

In the previous tutorial, I showed you the how to create images in Inkscape, how to import them into Unity, and how to add physics to the objects. Today, I’m going to explain what prefabs are and how to use them, and we’re going to write a simple script to generate some terrain.

Load up your InfiniteRoller project and open the scene, then create a new folder called Prefabs (right click in the project window, then Create -> Folder).

Prefabs are basically template gameobjects. They are used when we want a number of the same gameobject in a scene. So in this game, we’ll want the piece of ground to be a prefab. To create a prefab, you just need to drag the gameobject from the hierarchy back into the project window. Do this for the ground gameobject (drag it into the Prefabs folder). You can then drag the prefab back into the scene. If you change properties of the prefab, it affects all of the instances of it in the scene. You can also change the properties of a single instance of a prefab and then click on Apply to apply the changes to the prefab itself. Prefabs can also be instantiated in code which we’ll look at in a bit.

Create a new folder and call it Scripts. In that folder, right click, go to Create, and click on “C# script”. Name it Chunk and open it – Unity will open the file in MonoDevelop which it uses as its code editor. Each script contains a class which is like a container for code that does a specific job. At the top, in the class declaration, it says

: MonoBehavior

This means that the Chunk class is a subclass of MonoBehavior, one of Unity’s own classes. Any script that will be attached to a gameobject like this one will should be a subclass of MonoBehavior.

Delete the code that’s already there (inside the highest and lowest curly brackets). First, we’ll create a new public variable that will represent the prefab we just made of the ground:

public GameObject groundPrefab;

groundPrefab is the variable’s name, GameObject is the type of object it is, and public is it’s access modifier, which means that it changes what can access the variable. Public means another script can access groundPrefab. In Unity, it also means that we’ll be able to see this variable in the inspector. To see this, save the script (Ctrl+S), then go back to Unity. In the hierarchy, right click, then click on “Create Empty”. This will create a gameobject with only a transform. Drag the Chunk script from the project window into the inspector to add it as a component of the new gameobject. You should be able to see an empty slot called Ground Prefab. Drag the prefab of the ground we created into the slot.

Unity tutorial prefab

Unity now knows to use that prefab in the script everywhere we use the variable groundPrefab. Rename the gameobject by clicking on it while it’s already selected or by right clicking on it and selecting Rename. Call it Chunk.

Go back to the script. The semi colon at the end of the variable is to tell Unity that we’ve finished with that instruction. Next, we’ll create a method. Type in

void Awake()
{
}

Awake() is one of Unity’s own methods. The code inside Awake is run by Unity when the script is loaded. This happens when we press play. To see this, type in

print (“Awake!”); 

inside the method brackets, save, then go back to Unity and press play. Click on the Console window next to the project window. You should see the Awake! printed here.

Go back to the script. We’re now going to create a method of our own to demonstrate what a method does a bit better. After the Awake method, type

private void PrintToScreen(string words)
{
	print(words);
}

private is this method’s access modifier. It means we can only use this method within this class. If it were public, we would be able to use it in other scripts. void is the return type of the method – it tells us what information we will get back from the method. void means we don’t get anything back. We’ve called this method PrintToScreen, and it has one parameter called words which has a type of string. Inside the method, we’re printing whatever value we assign to the variable words. To see what I’m talking about more clearly, let’s call this method from Awake. To do this, delete the contents of Awake and type

PrintToScreen(“Printing from Awake!”);

The words between the speech marks are a string. If we save and play this in Unity, the console will now display these words. We could also write

string wakingString = “Printing from Awake!”;
PrintToScreen(wakingString);

This would do the same thing – we’re just explicitly naming a new variable called wakingString. This variable doesn’t need an access modifier (like public or private) as is only exists inside this method, whereas the variables at the top, like groundPrefab, exist as part of the whole class. Now that we’ve covered these basics, we’ll start programmatically creating some terrain. I’ll introduce other concepts as we come to them.

Unity tutorial C#

First, let’s look at how this project is going to work.

We have a character (see the image below), and we draw box number 1 underneath it. Box number 2 next to the first one, then 3 etc. As the character rolls over the ground, we’re going to add more in front, and remove what’s behind it. We’ll split these into parts called “chunks” – there’ll be a previous chunk, current chunk and a next chunk, each made up of a certain number of pieces of ground.

Unity tutorial infinite terrain diagram

We’re going to write some code that will instantiate a row of blocks underneath the character. Delete the PrintToScreen code, then create a new method

/* Initialise the pieces of ground that make up the chunk*/
public void InitialiseChunk()
{
}

The /* */ is an one way to write a comment – these are ignored by Unity and are to remind us what the method or piece of code does. In this method, we’ll generate each chunk by creating the right number of pieces of ground and setting their positions. At the moment, it won’t be very exciting as the pieces will just be in a straight line, but we’ll build on it in later tutorials. First, we need a variable to record how many pieces of ground will be in a chunk:

//number of pieces of ground in a chunk
public const int CHUNK_SIZE = 20; 

The // symbol is another type of comment. These are usually used inside a method or above a class variable, whereas the /* */ type is used above methods. const means that CHUNK_SIZE is a constant. We use capital letters so that we can see it’s a constant if we’re reading it in the code later. It has a data type of int, which means it’s an integer, or whole number. In the image above, CHUNK_SIZE would be 3.

We’ll also need to know the width of the ground prefab. Create a class property

public float WidthGroundPrefab
{ get; private set; }

float is a numerical data type. A property is a bit like a simple and specific method that is used like a variable. This property is shorthand for (ignoring that the set is marked private for now)

private float widthGroundPrefab;

public float WidthGroundPrefab
{
	get { return widthGroundPrefab; }
	set { widthGroundPrefab = value; } 
}

i.e. there’s a private class variable that WidthGroundPrefab can get or set within or outside the class. This, in turn, is equivalent to having a private class variable and two methods to get and set that variable.

private float widthGroundPrefab;

public float GetWidthGroundPrefab()
{
	return  widthGroundPrefab;
}

public void SetWidthGroundPrefab(float widthGroundPrefab)
{
	this.widthGroundPrefab =  widthGroundPrefab;
}

The “this” is used to tell Unity that a variable belongs to the class, so this.widthGroundPrefab refers to the private class variable widthGroundPrefab, while the widthGroundPrefab after the equals sign in SetWidthGroundPrefab is the variable passed into the method. If we use the property

public float WidthGroundPrefab
{ get; private set; }

C# auto-implements the private class variable. The private set means that the variable can’t be set outside the class – its value can only be changed from within this class. Public properties are not visible in the inspector like public variables are, but they are visible to other classes. Properties are used to stop other classes from directly accessing and changing variables that they shouldn’t be allowed to change.

Now put the following code inside the empty Awake() method

//set the width of the image
SpriteRenderer spriteRend = groundPrefab.GetComponent<SpriteRenderer>();
WidthGroundPrefab = spriteRend.bounds.size.x;

GetComponent is a method accessible to GameObjects, which groundPrefab is. To use it, we add a dot after groundPrefab. This is how you access methods or variables in other classes. In this case, we’re accessing the components that make up the gameobject. Look back at the prefab we made by clicking on it in Unity. It has a transform, a Sprite Renderer component, and a Box Collider 2D component. We can access the renderer using GetComponent< SpriteRenderer> (). We could do something similar to access the collider. To get the width of the image, we use .bounds.size.x

Next, go back to Unity and create another new class in the Scripts folder called TerrainGenerator. Open it and add some new class level variables –

//Three chunks at a time - previousChunk, currentChunk and nextChunk
public Chunk previousChunk;
public Chunk currentChunk;
public Chunk nextChunk;

These variables are Chunk objects we will create in the scene. They represent the set of CHUNK_SIZE bits of ground.

Save the script, go back to Unity and create an empty gameobject in the hierarchy called TerrainGenerator. Drag the TerrainGenerator script onto it. Make sure you save the scene (Ctrl+S) often. Next, click on the Chunk gameobject in the hierarchy and press Ctrl+D to duplicate it. Do this twice. Click twice on one of them to rename it PreviousChunk. Rename the other two as CurrentChunk and NextChunk. Click on the TerrainGenerator gameobject and drag and drop each chunk gameobject into the appropriate slot. As the variables we created are Chunk variables, Unity knows we’re looking for the Chunk script that’s attached to the gameobject.

Go back to the Chunk script and create a new method

public void RepositionChunk(Vector3 newPosition)
{
	transform.position = newPosition;
}

This is a public method, so can be called from another class. All it does is move the chunk gameobject to a new position. Remember, the transform is the part of every gameobject that holds information about its position, rotation and scale. Using transform.position = newPosition, I can move it. A Vector3 holds the coordinates of the new position (x, y and z positions, just like is shown in the hierarchy).

Go back to the TerrainGenerator script. Create a new method

void Start()
{
}

Start is another one of Unity’s inbuilt methods. The Start method of a script is called after the Awake method in all of the scripts has run. I’ve used the Start method here because I want the code in Chunk’s Awake method to run first. There are other ways to do this – you can tell Unity which order to run the scripts in, but this way is fine for what we’re doing.

Inside Start(), add the following code

//get the width of the ground prefab. It's the same for each chunk, so just use this one
float widthPrefab = previousChunk.WidthGroundPrefab;
		
// reposition each of the chunks
previousChunk.RepositionChunk(new Vector3 (-widthPrefab*Chunk.CHUNK_SIZE, 0, 0));
currentChunk.RepositionChunk(Vector3.zero);
nextChunk.RepositionChunk(new Vector3(widthPrefab*Chunk.CHUNK_SIZE, 0, 0));

So first, we create a new local (it only exists inside the Start method) variable called widthPrefab. We assign it the value of the previousChunk’s WidthGroundPrefab property (the one we created in the Chunk script). Next, for previousChunk, we access the RepositionChunk method we just created in the Chunk script using .RepositionChunk. We pass in a new Vector3 which has coordinates x = -widthPrefab*Chunk.CHUNK_SIZE, y = 0, z = 0. This moves the chunk to the left by the number of pieces of ground in a chunk. Notice that we’ve accessed the CHUNK_SIZE variable in a different way, using the class name, Chunk rather than a specific instance of that class, like the previousChunk, currentChunk and nextChunk are. This is because CHUNK_SIZE is a const variable. As well as being a constant that cannot be changed, that means it’s a static variable – it’s a variable that belongs to the class itself, not to an instance of the class which the other variables, properties and methods we have created do. So when we have previousChunk.RepositionChunk, we are calling previousChunk’s RepositionChunk method. When we have currentChunk.RepositionChunk, we are calling currentChunk’s RepositionChunk method etc. But when we have Chunk.CHUNK_SIZE, we are calling Chunk’s CHUNK_SIZE variable.

The Vector3.zero is equivalent to new Vector3(0, 0, 0). The zero is another static variable belonging to Unity’s Vector3 class.

Go back to the Chunk script and inside the InitialiseChunk method, add

for (int i=0; i<CHUNK_SIZE; i++)
{
	//instantiate the object
	GameObject groundPiece = GameObject.Instantiate(groundPrefab) as GameObject;
}

The line starting with for is a loop. The int i = 0 means an integer variable i starts with a value of zero. The i++ is shorthand for i=i+1, and means that on every loop, i is incremented by one. The i < CHUNK_SIZE means that it loops until i=CHUNK_SIZE-1. The code inside the loop creates or instantiates a new gameobject – one piece of ground each time the loop runs.

Unity tutorial infinite terrain

Inside the loop, add in

			
//set the parent
groundPiece.transform.parent = chunk.transform;

Parenting in Unity means that if we move the parent, the child will move with it. We want to be able to move each chunk with its pieces of ground. Next add code to position the groundPiece object

Vector3 piecePosition = new Vector3(transform.position.x + i*WidthGroundPrefab, transform.position.y, transform.position.z);
groundPiece.transform.position = piecePosition;

We create a new Vector3 object called piecePosition, then assign it to the groundPiece’s position. The y and z parts of piecePosition are just the parent chunk’s positions. For the x position, we start off with the parent chunk’s x position, and add on i* WidthGroundPrefab. The i is the variable we are looping over, so the first time through the loop, it’s zero – the x position is the same as the parent chunk. The next time through the loop it’s one, so the x position of the second piece of ground is transform.position.x + WidthGroundPrefab etc.

Next, call the InitialiseChunk method from Awake() by adding

InitialiseChunk();

to the end of the Awake method.

Make sure both scripts are saved. Go back to Unity and delete the ground that is already in the scene, then press play. You should see a long row of ground. Zoom out and click on the chunks to see where each is. Save the scene and the project.

Unity tutorial infinite terrain

In this tutorial, we’ve started coding the terrain. In the next one, we’ll add to the code to make the terrain infinite, and we’ll start making a character controller.

Remember you can download the files for this tutorial on the Downloads page. See you next time!

PreviousTutorialButtonNextTutorialButton

One Comment:

Leave a Reply

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