This chapter will introduce the idea of procedural content generation and one highly useful component, pseudo random numbers. Later in the chapter, you will use pseudo random numbers to create a derivation of the classic Hello World
program. For convenience, procedural content generation will be abbreviated to PCG for the remainder of the text. Let's also abbreviate pseudo random numbers to PRNs.
Here's a quick overview of what the chapter will cover and what you will learn:
Define PCG: What it is and what you can do with it
Discover PRN generation
Learn how PRNs relate to PCG
Use PRNs in our first program
Develop a new randomized PCG like the
Hello World
program
In this chapter, we will complete a very simple step-by-step example. The example in this chapter will be simple enough to help introduce newcomers to Unity and also serve as a refresher to those coming back after some time away. However, it is important to remember that the successive examples will be much more involved. It is best that you are fundamentally familiar with Unity and C# scripting in Unity.
Note
Unity Technologies offers a range of tutorials for beginners at https://unity3d.com/learn/tutorials.
You can also reference the Unity Documentation if you need to know the usage of any specific part of Unity at http://docs.unity3d.com/Manual/index.html.
Now, let's dive in and start learning.
We begin our learning adventure with the broad concept of PCG. The key word here is procedural. A procedure in programming, simply put, is an instruction to be executed. Procedures are the main paradigm in computer programming. A script you write in Unity is just a set of instructions or procedures we want Unity to perform.
You use procedures, methods, or functions as a means to communicate the instructions you want the computer to complete. We can use these same procedures to instruct the computer to generate content in many different ways. We can apply this idea to a broad range of programming disciplines such as data visualization, dynamic advertising, and so on, but in this book, we are using it for video games.
If procedural is the how then content is the what. Content can be anything we are presenting to the user. In our Hello World
example later in the chapter, our content will simply be text. However, video games have a wide range of assets that make up the content we want to deliver to a player.
Typically, we think of the levels, character models, and other art assets when we think of content in video games. But there is also textures, music, sounds, story, artificial intelligence, and more that together make up the content of a game. PCG is the concept or paradigm by which all these pieces of content can be generated with some well-written code. PCG can be applied to nearly all aspects of a game through scripting, and you will learn some of the main ways to do this throughout the book.
What's exciting about PCG is that we can let the computer take some of the responsibilities of the designer by giving it some instructions and letting it create parts of the game world on its own. We might even be surprised by the results. As developers, we usually do not like being surprised by our script's actions, but in this case, it's part of the plan.
PCG can also come in a few different forms for practical use. We can generate content from scratch, such as the texture see earlier, or we can generate assets from a set of premade parts, such as generating a tavern scene from premade props such as tables, chairs, barrels, and crates. Another option, though, is providing tools to the player to take on the role of creating content. The player creating content isn't necessarily PCG but you will have created a PCG system that now takes user input as a parameter. A great example of this is the popular game Minecraft developed by Mojang.
Minecraft is also an example of one of the most popular uses of PCG, randomization. Players in Minecraft can make structures and break down the land around them. However, the game's entire landscape is based on randomization. Randomization is used in many games, both virtual and table top. Randomness introduces a chance factor that creates fun out of unpredictability.
However, the most important thing about randomness in video games is that it is almost impossible to achieve true randomness on a computer system. This is why we refer to them as pseudo random numbers, because they are technically not random. We will explore this aspect of randomness, or pseudo randomness, later in the chapter with PRNs.
The reasons we might consider using PCG include unique, robustness, adaptability, and size. We might strive for our player to experience the game in their own truly unique play-through. We could use PCG to take the content that we have designed and make truly robust games that would take many hours to explore every inch of. We can make our game adapt to the player in interesting ways such as scaling the difficulty to easier or harder based on the actions of the player.
Size, though, is an interesting benefit to PCG. Well before games played with amazing HD graphics at 60 frames a second, they were mostly text based. Early computer systems were very limited both in processing power and storage memory. One of the earliest occurrences of PCG was in games that procedurally generated levels using ASCII characters. We can see an example of this in the game Rogue developed in the 1980s. We discuss Rogue and the subsequent sub-genre of games Roguelike in later chapters.
PCG was thus a solution, of sorts, to the fact that early computers really had no means to present graphics to the player. Graphics comprise the bulk of a game in terms of size taking a lot of processing power and memory. The procedurally generated ASCII levels of Rogue were calculated instead of being loaded from the file. This meant early computer systems could use memory and processing power as needed instead of needing a lot of memory all at once when you start a predefined game level.
We can also consider size savings in terms of our team as well. A designer/artist typically will need to make every piece of game content by hand. As games get larger, it becomes more difficult to create enough unique content within one game. Games lose their reward factor and players become bored easily when they see continuous repetition of in-game content. We then need to produce more content, which means more designers, artists, and individual assets. PCG helps alleviate this need by taking on some of the burden of producing unique content.
PCG can thus be viewed as a tool for the designer. There is a very creative facet to the idea of PCG. We can design pieces or modules of a whole, like a level or item, and use PCG to put them together in unique and interesting ways. We could also make 3D models, but then, we would have to generate the textures for them. Otherwise, we could generate full levels from scratch and add in some designed props. There are plenty of possibilities to fit the situation or team's needs.
You also have a unique opportunity to create games that can expand infinitely (or close to it). We will see this later in the book when we learn how PCG can be used to create a game level that never ends. Are you convinced that PCG is an amazing game development component?
Where we can apply PCG is an interesting question, as it can theoretically be applied to every aspect of a game. Here is a brief list of examples of where it has already been used:
Level layout—Blizzard Entertainment's Diablo series
Unique item creation—Gearbox Software's Borderlands series
AI behavior—Hisanori Hiraoka, Daisuke Watanabe, and Kyohei Fujita's dreeps
Texture generation—Farbrausch's .kkrieger
Model Generation—Speed Tree (which can be used with Unity)
Storyline—Bethesda's The Elder Scrolls 5: Skyrim Radiant Quests system
Music—Ed Key and David Kanaga's Proteus
This list encapsulates some of the more popular uses of PCG. As a game lover, you are encouraged to research each of these games as they are wonderful examples of PCG. We will cover most of these topics in this book, but this is by no means an exhaustive list of how PCG can be used in game development.
So now you know what PCG is but what about implementing it? To put it plainly, PCG is just the idea that we can automatically generate game content. We will develop different algorithms and use certain programming practices to apply the idea of PCG to our video games.
One of the more popular ways to implement PCG utilizes randomness or random events to produce content. It is popular because it is easier to let randomness determine certain events over scripting every outcome. For example, we might let randomness determine which pieces are used to generate the weapons seen previously in Gearbox's Borderlands. We might use a pseudo random number generator and bind each piece of the weapon to a randomly determined number. Of course, keep in mind that this isn't truly random as we will soon discuss further.
Random numbers have been used in games for a very long time, from traditional card games to dice rolling in table-top games. Random numbers add a chance factor to games that make them exciting and forever unpredictable. The unpredictability of a game is exciting because it always offers a unique experience. You can introduce this randomness factor into your games with a little computer science in the form of a PRN generator.
PRNs and PRN generators are a highly researched subject in computer science, as they are central to cryptography and cyber security. If you ever look into cryptography, you'll find it heavily steeped in complicated mathematics. Luckily, Unity has a very easy method to generate random numbers. Certainly, the complexity of secure number generation isn't required for video games. Nonetheless, it is important to understand some of the theories behind what seems like magic.
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.
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?
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.
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.
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
.
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.
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:
Select Canvas.
Select the Render Mode dropdown in the Canvas component section.
Select Screen Space - Camera.
This will open up a new field called Render Camera.
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.
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:
Select the value in the Pos X field and change it to
0
.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:
Select the Width field and change it to
500
.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:
Under Character, select the Font Size field and change it to
55
.Under Paragraph, select the center alignment button.
Select the Color field and change it to white.
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.
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 includeUnityEngine.UI
or you won't be able to access theText
componentLine 7
: This is our publicText
object, which we will define in the Unity editorLine 10
: At the start of the scene, we will take our text object and assign the stringHello 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:
Drag and drop the
HelloWorld.cs
script from the Assets folder to the Text object on the scene.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:
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.
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 callhellos
, which will hold all ourHello 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 callRandom.Range
to choose a PRN from 0 to 3, which will be the index ofhellos
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 selectedHello 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.
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.
As an added challenge, you can turn this into a die simulator. Try on your own to have the script display a random number from 1-6. You can, instead of displaying text, display an image of a die face. Also, see if you can display the image and store the random number corresponding to the die face for reference.
You can also try changing the seed to a number of your choice. If you play the scene with a constant number, you will get the same result every time. Try different numbers for different results. This is the benefit of the seed value; even though we are introducing a bit of randomness to our game, we have a way to control it. We will explore the seed value further in a later chapter.
So you learned quite a bit of theory in this chapter and just touched the surface of how to apply it to games in Unity. You went over an introduction to PCG and why and how to use it. You were also introduced to PRNs and to how they are generated. We discussed what makes PRNs different from your average random number and the benefit a seed provides. You also learned why and how to use PRNs and then completed an example expanding on the classic Hello World
program.
So what's left? Well, we are gearing up to build a fully functional game throughout this book. We are going to look at some of the more popular applications of PCG in video games. In the next chapter, we are going to briefly get acquainted with Roguelike games, which is a popular game subgenre. Roguelike games are known for their procedurally generated content, so it is a perfect fit for our learning adventure.