Last time, I promised that in this chapter we’d start generating trees and forests. I’ll show you the forests soon, but before we go any further with this project, we need to move this over into a real graphics engine. As neat as it is, the command line isn’t exactly fast/powerful enough to draw the detail we’ll need as we keep going. To this end, I decided to work with libGDX because it’s a framework I’m already somewhat familiar with. It’s basic enough that I have a lot of graphical freedom and efficiency, without having to do a lot of the background work myself. It’s also useful because it lets me create a helpful visual demonstration of how “brightness” in Perlin noise translates to “elevation” in our map:
On the left, you can see what the Perlin noise actually looks like: a 2D map of gray values, with a higher value corresponding to a brighter pixel. On the right is the final result with those gray values mapped to different terrain types, and the middle has both overlaid to make what’s going on a bit clearer. Like I somewhat failed to explain last time, the brighter a pixel is, the higher we say its elevation is. Everything below a certain value is water. As we get higher, the terrain becomes sand, dirt, grass, and finally snow. With that out of the way, we can almost move onto trees, but first we need to talk about time travel.
The Vaguely Sci-Fi Side of Terrain Generation
In the real world, we think of “time” as being a fourth dimension in our three-dimensional space. However, when we’re generating noise we only care about two dimensions (latitude and longitude). Because of this, we can say that time works as the third dimension in our two-dimensional space. The practical upshot of this is that if we move along the z axis (“time”), we can get a completely different map without having to regenerate the whole formula. This saves time and lets us get away with being way lazier.
So, how to we apply this to creating forests? Well, first let’s think about what the noise values meant when we were generating the initial terrain. Every cell of our Perlin noise is given a decimal value somewhere between 0 and 1 (I’ll call these “Perlin values” from now on). When we were creating the terrain, we multiplied those values to determine the elevation of each cell, making it one of 256 different “states” (heights). However, when we’re planting trees, there are really only two states we care about: “There is a tree” and “There is not a tree”. So, instead of mapping those values to different elevations, we’ll say the value between 0-1 is the percent chance of a tree being spawned. As a result, areas that would have higher elevation when generating terrain instead have denser forests when planting trees. For a little more clarity, we can represent what Perlin values mean in different contexts with a table:
Of course, we can’t just use the same map we used for determining elevation, because that would just result in forests and mountains being in all the same locations. Thus, we move the noise forward along the z axis to get a brand new map. The final result, with forests scattered around the land, is shown below.
There we have it. By doing almost zero additional math, we’ve added life to our little planet!
The Planet Prettification Project
It was around the time that I was getting forests to work that I decided this world sadly can’t just be a bunch of colorful boxes. Up until now it’s worked out fine, but as the world becomes more complex and gains more types of terrain, we’ll need a clearer representation of the land. Naturally, that means it’s time to break out the “functional”-tier pixel art:
So with that, our world is starting to actually look like a world! Now we just need some polar ice caps to melt. Next time, we’ll introduce global climate, deserts, and actual ice instead of just tall mountains!
P.S. I hope this chapter was slightly more coherent than the last one. This time I took a bit more effort to get my thoughts in order, and I’m still trying to get a hang of how to explain what I’m doing. I don’t want to get too into the nitty-gritty of the math/programming so a general audience can sort of get what’s going on, but it makes it hard to determine which details are worth mentioning. My point is, feedback helps, so please leave a comment if you are so-inspired.