6

I am experimenting with a new algorithm for random landscape generation. This method was my idea, so it may turn out to be impossible. In the meantime, I'm trying to smooth this thing out.

    public static void generate(){
    for(int x = -64; x <= 64; x++){
        for(int y = -64; y <= 64; y++){
            double val = 2d;
            for(int i = x - 4; i <= x + 4; i++){
                for(int j = y - 4; j <= y + 4; j++){
                    double w = Math.pow(hypotenuse(x, y, i, j), 1);
                    val += (1 * (random(i, j) / w));
                }
            }
            tileSet(x, y, val);
        }
    }
}

Before I explain this code, let me say that this is a 2D game, and so the heightmap is types of tiles instead of actual height. Water is lowest (0), then sand, grass, and tree (3). For testing, I am looping from -64 to 64 in both dimensions. For each tile, a value (starting with 2 or grass) is declared. Then we loop around this tile (-4 to 4 is an arbitrary amount, I have not been able to achieve significant differences in results by modifying this value, but 4 seems okay). Now, variable w stands for weight. The farther away each (i, j) point is from (x, y) the less weight the random value is given before it is added onto val. I have experimented with different exponents for w, and different multipliers for the next line (in the example both cases are 1); lower exponents and multipliers seem to widen the end result without necessarily smoothing, higher ones do the opposite and you get more trees closer to water with less grass and sand in between.

Here is the method random()

public static double random(int x, int y){
    Random random = new Random(seed * 17717171L + x * 22222223L + y * 111181111L);
    return random.nextGaussian() / 2d;
}

After those inner loops finished, the value of x, y is set.

Here's one run

too much roughness

Here's a multiplier of .5, meaning val += (.5 * (random(i, j) / w));

still too much roughness

As you can see, there is more sand and grass, significantly less trees (and water overall), but still just as many little bumps. It does not effectively change the scale therefore.

Thank you for your time!

Community
  • 1
  • 1
Joe Johnson
  • 177
  • 1
  • 7
  • 3
    This is probably unrelated to the 'smoothness' you are asking about but the way you are creating your Random seems wrong. You are creating a new one each time with a seed that appears mostly fixed. This will make the generated sequence more predictable. You should create a single Random instance and use it for the entire generation (and probably for the life of the program). – Radiodef Apr 03 '14 at 23:55
  • that's clever. what is the variable `2d` in your`random(...)` method? If you ask me, the first screenshot you posted looks like a great distribution. What is the actual question you are asking? – edthethird Apr 04 '14 at 00:12
  • 1
    @Radiodef The reason I am using a set seed plus the tile's x and y values multiplied by large prime numbers is because I want to get the same result for (x, y) no matter what order I generate the tiles in. In this way, though I am using a new Random object each time, I can get a predictable 2 dimensional random sequence for every value of "seed" – Joe Johnson Apr 04 '14 at 00:27
  • @edthethird 2d increases the scale of the normal curve returned by the random. The standard deviation is 1 by default but since my values range from 0-4 I was not getting the extreme tile types enough. As for my question, I feel like there is too much roughness at certain points. For example, I want a grass bordering a water to be very rare because that's a whole 2.0 of value apart just the next tile over. Using the multiplier I mentioned in my question spreads things out, but makes the values more likely to be sand and grass (close to my initial value of 2) – Joe Johnson Apr 04 '14 at 00:31
  • 1
    The thing about random is that, well, it's random. I recommend some simple rule-based post-processing to smooth your bumps. Something like "If tile surrounded by 6 or more of another type, convert tile to that type" and then iterate your rules a few times. – AndyG Apr 04 '14 at 00:37
  • 1
    Try to generate boundaries randomly rather than generating tiles randomly. Any way of generating tiles randomly without what you call 'smoothness' (which you didn't explain at all, by the way) would likely be quite hacky. – Bernhard Barker Apr 04 '14 at 00:42
  • Looks smooth enough for me. You will have to be more specific about what you want. – Niklas B. Apr 04 '14 at 00:49
  • Well as pointed out it seems like some kind of post-processing is what you will want here. Either simple rule-based or like a low-pass filter ("blur"). It would be easier to present a working answer for you if we had a [simple driver program](http://stackoverflow.com/help/mcve) to work with. – Radiodef Apr 04 '14 at 01:13
  • @JoeJohnson I think you need to use Random function without internal manipulation to get a normal distributed random numbers, then you need to define parameters that tell you the ratio between lands, green-lands, mountains, & water, then it'd be better to start with all land, then randomly create some water and mountain sections that are strongly connected .. good luck – Khaled.K Apr 07 '14 at 06:22
  • The question is very broad. You could consider one of the many, many landscape generators (http://en.wikipedia.org/wiki/Fractal_landscape). If your intention is only to *smooth* an existing landscape, the solution could be to set each cell value to the *average* of its surrounding cells, and normalize the result to be in [0,4) again. Also note that the "bumps" that you mention are not really "bumps". One value may be 1.999, and the other may be 2.001, but despite the small difference, it will cause another tile type to be shown. Further suggestions do not fit into this comment. – Marco13 Apr 07 '14 at 10:29

1 Answers1

1

What you're doing is known as inverse distance weighting. As the article points out, the key parameter is the exponent of the weight. So in your code, try

double w = Math.pow(hypotenuse(x, y, i, j), p);

taking p to be 0.25, 0.5, 1.0, 2.0, etc.

Also a slight variant on your algorithm that'd probably make reasoning about the algorithm easier: rather than generating a random value for each cell and adding to to neighbouring cells, start off by generating a random value at each cell corner, then use inverse distance weighting measured from the cell centres to calculate your cell values.

Using this scheme, the random values that contribute to a cell's value are all treated in a uniform manner, rather than one random value getting a weighting of 1 and the others getting inverse-distance weighted.

You might also be interested in gradient noise if you haven't encountered it already.

Andy Jones
  • 4,723
  • 2
  • 19
  • 24