A world generator and simulator sandbox – currently under development

Main menu

Post navigation

Hot Air About Water

Water is obviously an important chemical for any world to sustain life as we know it. It’s why so much of SagaSim’s world generation revolves around the distribution of water. Up to this point, we have oceans and inland lakes, but we’ve not discussed rivers, nor assignment of biomes. Both of these are influenced by climate, and biomes are also influenced by rivers. I will discuss how each is generated.

River Run

Let’s start by generating another new world, because that’s just so much fun. (You’ve probably gotten the impression by now that the world generation process is fairly disposable and I can make new ones quickly and easily. You’d be right!) Let’s start with the terrain and climate maps, since those will determine the behavior of the river generator.

Terrain to be used for river demo.

Climate map to be used for river demo.

After the river generation process is run, the river map is stored as a single image, where blue pixels signify the presence of a river and black pixels signify… well, nothing. They aren’t important to the river map. When overlaid the terrain and climate maps, you can see the rivers pretty easily and the ways they flow should make logical sense:

Terrain map with rivers overlaid.

Climate map with rivers overlaid.

You can see how they flow down from high ground to low ground, either working toward the coast or toward the lowest point they can reach. Sometimes, multiple rivers will meet at the same low point and join together. Sometimes, a river may flow around a low level long enough to create a lake. So, how are they actually generated?

It begins by selecting a pixel–any pixel–from the climate map. Each climate is either considered rainy or not. A river can only begin on a rainy climate, so the randomly-chosen pixel is checked, and if it falls on a rainy climate, then it is considered a valid starting point for our river. Then, the river generation process begins. The pixels surrounding our chosen starting point are examined. If any of the surrounding pixels are lower than the current one, they are added to the list of possible successor pixels. Out of that list, the generator always picks the lowest one available. If two or more pixels are tied for the lowest, one is chosen at random. Then, the process is run again from the next pixel. This is done a set number of times, based on the following formula:

river length < (width * height * 0.005)

Given our existing map dimensions (600×465), each river will run for up to 1395 pixels. That may sound like a lot, but bear in mind that once a river reaches the ocean, it has nowhere else to go, and once it reaches its lowest point, it will simply pool around that. Rivers can never climb uphill. The length limiter is admittedly arbitrary. One could generate much longer rivers by making that number larger. I’ve found that 0.001 makes them too short, but 0.005 produces lengthy yet not overwhelming rivers.

This process is run to generate a set number of rivers. I use 100 as a default, but it’s a user-defined option so you can have as many or as few as you like.

Intro to Biomatics

Biomes are easily the most complex part of the world generation, because they bring together all the other elements we’ve created so far: terrain, climate, and rivers. The available biome types are:

Tundra

Taiga

Montane Grass/Shrubland

Temperate Coniferous Forest

Temperate Broadleaf Forest

Tropical Coniferous Forest

Mixed Forest

Coastal Forest

Tropical Broadleaf Forest

Temperate Grassland/Savanna/Shrubland

Tropical Grassland/Savanna/Shrubland

Desert

Flooded Grassland/Savanna

Riparian

Wetland

Each biome type is associated with one or more climate types and one or more terrain types. For a given land pixel, the possible biome types are determined based on the terrain, climate, and whether any adjacent pixels are river or ocean pixels. In particular, riparian biomes must be adjacent to/on a river, and coastal forest biomes must be adjacent to an ocean. Once we have the list of possible biomes for this pixel, the logic is then very much like climate logic: surrounding pixels are examined for their biomes, and the biome generator will either pick one of those or (less likely) pick a random one from the possible biomes for this pixel. This produces a “banding” effect similar to the climate generator. Like the climate generator, the biome generator currently doesn’t wrap seamlessly at the left and right edges of the map. Unlike the climate generator, we only make a single pass to generate the biomes, rather than several. These issues will likely be corrected/modified at some point in the future, but they are sufficient for now.

Believe it or not, at this point we’ve taken care of the basics of world generation. We have a planet with a terrain map, we’ve laid out its climate, waterways, and biomes, and now we’re ready to start populating it with forms of life: plants and animals! Those will be discussed next time.

Note: In the course of working on this entry, I realized there was a bug in the river generator, which I’ve since fixed, though the images were generated with the buggy version. Let them serve as a lesson that there are always improvements to be made. Future blog entries will use the corrected river generation algorithm.

2 thoughts on “Hot Air About Water”

A few comments regarding the river generator: You are placing a lot of emphasis on the spring. This does of course make sense but aren’t you underestimating the importance of drainage basins and drainage divides? Most of the water flowing in a river doesn’t originate from the spring but is surface overflow of the area the river runs through. To correctly predict a river’s size and flow you would need to simulate drainage divides for your world to make sure you have consistent drainage basins.

You are correct, of course. My approach is to build these things up iteratively, and in a modular fashion. I want the river generation to be “good enough” at the start, and then go back to it later. One step I’d like to add to the generator at some point is to simulate erosion and actually have it carve drainage paths through the landscape.

Another approach would be to take every region I’ve identified as rainy (based on the climate) and simulate the effects of rainfall on it. As before, water would run from high ground to low ground, taking the quickest path possible, and in instances where there’s more than one path (that is, multiple adjacent pixels are tied for lowest), the river would fork and continue its downhill path in multiple directions. I actually had that in the code originally, but it created a huge mess and covered most of the map with rivers, so clearly I need to constrain it somehow.

One thought I had was to allow the length to be variable based on the river generation. Generation 1 would be where the river began at its highest point, then when it forks, those would become subsequent generations. Since a fork means the river now has half as much water flowing along each of two paths (simplifying here), its maximum possible length should also be halved, and so on until we’ve “run out” of water to flow or hit an ocean, whichever comes first.

So, yeah, I know my current model is very simplified and leaves out a lot of important factors. Luckily, I can go back and change any part of the generation process independently, and re-run the subsequent generation steps as needed. In the case of rivers, I’d only need to re-run the biome generator after that. (Well, and the species placement algorithm, which I haven’t done yet.)