Book Image

Procedural Content Generation for C++ Game Development

By : Dale Green
Book Image

Procedural Content Generation for C++ Game Development

By: Dale Green

Overview of this book

Procedural generation is a growing trend in game development. It allows developers to create games that are bigger and more dynamic, giving the games a higher level of replayability. Procedural generation isn’t just one technique, it’s a collection of techniques and approaches that are used together to create dynamic systems and objects. C++ is the industry-standard programming language to write computer games. It’s at the heart of most engines, and is incredibly powerful. SFML is an easy-to-use, cross-platform, and open-source multimedia library. Access to computer hardware is broken into succinct modules, making it a great choice if you want to develop cross-platform games with ease. Using C++ and SFML technologies, this book will guide you through the techniques and approaches used to generate content procedurally within game development. Throughout the course of this book, we’ll look at examples of these technologies, starting with setting up a roguelike project using the C++ template. We’ll then move on to using RNG with C++ data types and randomly scattering objects within a game map. We will create simple console examples to implement in a real game by creating unique and randomised game items, dynamic sprites, and effects, and procedurally generating game events. Then we will walk you through generating random game maps. At the end, we will have a retrospective look at the project. By the end of the book, not only will you have a solid understanding of procedural generation, but you’ll also have a working roguelike game that you will have extended using the examples provided.
Table of Contents (19 chapters)
Procedural Content Generation for C++ Game Development
Credits
About the Author
Acknowledgment
About the Reviewer
www.PacktPub.com
Preface
Index

Seeds


We just created a program to generate pseudorandom numbers, but every time we run it we get the same results. We know that these numbers are the results of complex equations and algorithms, so why are they the same? It's because each time we run the program, we're starting with the same seed.

Defining seeds

A seed provides a starting point for an algorithm. So, in the previous example, yes we're using complex algorithms to generate numbers, but we're kicking off the algorithm at the same point each time. No matter how complex the algorithm is, if you start at the same point, and perform the same operations, you're going to get the same results.

Imagine that we have three people, and each person is about to walk the same path by 5 steps. If they all start from the same square, they will end at the same square:

Now, in the next diagram, we give these three people unique starting positions. Even though they are doing the same actions as before, and are on the same path, their results are different because they started from different locations:

In this analogy, the path is the algorithm, and the starting square is the seed. By changing the seed we can get different results from the same actions.

You will have most likely used seeds before and not even known it. Games that procedurally generate worlds, such as Minecraft and Lego Worlds, give you the option to set a seed manually before generating a world. If your friend generates a world that looks great, they can grab their seed and give it to you. When you input that seed yourself, you kick off the algorithm at the same place that your friends did and you end up with the same worlds.

Using seeds

Now that we know what seeds are, let's fix the previous example so that we don't keep generating the same numbers. To do this, we will use the std::srand() function. It's similar to std::rand(), but it takes an argument. This argument is used to set the seed for an algorithm. We'll add the call to std::srand() before we enter the while loop.

Tip

You only need to set the seed once per run of the application. Once std::srand() has been called, all the subsequent calls to std::rand() will be based upon the updated initial seed.

The updated code should look like this:

// Random number generation
// This program will generate a random number each time we press enter.

#include <iostream>

using namespace std;

int main()
{
  // Here we will call srand() to set the seed for future rand() calls.
  srand(100);

  while (true)
  {
    cout << "Press enter to generate a random number:";
    cin.get();

    // Generate a random integer.
    int randomInteger = rand() % 201 + 50;

    cout << randomInteger << endl << endl;
  }

  return 0;
}

Now when we run this code we get different results! I got 214, 60, 239, 71, and 233. Don't worry if your numbers don't match mine exactly; they are both CPU- and vendor-specific. So, what will happen if we run the program again? We changed the seed. So we should get different numbers again, right?

Not quite. We called std::srand() and set a new seed, but each time we run the program we're setting the same seed again. We're kicking the algorithm off at the same position each time, so we're seeing the same results. What we really want to do is randomly generate a seed during runtime so that the algorithm always starts at a new position.

Generating random seeds during the runtime

There are many ways to achieve this, and your use case will determine which method is suitable. For us, as game developers, something relatively trivial such as the current system time will usually suffice.

This does mean that if you run the program at the exact same time you'll get the same results, but that's almost never going to be a problem for our use. C++ provides us with a nice function to get the current time, time(), which is located in <ctime>.

Let's update the program one last time and pass time() as a parameter in std::srand() so that we generate unique numbers with every run:

// Here we will call srand() to set the seed for future rand() calls.
//srand(100);
srand(time(nullptr));

Now, every time we run the program, we get unique numbers! You may have noticed that if you run the program multiple times in succession, the first number is always very similar to the last run. That's because between the runs time doesn't change a lot. This means that the starting points are close to each other and the results reflect this.

Controlled randomness is the key to generating random numbers

The process of generating random numbers is a huge component in creating systems that procedurally generate game content. There are lots of ways in which random data is generated, such as noise maps and other external systems, but in this book, we'll stick to these simple C++ functions.

We want systems that are predictable enough to give us control over them as developers, but they should be dynamic enough to create variations for the player. This balance can be hard to achieve, and sometimes games get it wrong. Later in this chapter, we'll look at some of the things that you have to watch out for when incorporating procedural generation into a game project to avoid this.