By Nicholas Piazza
Some of the types of video games that I like to play are open world games and procedurally generated games. Open world games typically allow the player to explore a large map, complete quests, find items, and just mess around. Procedurally generated games are generated randomly based on your progress through the game. For open world games, the developer might have a pretty good idea of what the topography is supposed to look like, but coding every square meter of elevation, water content, and shading can be a pain. With procedurally generated games, the world changes as you move through it and discover different areas. For the procedurally generated games, you cannot do this by hand. There are too many different possibilities for what a game map could look like to have a team of developers create this. To solve these problems, game developers utilize randomly generated data with a few tweaks to create realistic terrain for their characters to navigate.
Two of the common algorithms that are commonly used are Perlin Noise and Simplex Noise. Perlin noise was developed by Ken Perlin in 1983 to create more realistic terrain generation. Simplex Noise was also developed by Ken Perlin in 2001 as an improvement to Perlin Noise. This version of the algorithm utilized less computing power than Simplex Noise, allowed it to scale to both four dimensional and 5 dimensional values, and made a more continuous gradient across the generated terrain. These functions are commonly used today and allow developers to create terrain with relative ease.
It’s relatively simple to get started with your own randomly generated two dimensional terrain. For this post, we will be using Python code samples. For Simplex Noise generation we will be using the OpenSimplex Python library. For showing this terrain, we will be using Pillow, which is a fork of the defunct Python Image Library (PIL). First, let’s try creating just a simple distribution of elevation. In Figure 1 we can see that we instantiate an instance of OpenSimplex, Pillow, and a map space of 300 x 300 pixels. We then iterate through all of the pixels on the map, use Simplex Noise to get a random value, assign it a greyscale color, and then print it. We can see in Figure 2 what the result of this is. We essentially get a large grey blob. This isn’t much, but it’s a place to start.
That bit of “terrain” that we got from this seems a little too much like the static that you used to see on a TV set when the antennas weren’t aligned properly. We can change this by adding more layers of Simplex Noise on top. We do this by modifying the period of the noise that is generated. This is akin to zooming in and out of the terrain. By doing this, we can create more smooth textures for our map. In Figure 3 we can see a few different changes. We can see that we have added a little complexity to the random noise generation. We have used a total of three Simplex Noise functions with a few modifications. Inside the function we multiple the x and y values to increase the period. We then multiply the result of the function by a proportional number to bring it back to a usable value. This allows us to create more randomly generated terrain that is then masked on top of the original calculated terrain. Figure 4 shows one of the results of this modification, but in the end we are still seeing that big grey blob.
Now, this randomly generated landscape is great and all, but this isn’t the 1940s anymore, so let’s add in some color. We can do this by adding biomes of sorts depending on the elevation. So, what we can do is use the same layering process that was discussed before, but add a few conditions that determine the colors of our map and what the terrain might be like. In our case, let’s go with three simple biomes: water, grass, and mountainous areas. In Figure 5 we can see that we have set the different colors, allowed Pillow to create color images, performed some redistribution of the data by using exponents, and rounded out the numbers so that we can get more distinct biomes. This creates a much more realistic image than we had before, and with much more color. We can see in Figure 6 that we have a large amount of green space with little bits of water and mountains interspersed.
In Figure 7 we can see a much more zoomed in view of this same terrain. One could imagine something like this being used as an overworld for a game. All of this was achieved in under 50 lines of python code. The amount of time that this can save a developer is great if they can tune the algorithm to produce the terrain that they want.
This little excursion into randomly generated terrain has produced some pretty cool stuff, but there are many more things that you can do to create more realistic terrain. Another technique for determining where water will be is to actually create two different Simplex Noise maps and overlay them. One of the maps is used for the elevation of the terrain and the other is used to determine the water table for the area. If the water table exceeds the elevation, then water will be placed there. Another way to use this is to create actual three dimensional maps instead of two dimensional maps. As stated before, Simplex Noise scales all the way into the fourth and fifth dimensions, so creating a map that changed with time or other conditions could be possible.
With all of this randomly generated terrain, another cool project that could be done is to use genetic algorithms to create creatures that would roam the land autonomously and see how they progress in different biomes, climates, and weather patterns.