Book Image

Procedural Content Generation for Unity Game Development

By : Ryan Watkins
Book Image

Procedural Content Generation for Unity Game Development

By: Ryan Watkins

Overview of this book

Procedural Content Generation is a process by which game content is developed using computer algorithms, rather than through the manual efforts of game developers. This book teaches readers how to develop algorithms for procedural generation that they can use in their own games. These concepts are put into practice using C# and Unity is used as the game development engine. This book provides the fundamentals of learning and continued learning using PCG. You'll discover the theory of PCG and the mighty Pseudo Random Number Generator. Random numbers such as die rolls and card drafting provide the chance factor that makes games fun and supplies spontaneity. This book also takes you through the full development of a 2D game. Starting with level generation, you'll learn how PCG can make the game environment for you. You'll move into item generation and learn the different techniques to procedurally create game items. Thereafter, you'll be guided through the more abstract PCG areas such as scaling difficulty to the player and even generating music! The book helps you set up systems within your games where algorithms create computationally generated levels, art assets, quests, stories, characters, and weapons; these can substantially reduce the burden of manually creating every aspect of the game. Finally, you'll get to try out your new PCG skills on 3D terrain generation.
Table of Contents (18 chapters)
Procedural Content Generation for Unity Game Development
Credits
Disclaimer
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Random versus pseudo random numbers


The most important distinction to make is that PRNs are not random numbers. A truly random event would be something like a die roll. We could write some sort of physics simulation to simulate a die roll to achieve a random number. We could also take the static from a TV screen and plot it on an XY plane and take a single point to represent a random number. However, PRNs are preferred in game programming because they are easier to generate. The preceding examples would take too much processing power as they are a fairly complex idea. But a PRN generator is an equation that calculates a string of numbers. This also produces an added benefit of being able to find our way back to a certain generated result.

You could generate a more random sequence of numbers by grabbing points off our TV static graph, but what if you want to reproduce a result? Imagine we created an entire planet using a specific sequence of random numbers. Unless we generate that planet the first time, record the sequence used, and ship the game with the sequence included in the code, we might never be able to reproduce those results again.

Now imagine we generated trillions of planets. We would have to somehow come up with a system to store all the results of generating each planet on the first run. That just sounds unwieldy. A PRN generator, however, uses a seed number to generate a sequence, which will eventually repeat. So, instead of saving all the information needed to generate the planets, we just need the PRN generator equation and seed to regenerate all the planets at runtime.

Random number noise signals – on the left is a random number pattern that does not repeat and on the right is a PRN pattern that repeats

A seed in reference to PRNs, is simply a number either designated by you or by some other pseudo random means. The Unity Random method will acquire a seed from the system time if you don't provide one. The seed of a PRN generator can be stored as a variable like you would store any number in a script. This is useful when we need to recreate the sequence. We just plug our seed back into our number generator and get the same sequence again.

For example, imagine we used a sequence of PRNs to create a level. Let's say the numbers represent whether a room, a hallway, or a trap is placed in a certain area. Now, a player has just finished that level and we decide to delete the level to save space in the memory. But later, the player gets a quest that requires them to go back to that same level. If we keep the seed number we used to generate the level, we can put the seed back into our number generator, get the original sequence, and remake the level as it was initially.

As stated earlier, one of the side effects of PRN generation is that it is cyclical. There comes a point in which the PRN generator generates the seed again, starting the process over. This is important to consider as it might become a cause of repetition in some of your procedurally generated content. There are multiple factors in avoiding repetition, such as the size of the number, the value, and the sequence range. Unity's Random method should be enough for most cases though.

So, in short, you know that PRNs are not random numbers but they are close enough. The key points are:

  • PRN generators need a seed value

  • You should store the seed value so we can easily recreate the PRN sequence

  • PRN generators will eventually repeat

  • If repetition becomes a problem, look into creating a more complex equation for a longer seed range

PRNs are cool and all, but how are they used?

PRNs in PCG

You can use PRNs as a decision driver to PCG. As a developer you want to be concerned about minimizing minor detail decisions. These decisions could be tasks such as placing every single tree by hand in a forest scene. You want the scene to look realistic but placing all of them yourself could be very time consuming. You can use PCG for some of this decision making. Using some directed randomness to build the forest for you saves a massive amount of time.

A forest scene created with Unity Terrain Engine which uses PRNs

So it's time to get our hands on this! As previously stated, PRN generation can be a complicated mathematical problem but Unity has a built-in class for us called Random. Now, let's get exposed to PRNs in our first PCG example.

Random Hello World

We are going to start with an age-old classic programming example, the Hello World program. If you have been programming for a while, you likely have done one or more of these already. There will be a twist though. We are going to use PRNs to randomize how we say Hello World.

Note

Be aware that this book is using Unity 5.2.2. Some of the examples will be incompatible with earlier versions.

Classic Hello World

Let's begin by setting up the project and completing the classic Hello World program. Start by launching Unity and creating a new project. You can name it Hello World. You can also set the perspective to 2D since most of what we do in this book will be in 2D.

Unity launch screen

Once the project is loaded, we will create a new Text GameObject in order to render our Hello World to the screen. On the top toolbar, select GameObject | UI | Text. This will place a new Canvas object with a Text object child onto the scene. An EventSystem is also placed in the Hierarchy panel but you can ignore this.

Note

If you are unfamiliar with or would like to know more about Unity's UI features, Unity Technologies offers video lessons on the topic. You can find them at http://unity3d.com/learn/tutorials/topics/user-interface-ui.

