Little MartianDiscordTwitterDevlogDemo!

Making Procedural World Gen Interesting

One of the things I love about games like Minecraft and Terraria is how incredibly varied the randomly generated worlds are. They invite and encourage exploration, and I wanted to try to put that same feeling of discovery into Little Martian.

But every time I researched procedural generation I kept coming across the same warnings: if not done well, procedural generation can lead to worlds that – whilst being unique – all sort of 'feel' the same. And Perlin/Simplex noise algorithms seemed to be at the heart of this issue: they make it easy to generate random worlds, but also make it easy to generate boring random worlds. Nevertheless, armed with bags of inexperience I forged ahead naively! :-D

I started with this excellent article by Red Blob Games, which explains the finer details of noise functions far better than I ever could:

I quickly had something working, but as expected, all the worlds were a bit boring! 10 minutes of exploring and you'd seen all they had to offer. But I wanted to stick with this Simplex/Perlin noise based approach for two reasons:

  1. I need the pseudo-random on-the-fly nature of it. I want to be able to regenerate exactly the same world repeatedly.
  2. I also need to be able to do it one chunk at a time, to avoid a costly up front world generation process.

The reason for these requirements is that I want to be able to adjust the climate of the world dynamically, warming it up, cooling it down, adjusting the moisture levels, raising the sea-level, etc, in response to the player's actions. That wouldn't be possible if I had to generate the entire world up front. (Hopefully I'll explain all of that in a follow-up post).

So I began looking at ways to make world generation more interesting. Here's what worked for us, your mileage may vary:

Lots of items

Given the retro art-style I didn't have a lot of scope to vary the base tiles for each biome. The colours of them vary, of course, but there's quite a bit of texture re-use. So instead, I try to bring the worlds to life with more variation in the items that occur naturally in each biome. For example: grasslands, warm forest and cold forest biomes all have the same base tile (grass), but the items found varies greatly: "grasslands tend to quite bare, with lots of tall grass and plants, whereas the forest biomes contain lots of trees, mushrooms, plants, fallen trunks, etc.

Generate more noise values

I started with just three noise values: temperature, moisture, and elevation. I generate each of those for each cell, then map from those to biomes:

  • Less than sea-level? Then it's ocean.
  • Moisture low and temperature high? Then it's desert
  • etc...

This got us so far, but it didn't help with 'special' biomes such as the "void", "magma" and "sulfur fields". For these I generate extra noise values, and I let these special biomes override regular biomes, though there are some exceptions, such as "magma" biomes can only appear where it's hot.

Apply transformations to noise values

Noise functions tend to generate noise values that give cloud-like textures, with areas of low values and areas of low values all pretty uniform in shape and size. For the "mineral vein" biome I wanted to generate curved strips that sweep across the landscape in long arcs, so I calculate two noise values mv1 and mv2, then combine them like so: mv = 2 * (0.5 - abs(0.5 - mv1)) * mv2.

Let special biomes influence regular biomes

A cell gets the "mineral vein" biome if the mv value is above a threshold of 0.8. However, I also add a percentage of the mv value to the elevation value, meaning that the landscape around mineral veins is lifted up and they are surrounded by rocky, mountainous biomes. Also, this means that I sometimes see the same sweeping arc shaped pieces of land in other places, where the mv value isn't quite above the threshold.

Vary climate more gradually

Within the space of just 4 or 5 chunks (8 x 8 tiles) the temperature can range from very cold to very hot, giving a dramatic change in biomes. I also generate a 'base temperature' noise value that varies far less dramatically, changing only by at most 0.01 per chunk. By combining this base temperature with the local temperature, the climate varies gradually across the world, but there can still be localised hot and cold areas.

Prevent special biomes close to the spawn point

This feels a little artificial, but seems to work quite nicely. We don't allow special biomes to be generated too close to the world spawn point. I achieve this by applying a transform to each of the special noise values if the distance to the spawn point is less than the threshold for that biome type. This has a practical benefit: the player cannot spawn in or near a biome they aren't equipped to deal with early on in the game, but also it encourages/forces more exploration! :-D