Generating Natural Looking Video Game Worlds

Posted by admin on August 7th, 2016 filed in Development, Games, Hacks

No Man’s Sky was created by a small team, but it will contain a gigantic number of different, explorable, natural looking planets. HelloGames say when two people navigate to the same coordinates, they will see the same planet (minus any mining damage done by others). At the same time, the download is only a few Gigabytes, most of which is music. Clearly they neither designed all planets manually, nor are they random. How does No Man’s Sky do it?

Everyone who generates random maps for their video game notices immediately that such generated content turns out quite ugly: Your first random walls are too angular and in the way. Your first random hills are too spherical, your first random streets are redundant, and your random artefacts look just nonsensically arbitrary. Raw randomization is just as unnatural as raw geometrical shapes. They will fail to make the player believe that “an intelligent ancient civilisation was overgrown by wilderness” here.

For a few years now, I have been dreaming of writing (a tinier, simpler variant of) a game like No Man’s Sky. I investigated how game developers generate maps and textures, and learned that they use mathematical noise and fractal functions. Custom hash functions ensure that generated results are evenly distributed and cover all cases. Instead of the naive extremes of either using fully unconstrained random numbers, or using fully constrained formulas, smooth noise formulas output natural gradient values. When you visualise e.g. Perlin Noise output as grayscales, you do no see a clear curve or a geometric object. The distribution looks like blurry bubbles or a bit like “hills and valleys”:

The cool thing about Perlin noise is that it’s really just a function. You give the function some input values, and it returns a value. When you input neighbouring values, it will return a neighbouring value — and not a completely different, unrelated random number. Secondly, you can trust this function to give you the same output for the same input, reproducably, — that’s why two No Man’s Sky players can visit the same “randomly generated” planet and see the same hills.

Which decisions do you make based on noise functions during world generation? Totally up to you! (And totally independent of the game engine, which eventually draws the calculated data points.) Game developers do not use Perlin noise only to generate hills and valleys for heightmaps. They also use scaled layers of Perlin noise as factor to smoothen out textures, clouds, waves, color gradients, even for a more natural distribution of objects on the map. All you need to do is multiply the function’s input/output numbers (0.0-1.0) to fit the scaled ranges that you need. E.g. if you are generating RGB colors gradients, multiply the output by 255. If you generate continents, multiply by 10000 km, if you generate hills, multiply by 1km, if you generate wrinkles on a wall, multiply by 10 cm, etc.

It’s fun to design a mapping between Perlin noise layers and a world. Experiment to get a feeling for it.

In the simplest case, fill a 3D quadrant with cubes: Generate noise for input coordinates with different step sizes (try 0.001 or 0.01 or 0.1) and compare what it looks like. RGB color is a good way to visualise a cube’s Perlin values quickly. Or try this: For each output value that is larger than a chosen threshold (try 0.5 or 0.25 or 0.75), you attach a cube at a coordinate that is a multiple of the input coordinates — and otherwise you attach nothing. Different step sizes or different threshold values will make it look like you are zooming in or out of the noise cloud. Your cubes will be distributed along many small bubbles, or as one big bubble.

If the rendered demo output looks either all empty, or all full of cubes, that means you are inside a “bubble” of the noise. In this case, try larger steps. If the rendered output is a dense “too many cubes – no cubes – too many cubes – no cubes” pattern, try smaller steps. Play with these factors, and you’ll get the hang of what effects you can achieve with noise functions.

I don’t recall which step sizes I used, but the following screenshot already shows bubbles and caves emerging in the block. I wanted larger caves, so changed the step size (I divided by 10) to “zoom in”.

When you have a noise that looks like what you want — the perfect bubbles that look like hills or caves, for example — then start mixing it with other formulas. And who says that step values or threshold values must be constants? You can generate Perlin noises at different step sizes, and add them, layer them, stagger them. You can take a sphere or plane formula and intersect, or even use formulas as variable threshold values. Combinations several conditions — say, you only want to generate trees above water level, but below snow level, and you want them in some regions of continents, and within those regions only in a few clusters… etc.

Have fun experimenting. Understanding noise functions is only the first step. Advanced developers use many more functions (or write their own) — look at this to see what I mean.

Comments are closed.