Showing posts with label Midpoint Displacement. Show all posts
Showing posts with label Midpoint Displacement. Show all posts

Wednesday, June 15, 2011

Terrain Generation Explained

Hey guys I have been putting off writing this kind of a post for awhile (mostly because I didn't feel that my code was 'good enough' to show off), but I've had a request and so I will try and explain my method for terrain generation using a mixture of different techniques.

Let me start by saying that my way is definitely not the only way, or even the best way to create terrain; it just happens to work for me (for now). At first, what I tried to use was the Midpoint Displacement Algorithm for creating a rough sketch of ground-level blocks. This method was ok for the time, but I needed something else that gave me a more 'natural' look. This led me to the Perlin Noise algorithm, which turned out to be fairly easy for me to understand from reading a few good explanations of it. Before I put the code into my game, I also looked at techniques for cave generation, which was even easier for me to understand than the Perlin Noise.

For this reason, I decided to integrate the cave generation first as it was the easier of the two. My method for cave generation involves the classic 4-5 rule which states that a block is a wall if the 3x3 region centered on it contains at least 5 walls. Repeating this process n times gives decent looking caves in the map. The entire process is modified slightly in the last two iterations to further refine the shape of the caves and make them appear more 'natural'. Here is some code for example:

 private void CreateCaves(int i) { // i == Number of iterations  
      if (i == 1) {          
         for (int y = Height / 10; y < Height; y++) {            
           for (int x = 0; x < Width; x++) {  
             // Percentage needs to be 60% to make 40% filled  
             // NOTES 06/10: Perlin Noise method functional and not bad,  
             // "Poking holes" cave creation method pretty good.  
             // NOTES 06/14: Making the percentage 55% / 45% is pretty good  
             // with the addition of possibly skipping over the block if the   
             // depth is too little  
             if (rand.Next(100) <= 55) {  
               // PUTTING HOLES IN COMPLETELY FILLED MAP  
               // AS OPPOSED TO PUTTING BLOCKS IN EMPTY MAP  
               blocks[x, y] = LoadBlock('.', x, y);  
             }  
             else if (y < Height / 7)  
               continue;  
           }  
         }  
       }  
       else {  
         int neighboringWalls = 0;  
         string blockName = "";  
         for (int y = Height / 10; y < Height - 1; y++) {  
           for (int x = 1; x < Width - 1; x++) {  
             if (blocks[x - 1, y - 1].Texture != null) {  
               blockName = blocks[x - 1, y - 1].Name;  
               neighboringWalls++;  
             }  
             /////// Some block tests removed to shorten code \\\\\\\\\  
             if (blocks[x - 1, y].Texture != null) {  
               blockName = blocks[x - 1, y].Name;  
               neighboringWalls++;  
             }  
             if (i >= 4) {  
               if (neighboringWalls >= 6)  
                 blocks[x, y] = blocksDictionary[blockName + "SingleTop"];  
             }  
             else {  
               if (neighboringWalls >= 5)  
                 blocks[x, y] = blocksDictionary[blockName + "SingleTop"];  
               else if (neighboringWalls <= 2)  
                 blocks[x, y] = LoadBlock('.', x, y);  
             }  
             neighboringWalls = 0;  
           }  
         }  
       }  
     }  

The main difference between my code and the algorithm in the link I provided is that instead of filling in an empty map, I'm poking holes in a completely filled in map. This in my opinion gives different and better results for me.

After I added the cave generation to the game, I felt confident enough to tackle the Perlin Noise algorithm. Using the method provided in the link I gave above, I produced this code to create my surface terrain:

 private int[] CreatePerlinNoise() {  
       float[] values = new float[Width];  
       int[] realValues = new int[Width];  
       for (int i = 0; i < values.Length; i++) {  
         values[i] = PerlinNoise(i);  
         values[i] *= 10;  
         realValues[i] = (int)Math.Ceiling(values[i]);  
       }  
       return realValues;  
     }  
 private float Noise(int i, int x) {  
     if (i == 0) {  
       x = (x << 13) ^ x;  
       return (float)(1.0 - ((x * (x * x * 557 + 1049) + 2411) & 0x7fffffff) / 1073741824.0);  
     }  
     else if (i == 1) {  
       x = (x << 13) ^ x;  
       return (float)(1.0 - ((x * (x * x * 1303 + 2473) + 3229) & 0x7fffffff) / 1073741824.0);  
     }  
     else if (i == 2) {  
       x = (x << 13) ^ x;  
       return (float)(1.0 - ((x * (x * x * 4441 + 6277) + 7549) & 0x7fffffff) / 1073741824.0);  
     }  
     else {  
       x = (x << 13) ^ x;  
       return (float)(1.0 - ((x * (x * x * 4663 + 6007) + 6961) & 0x7fffffff) / 1073741824.0);  
     }  
 }  
 private float SmoothedNoise(int i, float x) {  
     return Noise(i, (int)x) / 2 + Noise(i, (int)x - 1) / 4 + Noise(i, (int)x + 1) / 4;  
 }  
 private float InterpolatedNoise(int i, float x) {  
     int y = (int)x;  
     float fractionalX = x - y;  
     float v1 = SmoothedNoise(i, y);  
     float v2 = SmoothedNoise(i, y + 1);  
     return MathHelper.Lerp(v1, v2, fractionalX);  
 }  
 private float PerlinNoise(float x) {  
     float total = 0;  
     float p = 0.5f; // persistance  
     int n = 4; // four octaves  
     for (int i = 0; i <= n; i++) {  
       float frequency = 2 ^ i;  
       float amplitude = (float)Math.Pow(p, i);  
       total += InterpolatedNoise(i, x * frequency) * amplitude;  
     }  
     return total;  
 }  

A high-level explanation of what happens in a Perlin Noise algorithm (as I understand it) is that the final product is an array of y values (for height) that is the size of all the x values in the map. To get these values, we basically generate random numbers for every x value in the array and linearly interpolate between them to 'smooth out' the shape of the terrain. The link I provided above was, for me, the best explanation I could find about how to make a Perlin Noise algorithm.

I am by no means the expert on Perlin Noise but if you have any questions please feel free to leave a comment below. This is a very basic overview of my technique for creating terrain and it is by no means the only way or the best way. Also, I intend to modify this technique before my game is complete, so feel free to use my code in your own projects, although it may not work with your specific game.

As always, thanks for reading and I will give a game update as soon as I have one.

Monday, June 6, 2011

Update on Terrain Generation

Hey guys, it's been a busy last few days. This weekend was fairly productive and even a little frustrating at times. However, after much digging on the internet and reading up on terrain generation techniques, I have decided (at least for now) to use a modified version of the Midpoint Displacement algorithm for my terrain generation.

So far, this seems to be working moderately well. The overall shape of the terrain (top layer, of course) is fairly randomized but not too chaotic. The issue I have yet to work out is my technique for putting the correct block (i.e. with the correct texture) with the correct sides, cutouts, corners, etc.

I have spent a fair amount of time tweaking my brute force if then else statements to handle the different cases, but am having difficulty when I get more than just a few cases to try. Anyway, here is a screen shot of my latest build in all it's glory.
Though I am having trouble at the moment, I am enlisting the help of the wonderful people on the Xna forums so I should except to have a better strategy for my blocks in the near future.

In the meantime, I will be working on either the player's inventory (which at this point would only include blocks he has 'mined') or fixing the map saving/loading which has no doubt suffered because of my new terrain generation procedures.

Check back often for updates!

Sunday, June 5, 2011

Terrain Generation using Midpoint Displacement

Well it's been a productive couple of days since my last post. I've been busy working on implementing a better random terrain generator for my game using some tiles that I made myself using Paint.net. I must say that I have never been a strong algorithmic programmer, but I have been reading up on the subject and from what I have found, an easier way to implement random terrain is to use the Midpoint Displacement Algorithm.

This algorithm repeatedly uses two points in space and places a midpoint between them. Then, one point on either side of the midpoint is created at a random height (or displacement) from the midpoint. This algorithm recursively calls itself and continues to create a rough terrain shape. The more times this is done, the smoother the effect.

I'm definitely not the expert on this subject, so if you would like to read more about it check out this post. In the meantime, here is a sample the results of my terrain generation code. Note that there is still much work to be done to clean it up and improve it. This is definitely the first time I've done this.

That's all for now...