Chapter 11. The Game Level
Let's create a nonrepetitive, endless level that the player can enjoy.
In this chapter, we will cover the following topics:
Generating levels versus designed levels
Creating a level chunk
Planning LevelGenerator
Writing LevelGenerator
Instantiating random-level pieces at runtime
Using triggers to create and destroy level chunks
Generating levels versus designed levels
The next big chunk of development in our game is the level. A level is simply the environment that a player is placed in virtually. You are probably a gamer yourself, so I don't really need to explain this much. However, I want to talk about one thing—we need to make a decision on how we want the level to look and behave to keep the player engaged all the time.
A level can be either randomly generated during the game (for example, in Run), or have static, designed by level designer layout (for example, Super Mario Bros.).
There are pros and cons to both level types. A designed level can be customized very easily and is easier to develop in general. However, the player might not like the repetitiveness of the level at all.
If we choose the generating-during-gameplay approach, we have slightly more work to do. However, the level can be endless and random every time the player sees it. The player will always be challenged by different level layouts. Let...
Creating a copy of the level piece
We are now ready to write a clever method that will copy levelPrefab
and place it at the right location on the level.
Add the following method to the LevelGenerator
class:
When creating procedurally generated levels, we want to make sure that the level is different every time the player sees it. For our solution, we simply want to pick a random element from the levelPrefabs
.
Please take a look at Unity's reference and search for Random.Range
. You will realize that Random.Range
returns a randomly generated number that lies between two numbers (min
and max
).
So, if we use Random.Range
, passing 0
and the count of all level prefabs, we have at our disposal a random integer number. We can use this number as an index from the levelPrefabs
list. This is exactly what we are doing in line 33.
Great! Now we know how to generate a random number to help us pick the right levelPrefab
to copy and add to the level.
I have used the word Instantiate
a few times before. What does it mean? Instantiating simply means creating a copy of the object. Yet again, I encourage you to go back to Scripting Reference and read about Instantiate
.
Line 36 is where we are using Instantiate:
In this line, we are creating a copy of one of the levelPrefabs
elements stored under the randomIndex
value. We assign the instantiated object straightaway to the local piece variable. So basically, this is the line that creates an exact copy of the prefab and places it in the scene.
When instantiating a game object, we are creating a copy of the object. Unity, however, doesn't copy its parent assignment, so the instantiated object will be created on top of the hierarchy. To correct this, we set the parent to the piece object using the transform.SetParent
function on line 37.
Great! We know how to create a copy of a game object and assign a parent to it...
As you know Unity a bit, have you heard of Vector3 already? If you haven't, I will explain it very briefly. Vector3 represents a 3D vector and a point or direction. The Unity documentation says:
"This structure is used throughout Unity to pass 3D positions and directions around. It also contains functions for doing common vector operations."
Feel free to study more about Vector3 at this link: http://docs.unity3d.com/ScriptReference/Vector3.html. If you are not a math master, you will feel confused now. All I want you to remember right now is that Vector3 can be used to store the position of a game object in 3D space. It contains the X, Y, and Z positions in 3D space. That's it! Don't bother yourself with too much information about 3D vectors at this stage; it is a massive subject.
Line 39 is where we are creating a new Vector3 type variable to store the position we will move our level position to in the next few lines.
Note
You can use List<T>.Count
to access the current size of...
We went through some difficult coding recently. You might feel a bit uncomfortable still, but don't worry. The more time you spend coding, the more confidence you gain.
To test whether everything works correctly, we need to do some setup in the Scene:
Create a new GameObject
and call it LevelGenerator
.
Add a LevelGenerator
Component to the LevelGenerator
game object.
Create a new game object and call it startPoint
:
Position the start point game object in the scene so that it is below and behind the Player
game object. Thus, the first generated level piece will appear directly under the Player
.
Assign the LevelPieceBasic
game object as the first element on the LevelPrefabs
array.
Assign the startPoint
game object into the correct slot in the LevelGenerator
component:
Ready to test? Click on Play in Unity. If all went right, you should notice two initial level pieces generated. It should look more or less like this:
Congratulations! You just wrote a working part of a procedurally...
At the moment, Jake moves forward and eventually will run to the edge and drop. To avoid this, we will simply generate the next piece of the level every time the player leaves one level piece behind. We will also destroy the old and already used piece of level to keep things clean.
We will use the OnTriggerEnter
method to recognize when the player reaches the ExitTrigger
of a certain level piece:
First things first; we need to make sure that our level generator contains the functionality needed to extend the level. Let's add the Remove OldestPiece void
method to the level generator.
With your coding experience, you should easily understand line by line what we are doing in this method. If you don't, just remember that this method will remove the oldest levelPiece
from the level.
We are getting closer to a working endless level. The last piece of the puzzle that is missing is calling the AddPiece
and RemoveOldestPiece
methods when the Player
game object enters the trigger...
The code used in this chapter
Here are the pieces of code used in the chapter:
The code for LevelPiece.cs
:
The code for LevelGenerator.cs
:
Great! We now have all of the functionality we need for infinite gameplay. You just learned how to create reusable pieces of a level. You also learned how to populate the level pieces to create an illusion of an endlessly running game. Not bad for a beginner! Well done! In the next chapter, we will explain how to construct and implement a user interface for our game.