Tuesday, November 6, 2012

Procedural Terrain Generation

Procedural Worlds

I wanted to be able to generate worlds procedurally in the game so I could encourage exploring - and why explore unless there is tons of content to find?  I want to have many different variables interacting to keep it interesting, but still not have to create too many assets manually. Well, that's a huge task, so I just started with making a generated heightmap from some multifractal noise functions.  I'm not yet worried about the water or textures - just the shape of the terrain. I want to have a few huge mountains, but also want some flat areas, rocky areas, etc. It's surprisingly difficult to get things at varying levels of detail, and looking natural.

Parameter tweaking

The hard part is that all you're tweaking are parameters like lunicarity, or persistence. What effect does it have on the terrain if you set lunicarity to 1.7? The easiest way is to just experiment with different values until you see something that looks good.  This means you're going to be spending a lot of time tweaking random numbers and then looking at the result, and you will want as fast of a turnaround time as possible.

At first, I started with just hardcoding some numbers in my C# script. In Unity, all you have to do is expose some public variables on your script and it automatically gets shown in the editor as a panel of numbers. What's more, you can even tweak those numbers at runtime and update the values right there. I actually used this technique at first, and just regenerated the terrain every frame. It worked just fine for a 256x256 mesh, but obviously started getting much slower at higher detail levels, and I realized I couldn't just calculate the terrain every frame.

I tried doing the generation on another thread and updating on the fly, but it got pretty messy since Unity enforces a lot of things (like setting heightmap values) to be on the main thread, which kept me from improving performance much.

Programming the Unity Editor

I realized that I could actually just write some code to interact with the Unity editor itself, basically updating the terrain whenever I click a button (even when the game isn't running).  This way, I could update the terrain without building and running the game - I'm simply changing the height values on the heightmap. This turned out to be a great idea, because waiting for the game to start up every time I changed a parameter was getting annoying. I also wanted to have several interacting noise functions which were getting confusing to keep track of in public variables, so I split them into classes and gave them their own parameters, which they each expose on the control panel.

This way, I can define a class once, like "Ridges", and then have two instances that act at different scales.  I can also combine other classes in interesting ways without getting confused now.

Custom Terrain Editor, with the generated terrain above

Saving state

Unfortunately, all of these values are still variables within the code, which means they aren't saved anywhere. I wanted to keep track of each variable that I was setting so I could load them up when generating new terrain.  A simple key-value store in the filesystem was all I needed, so I quickly wrote a system for serializing the variables on each noise class into a file using System.Reflection.  Now, I can even make a few different kinds of terrain for different worlds without worrying about having one massive random function that handles them all.

I have a feeling all this will get more complicated as I start implementing more world values like gravity, temperature, etc. Really, this will be about generating the right plants, animals, and resources to be convincing - but that's for another post.

Weekend progress

Here is a video showing me driving around in a test vehicle to get a feel for the scale:



The textures, water, and vehicle are just placeholders for now - I am just trying to get a feel for the size of the terrain I'm generating. I am not that happy with the size right now. It might be fine if I'm just walking around, but I would like more room to explore on a planet.  This is basically what you'd get if you were in space and wanted to land on the planet at a given latitude/longitude. I would be able to generate a consistent chunk at a different coordinate value if requested, but I don't want to have to worry about generating it all at once or streaming yet.

I will think about it some more, but my goal for the game isn't to have the best terrain out there (the procworld blog clearly blows me out of the water on this one), I just want some terrain to aid with exploring. I think I'll spend a little more time on terrain, then move on to the programming aspect.

Thanks for reading!


No comments:

Post a Comment