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's choose this approach. If you are feeling a bit confused now, I will break it down into a few features of a level, which are as follows:

  • A level is made up of level chunks. Each chunk is simply a part of the level.
  • Each level chunk will be predesigned.
  • The level will be generated randomly by placing the chunks one by one.
  • A chunk will be placed in the front of the Player game object and destroyed behind the Player game object.
  • Level chunks will be placed seamlessly next to each other so that the player has a feeling of continuous gameplay.
Generating levels versus designed levels

Individual level chunks in the preceding illustration are represented by rectangles with black edges. The section marked in the center of the image is the viewport visible to the player through Unity's camera.

So, in simple words, the player is traveling from left to right. At a later stage, we will make the camera follow the player. When the player game object enters the trigger (the green rectangle), the oldest chunk on the left-hand side will be destroyed; at the same time, we will instantiate a new chunk in front of the player.

Great! So, this approach gives us an endless level that generates itself, and technically the player can play forever.

Creating a level chunk

The level chunk is the most important part of the level. It's like a Lego piece. A single piece can't bring about much fun for the player. However, when you take a lot of Lego pieces and fix them together, you can build a structure that's really entertaining. Our level will work exactly the same way. Level chunks right next to each other will create a nice gaming experience (and fun) for the player.

Before we talk about coding the level chunks, we need to make sure that we understand the fundamental parts of each chunk:

Creating a level chunk

This is a level chunk. It can contain whatever you wish to add to it. It's up to you to design the chunks. You need to remember, however, that every chunk must have the following features to fit your game:

  • exitPoint—red dot: This is the point in 3D space where the next chunk will be placed to match this chunk
  • startPoint—greenDot: This is the pivot point of the parent chunk that is plugged into the exit point of the previous chunk
  • exit trigger—green trigger on the right-hand side: This detects when the player went through the chunk to tell LevelGenerator to destroy the old piece and instantiate a new piece
  • kill trigger—red box: This is an optional trigger that kills the player on contact

So, this is it! A very simple part of a level that we call a level chunk. Let's do something exciting now and write a -procedural level generation.

Planning the LevelGenerator class

Remember that we are not writing any code without quick planning. Let's quickly think about the LevelGenerator script and the functionality.

We will definitely need to write methods for:

  • AddPiece, which is the level chunk right behind the last level chunk that is already generated
  • RemoveOldestPiece, to keep Unity clean of already used level chunks
  • RemoveAllPieces, to cleanse the level of all chunks
  • GenerateInitialPieces, to generate a few pieces straightaway when the game starts

Don't panic! I promise to go through this gradually. In fact, most of the statements that we will use in these four basic methods are already well known to you. I have used the term piece instead of chunk, but these are the same things. Let's stick to piece instead of chunk in our code.

Before we get to coding, let's prepare some assets.

Download and import LevelPieceBasic.unitypackage into your project. You will notice that the PlayerPieceBasic prefab has been imported into the prefabs folder. Drag this prefab into the Hierarchy window. Make sure that the entire PlayerPieceBasic game object in the Scene is placed on GroundLayer. Otherwise, the Player script won't detect that the player is on the ground. If we get this wrong, the player won't be able to jump.

At this stage, we will have quite an unorganized project hierarchy. It might be worth giving it a little cleanup. We won't use the FloorShort and KillTrigger game objects for a while, so let's delete them. Our Hierarchy and Scene windows should look more or less like this now:

Planning the LevelGenerator class

Let's take a look at the prefab we just imported. As you can see, there is nothing fancy here. It's just a straight floor piece without any obstacles or holes. It's perfect for a start.

If you look closely at what's inside LevelPieceBasic, you will notice a very simple structure, as we have mentioned before. There are lots of individual floor pieces, ExitPoint, and LeaveTrigger. At the moment, we don't have any scripts attached to them.

The key element for generating the level is knowing the position where the elements will appear. We will use the ExitPoint world space position for this. I understand that this might sound a bit confusing again. Let's write a very simple piece of code that we will use to manage the LevelPiece and hold the reference to the ExitPoint game object.

Create a new C# script, call it LevelPiece, and add the exitPoint public member, as shown here:

Planning the LevelGenerator class

That's it! No complicated code here! We are using the LevelPiece component just to hold the ExitPoint transform reference.

Add the LevelPiece component to the LevelPieceBasic game object, and assign ExitPoint by dragging and dropping the ExitPoint game object on top of the slot, as shown in this screenshot:

Planning the LevelGenerator class

You are probably asking yourself, "Why do we need this stuff right now?" Good question! We are just about to build the LevelGenerator class that will spawn level pieces in the level. The LevelPiece class will help us manage the pieces that are already in the game and will massively speed up their positioning correctly through the ExitPoint reference. Please be patient; everything will become clear to you soon.

Writing LevelGenerator

We are ready to start writing the level generator. Woohoo! But before we proceed, let's have a recap of what functionality we have planned to include.

We will definitely need to write methods for:

  • AddPiece, the level chunk right behind the last level chunk that is already generated
  • RemoveOldestPiece, to keep Unity free of already used level chunks
  • RemoveAllPieces, to clean up the level of all chunks
  • GenerateInitialPieces, to generate few pieces on the game start straightaway

Let's create a new script and call it LevelGenerator. Let's also change the way we talk about new code here. As LevelGenerator is quite an important class, I want you to understand it fully. That's why we will talk about variables as of now. Later, we will move on to methods.

Now add the following code to LevelGenerator:

Writing LevelGenerator

I believe you are quite confident with reading this code and have probably spotted that... this code doesn't do anything? Yes, that's correct. The first part of the LevelGenerator class will store some crucial variables.

As you can see in line 7, we are declaring a static variable. You already know that we need this static variable for easy access using the singleton approach.

Lines 8, 9, and 10 declare some useful variables:

  • levelPrefabs: This is the list of all already prepared level pieces. We will store all different level pieces that we want the generator to copy from.
  • levelStartPoint: This is a transform. We will plug in a game object in the scene that we will use to describe where the level is starting. In simple words, this is the position of the very first level piece in the level.
  • pieces: This is another list of level pieces. We will use this variable to store all level pieces that are in the game at the time.

What is the difference between the levelPrefabs and pieces variables? Basically the levelPrefabs elements are our blueprints. Every time we ask the generator to add a new piece to the level, the generator will randomly pick one prefab (blueprint), make a copy of it, place it in the scene, and add this copied level piece at the end of the pieces list. So, remember that the levelPrefabs list won't change during the gameplay. The pieces list, however, will constantly change as the player progress through the level.

Note

Remember to use the System.Collections.Generic namespace if you want to use the C# List<T>.

Commenting on your code

We just mentioned some very useful information about variables. Right now, it's really easy to come back to the code, look at the lines, and know what line is doing what. In the near future, however, you will notice how easy it is to get lost in the code. We are all human, and we do forget stuff very often. That's why it is a very good practice to comment on your code.

Comments are fragments of code that the compiler skips. The computer isn't really interested in anything written there; the comments are for the developer. In other words, comments are for you and other developers reading your code.

To add the simplest comment into your code in C#, you simply have to add two forward-facing slashes, followed by the comment text.

Let's add some comments into LevelGenerator now to make our life easier in the future, as follows:

Commenting on your code

This looks much better. It's much easier to look at this code now and know exactly what we are planning to do.

Note

From now on, we will try to comment on code as much as possible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset