You are here
Procedural Forest Generation - How I Did it
After posting my videos of the procedural forest generator, a commentor asked:
That's really neat man, nice work! I'm curious about the procedural world generation - did you do that part yourself, or was there a good library for it for Unity?
There are procedural terrain generators for Unity on the Asset Store but I wanted to roll my own. I decided to use a library to handle the hexagonal play grid, which I modified slightly, and then I go through some steps to gradually build the world up. So, based on a blog post I found about creating a forest in procedural worlds, I came up the this set of steps to create my own:
The first step is to generate a hexagonal grid with tile heights generated using Perlin noise. You could also create a terrain mesh instead from the noise data rather than using a hexagonal grid as you just need to iterate over the terrain data to generate the rest.
To help create more natural biomes I next generate temperature and rainfall values for each node in the grid. My maps are supposed to represent areas of square miles rather than whole continents so each map is assigned an average yearly rainfall value in inches and an average yearly temperature. I use yearly averages so I can configure the nodes and more easily use real world data to help create a more natural looking forest. I then use following formula to calculate rainfall and temperature values for each node:
map base value + (Perlin noise value * seed) = node avg temperature/rainfall
Next I assign a terrain type to each node based on the node's height plus it's temperature and rainfall values. For instance, a node next to water might be sand or it might be a bog depending on how much rainfall the node gets on average.
Water bodies are detected, classified, and named next. I am only doing fresh water right now and the bodies are classified simply by size at the moment. Names aren't displayed right now but will be used for maps and other parts of the player UI as the game is developed.
A stream is then run between the two largest water bodies using an algorithm that favors lower terrain. The algorithm still needs tweaking because it can still just cut through the map terrain along the shortest calculated route depending if the map settings are within the proper thresholds. This is one of the most taxing bits of code to run as it is really hard to find the nodes which are closest together in two water bodies due to the way Perlin noise is being used for node height (everything with a value of X or less is water). In the game, the stream is currently just represented by dark blue shaded water tiles with the source tile being shaded red and the destination tile being shade black.
Rock formations are then detected, classified, and named. Just like for water bodies, classification is based solely on the number of contiguous nodes in a formation. Rock props are added to the nodes that are part of a rock formation. Names are not displayed right now but will be used similarly to water body names (think Wind Rock, Elephant Feet, etc).
The last step is to generate the forest. The types of trees used are based on the map's average rainfall and temperature values. Perlin noise is used to determine the range to place trees for the first generation. After that, for X iterations, the forest grows in the following manner:
- Death pass — Once a tree is old enough it dies and is removed.
- Growth pass — Next all remaining trees grow one increment. Any trees that collide use a weighted formula which favors the larger tree to determine which of the colliding trees dies. The dead tree is removed.
- Seed pass — Any remaining trees cast seeds Y number of nodes away. Seed survivability is based on whether the node already has a tree or not plus a survivability chance based on temperature/rainfall/terrain type.
I plan on repeating step 7 for bushes, grasses, and other flora in the future. My next steps are to get something of a playable game working.