Canvas is mostly off screen and you have to zoom out quite a lot to see the full view. Rather than zooming out, let's adjust the Canvas to occupy the Main Camera view space, as follows:

  1. Select Canvas.

  2. Select the Render Mode dropdown in the Canvas component section.

  3. Select Screen Space - Camera.

  4. This will open up a new field called Render Camera.

  5. From the Hierarchy pane, drag and drop the Main Camera object into the Render Camera field.

This will adjust your Canvas object to fit the Main Camera view. You might still need to zoom out slightly to see the edges of Canvas.

Workflow to get the Canvas in Main Camera view

You might have noticed at this point that there is some text on the screen. In the lower left corner of the Canvas, it says New Text in a default grey that is difficult to see. Let's change that.

Select the Text object, which is a child of the Canvas object. First, we will change the position. In the Rect Transform component section:

  1. Select the value in the Pos X field and change it to 0.

  2. Repeat for the Pos Y field.

The anchors are set to center so the Rect should snap to the center of the Canvas. Next, we'll change the size of the Rect so that we can make the text larger:

  1. Select the Width field and change it to 500.

  2. Select the Height field and change it to 65.

This will allow us to have much larger text. Note that if we had just tried to change the font size without changing the Rect size, the text would be clipped or would even vanish completely. Now let's get the text looking nice by going to the Text (Script) component section:

  1. Under Character, select the Font Size field and change it to 55.

  2. Under Paragraph, select the center alignment button.

  3. Select the Color field and change it to white.

    Note

    The center alignment button appears as in the Unity Editor.

Now our text is nice and visible. At this point, you can select the text in the Text field and delete it. We are going to have our script write the text for us.

Finished canvas and text formatting

Let's start scripting by creating a new C# script. On the top toolbar, select Assets | Create | C# Script. This will create a new script in your Assets folder under the Project pane. You can name the script HelloWorld.cs.

Open the script in MonoDevelop or your favorite IDE. We are going to use the following Code Snip 1.1:

1 using UnityEngine;
2 using UnityEngine.UI;
3 using System.Collections;
4 
5 public class HelloWorld : MonoBehaviour {
6 
7   public Text textString;
8 
9   void Start () {
10     textString.text = "Hello World";
11  }
12 }

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Let's take a look at what's happening in Code Snip 1.1:

  • Line 2: Be sure to include UnityEngine.UI or you won't be able to access the Text component

  • Line 7: This is our public Text object, which we will define in the Unity editor

  • Line 10: At the start of the scene, we will take our text object and assign the string Hello World to it.

That's all there is to it. Now, we just need to add the script to the scene. It doesn't matter which object you attach the HelloWorld.cs script to because we will declare the specific Text object the script acts on. To keep things organized, this way works well:

  1. Drag and drop the HelloWorld.cs script from the Assets folder to the Text object on the scene.

  2. Drag and drop the Text object from the Hierarchy pane to the Text String field in the Hello World (Script) component of the Text object.

Now, you can press the play button, and you'll see Hello World in a large font:

Hello World program's result

That completes the Hello World program. However, that's not all that interesting. In order to give this classic programming example some new flair, let's add some randomness.

PCG Hello World

Using our Hello World example, we will add PRNs into the mix and give our program some procedurally generated text. We'll start by editing our current HelloWorld.cs script. The goal here is to randomly display one of a few variations of the Hello World text.

You can achieve this by creating an array of different strings and having Unity's Random method choose a number from 0 to the length of the array. You will use that PRN as the index of the string array. In this case, our array holds the Hello World string in a few different languages. So instead of telling the Text object to display Hello World, we will tell it to display the contents of the array at the PRN index.

Note

Unity's Random.Range has a usage of (inclusive, exclusive). In our code, we use Random.Range (0, 4), which means 0 will be in the selected range but the range stops at 3. One reason for this is if we have a C# list, we can write the range as (0, List.Count) instead of (0, ListCount - 1).

You can find more information on Unity's Random at http://docs.unity3d.com/ScriptReference/Random.html.

Open the HelloWorld.cs script and make the following changes in Code Snip 1.2:

5 public class HelloWorld : MonoBehaviour {
6 
7   public string[] hellos = new string[4] {
      "Hello World", "Hola Mundo", "Bonjour Le Monde", "Hallo Welt"};
8 
9   public Text textString;
10
11  void Start () {
12    Random.seed = (int)System.DateTime.Now.Ticks;
13    int randomIndex = Random.Range (0, hellos.Length);
14    textString.text = hellos[randomIndex];
15  }
16 }

The changes in Code Snip 1.2 are as follows:

  • Line 7: Here you will declare a string array that we can call hellos, which will hold all our Hello World strings.

  • Line 12: This is the PRN generator seed, which we discussed earlier in the chapter. We are picking a random number to seed the generator. The seed comes from your computer's current time in processor ticks (which is somewhere around a millisecond).

  • Line 13: Here, we call Random.Range to choose a PRN from 0 to 3, which will be the index of hellos that we choose to display.

  • Line 15: This line is a modification from our previous example; here, we set the text display to our randomly selected Hello World string.

Head back into the Unity editor to see the changes. You should see the new Hellos field. If you expand it, then you will see all of the strings contained in the array. The script might also lose connection to the Text object. You can just drag and drop the Text object into the Text String field to reconnect it.

Hello World with PRNs program result

And that's it. You completed your first PCG capable program. Test it out by pressing the play button. You will randomly get one of the four Hello World strings displayed in the Game screen. There are only four choices so you might have to try a couple of times before you start seeing any variation.