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.
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.
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.
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.
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.
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.
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 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.