Generating terrain for a video game is almost always done by hand, by artists, over a long period of time, sometimes even going to the lengths of placing each blade of grass that the player will see. This visual design and implementation of large scale AAA video games is the vast majority of their development budgets, spanning tens of millions of dollars that we obviously don’t have.
So we don’t do it that way. We can’t compete with it. Instead, we (like many other indie game companies) cut corners by making the game world generate itself procedurally, writing algorithms for the placement of trees, grass, rocks, rivers, mountains, glowing ruins and evil monoliths. Seriously, we have an algorithm for evil monoliths.
David and I have been arguing since the last post on game terrain about the “binning” of our biomes into the 9 categories. His argument being that it’s an unnecessarily simplistic system for such a potentially rich environment. My argument was, of course, that at some point the simulation is growing so intricate that we’re spending time where we shouldn’t be, and that we’d be far better off improving the game-play than the terrain, but if we’re doing things right, the game-play will be pretty heavily influenced by the terrain, so a certain amount of this makes sense.
So I have capitulated, may the internet have mercy on me. Here’s how the system works right now. (If you don’t think that math functions are cool, this might be a little dry. Sorry about that!)
Where once we had a very Minecraft-like concept of biomes with somewhat abrupt transitions from the mild areas to the cold tundras, we instead store a point for every terrain object in 2d space with axes of temperature and humidity, and a range of values for land fertility that a terrain object can spawn in.
We then make a little voronoi diagram of these points, assigning every possible point in the space to a particular object, so that if we want to gradually shift the temperature of the map to colder as you get further north or south, we can assign temperatures to every game space, and as they get colder they’ll get new types of trees, new ground textures, maybe snow instead of rain when there’s precipitation, and all that. The voronoi diagrams give us the ability to put as little or as much in these 2d maps as we like: the more we add to them, the more rich the world will be, and things just get shuffled around automatically.
The cost for this change is that the possibility space for our terrain (what could be placed for any given combination of temperature, humidity and fertility) gets so huge that we can’t actually visualize it easily. Thankfully though, we can test it pretty easily by generating a whole bunch of terrains and looking for things that appear out-of-place. We still run the risk of having some grass that’s badly coded in XML never appearing in the game, but if the world feels enough more like a real world that you can immerse yourself in it, it’s probably worth it anyway.
So that’s how we know what to place, but we also need to know where to place it. After all, forests are places of fertile soil, but there aren’t trees everywhere, nor are they evenly distributed in rows, and forests usually have a structure. Little trees by themselves often don’t survive, as with long thin bands of trees, but large clumps of trees create a habitat for other organisms which protect and nurture the trees themselves. We obviously aren’t going to simulate all of that, it would take us years and would drive us all insane, but if we don’t pretend we do, then you spend time wondering why the forests look weird instead of thinking about how to abuse them for profit.
I have fallen in love with 2D Perlin noise functions. Basically, you supply a series of values to the function and it gives you a 2D map of values. Properly tuned this function in particular has a really amazing capacity for creating naturally shaped anomalies with really pleasing gradients.
We generate a Perlin noise map for ground fertility, modify it just a bit, then apply a histogram to the map so that we can easily designate the top 35% of values to be forest, the next 50% of values to be grass, and the rest to be barren.
At this point we could call it good, but we have some pretty weird looking regions. Like a contour map, you’ll have weird rings of contours of the anomalies, and that’s just… not how real forests exist. They just wouldn’t grow like that. So there’s one more step.
We generate a voronoi diagram overlay of the map using about a hundred totally randomly generated points. For each of the cells in the voronoi diagram, we average up their fertility values, and if the average of that cell is within the values for forests, it’s a forest cell, otherwise it’s a grassland cell or a barren cell. We still get some of the neat grouping that the Perlin function gave us because some of the anomalies will span a few of the voronoi cells, but we get big clumpy forests with weird shapes now instead of little Dr. Seuss forests. I’m sure I can find a use for those guys somewhere though.
As far as the haunted mines and evil monoliths are concerned, we generate another perlin function for “abnormal activity”, and we skim off the top few (or more) game tiles with the highest values, and we focus our paranormal activities there. The really high-value tiles get some really nasty surprises, and the outlying ones have just enough dangerous and terrifying things that hopefully you won’t keep exploring in that direction unless you really want to.
So… this is a lot of work, not for a computer, and honestly not once you get it all working, but it’s a very sophisticated system, and because of that it’s going to be prone to breaking and being a huge pain in the ass. But the world in which you play a game is so important, and the placement of the smallest details are often the ones that reach us in the most profound ways, that if there’s anything we can do to make these moments happen in just your game, hopefully you will feel like all of this effort was worth it.
Also, again, as always, a brief disclaimer that we may just get extremely pissed off at this process and change it later, and there’s always more that we can add to this if we have time, but I’ll continue to fight feature creep because we want this game to come out one day.