For anyone that saw the initial screenshot of the game, it was a little….. boring. To keep things simple the terrain in the game was just a simple plane. This allowed me to focus on other systems in the game. Finally a couple weeks ago I started down the path of switching the terrain over to be 3d, and man I wish I would have done this sooner.
Here is what the game looks like now.
And zooming out as far as the game currently lets you.
And the entire map at a size of 300×300. This is our current target for the largest map size.
This is all procedurally generated, and getting here took some work.
I started by going through this youtube tutorial series. I recommend it for anyone getting started on procedurally generating terrain. It gave me a very solid foundation to build on.
One important thing I took away from the videos is that I needed a way to test out the terrain generation without having to recompile and play the game. Modifying fields in the inspector and seeing the terrain update immediately will save you a ton of time. I generated almost all of the screenshots for this post without having to stop the game and change any code.
The first step was using what I learned from those videos to generate some basic terrain that I was happy with. This uses a couple octaves of perlin noise and flat shades the result.
The look we are going for is low poly, and this didn’t quite feel right. My next step was adding a way to constrain the height of each vertex. This is done with something I called HeightsPerUnit. This defines the number of steps allowed between each full height unit. With a value of 10 for HeightsPerUnit the height of a vertex can be 3.0, 2.1, 5.2…. with a value of 4, the height of the vertex could be 4.0, 1.25, 0.5…..
Generating a map with a constraint of 10 HeightsPerUnit results in this. The best way I can describe this is that it looks somewhat pixelated.
I found that the lower I set HeightsPerUnit, the less pixelated it looked. This is the result with a value of 2. It doesn’t look bad, but it isn’t quite the look we are going for.
My next idea was to vary each vertex so that it wasn’t perfectly on a grid. For each vertex, I modify both the x and y coordinates by a different random value between -VaryBy and VaryBy. Using HeightsPerUnit of 10 with a VaryBy of .25 gives the following result. This looks much better than the original HeightsPerUnit of 10.
The next step was figuring out how to ensure that the center of the generated map would include a flat area. Currently there are some buildings that spawn there and I wanted to make sure that they wouldn’t end up embedded in a hill.
The naive approach would be to set the height to 0 in a radius around the center. The problem with this approach is that you’d get a very unnatural looking center. Any hills near the center would be abruptly cut off. The solution is to use a curve to determine how much height is cut off.
This approach defines CenterRadius along with a FlattenAnimationCurve.
For a point x,y, determine the distance from the center.
If the distance <= CenterRadius then pass distance / CenterRadius through the FlattenAnimationCurve to determine how much height to chop off at this point.
Don’t allow the height to be set to less than zero.
The closer to the center, the more height is chopped off. As the distance approaches the value of CenterRadius less height will be chopped off. The curve helps ensure that the result looks more natural. The perfectly flat area in the exact center looks unnatural, but the transition away from the center looks good. Down the road this method will probably change to just make sure the center is “mostly flat”.
The animation curve
The resulting map with CenterRadius of 30
Seeing as the name of the game is currently FromTheFord, we definitely need to include a river and a ford in the map. For now the river is a straight line cut down the map a little off center. The result of this highlights the issue I mentioned above. This looks very unnatural.
Using the same approach I used for the center of the map, I defined a RiverFlattenDistance along with a RiverFlattenAnimationCurve and flattened out the terrain around the river. This has the side effect of making the river not be a uniform straight line. Currently any terrain below a specific height will be considered water.
The next step is getting some different terrain types into the map. The approach I used seeds the map with different terrain types at random locations. Then the map is iterated over, spreading the terrain one tile at a time to any neighboring unassigned tiles. This repeats until all tiles have a terrain type assigned. The result of this leaves a little to be desired. Most terrain areas intersect in clean lines and the result doesn’t look natural. To help things look a little more natural I modified the terrain spreading so I could assign a random chance to it.
The result without the random chance to spread.
And with only a 5% chance to spread
Next up I needed to fill the world with stuff. Without rocks, trees and bushes it looks very bare. The approach I’m using involves generating fractal noise and then determining the points where the noise is greater than nearby values. For placing trees in a forest tile, the radius used is very small. For placing trees in clay the radius will be much larger. The approach came from here. I’m not completely happy with this, but it works for now.
This is the world populated with trees
Along with all this work on generating the terrain I also implemented Unity’s NavMesh for path finding. This required constructing each terrain type’s mesh separately so that I could define the movement cost for each terrain type. Walking across the ford in the river should take more time than walking across a grass field. The map was already split into 9 regions, and now each region has a separate mesh for a terrain type that exists in it.
Here’s an example of the grass terrain in the center region.
With the introduction of several thousands of resources that can be harvested my code needed to be optimized. The foreach loops that were iterating across all of the resources on the map killed the framerate. I implemented a quadtree to handle locating resources and the difference in performance is amazing. While I was in that area I improved how the resource selection looks.
This is the current version of it
This was a ton of work, but man does it make the game feel better. Next up I am supposed to be working on fleshing out the crafting and production systems, but it seems like implementing the ability to modify the terrain would be so much more fun. Who wouldn’t want to level off a hill to build their village on top of it?