2

I would like help in smoothing my random terrain out to make it look more realistic. At the moment I'm just assigning random values to an array and setting tiles to the values stored in the array.

This gives this effect:

too random terrain

protected void generateLevel() {
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            int rand = random.nextInt(1000); //height
            if(rand <= 300){ //water level
                tiles[x + y * width] = 6;//set water
            }else{
                tiles[x + y * width] = 1; //set grass
            }
        }   
   }   
}

Please help me in making my terrain look more realistic, I would like someones opinion on how they might implement this into their game or mine. How do I smooth my terrain for a realistic effect?

Jongware
  • 22,200
  • 8
  • 54
  • 100
Max
  • 37
  • 1
  • 6
  • Can you please define realistic effect? Should your terrain be a island or more lands with some rivers? You can define pieces of rivers or lakes and put it randomly on your terrain – Mosa Oct 27 '14 at 22:35

2 Answers2

3

Instead of using random-for-each-point terrain, you may consider using perlin or simplex noise instead. This has the benefit of being smooth, replicable no matter in what order you read your map, and replicable with a given starting "shuffled array". A very good description and implementation is available at this post.

Community
  • 1
  • 1
nanofarad
  • 40,330
  • 4
  • 86
  • 117
2

Use OpenSimplex noise: https://gist.github.com/KdotJPG/b1270127455a94ac5d19

Do sommething like:

protected void generateLevel() {
    OpenSimplexNoise noise = new OpenSimplexNoise(); //optionally pass in a seed.
    for (int y = 0 y < height; y++) {
        for (int x = 0; x < width; x++) {
            double value = noise.eval(x / 24.0, y / 24.0, 0.5);
            if (value < 0) {
                tiles[x + y * width] = 6; //set water
            } else {
                tiles[x + y * width] = 1; //set grass
            }
        }
    }
}

There are also alternatives such as Perlin noise and Simplex noise. I'd skip Perlin noise entirely because it tends to exhibit visually significant grid artifacts (it lines all of its features up with its underlying square grid). Simplex noise in 2D is okay, though most implementations of 2D Simplex noise on the internet use a weird gradient set that results in artifacts down the negative diagonal. And 3D simplex noise is patent-saddled.

Perlin noise vs OpenSimplex noise, 2D slices of 3D: enter image description here

  • Left is noise(x, y, 0) grayscale
  • Next is noise(x, y, 0) > 0 ? white : black
  • Next is |noise(x, y, 0)| > 0.1 ? white : black
  • Next is noise(x, y, 0.5) grayscale

EDIT: Here's the code from the comment

Fractal noise:

OpenSimplexNoise n1 = new OpenSimplexNoise(seed1);
OpenSimplexNoise n2 = new OpenSimplexNoise(seed2);
OpenSimplexNoise n3 = new OpenSimplexNoise(seed3);

for each x,y {
    double value = (n1.eval(x / 48.0, y / 48.0, 0.5) + n2.eval(x / 24.0, y / 24.0, 0.5) * .5 + n3.eval(x / 12.0, y / 12.0, 0.5) * .25) / (1 + .5 + .25);
    //Do whatever you need to with that value
}

Rivers:

if (value > -0.1 || value < 0.1)
    water
else
    land

Biomes:

OpenSimplexNoise n1 = new OpenSimplexNoise(seed1);
OpenSimplexNoise n2 = new OpenSimplexNoise(seed2);
OpenSimplexNoise n3 = new OpenSimplexNoise(seed3);

for each x,y {
    double elevation = n1.eval(x / 24.0, y / 24.0, 0.5);
    double temperature = n2.eval(x / 24.0, y / 24.0, 0.5);
    double precipitation = n3.eval(x / 24.0, y / 24.0, 0.5);

    if (elevation < 0)
        water
    else {
        //decide biome based on temperature, precipitation, and elevation
    }
}
KdotJPG
  • 811
  • 4
  • 6
  • Thank you, this really helped and gave me some decent terrain.One last thing, is there any variables that i can edit to change how to terrain generates? – Max Oct 28 '14 at 09:52
  • Fractal noise: n1 = new OpenSimplexNoise(seed1); n2 = new OpenSimplexNoise(seed2); n3 = new OpenSimplexNoise(seed3); for x,y { v = (n1.eval(x / 48.0, y / 48.0, 0.5) + n2.eval(x / 24.0, y / 24.0, 0.5) * .5 + n3.eval(x / 12.0, y / 12.0, 0.5) * .25) / (1 + .5 + .25); //... } Rivers: if (v > -0.1 && v < 0.1) water Biomes: for x,y { elev = n1.eval(x / 24.0, y / 24.0, 0.5); temp = n2... precip = n3... if (elev < 0) water else { //decide biome from the three } } – KdotJPG Oct 28 '14 at 21:44
  • @KdotJPG: ouch! Excellent answer ... but this last comment! Please put that code in your post and remove the comment. – Jongware Oct 28 '14 at 23:52
  • Sure thing. I had to leave somewhere right after I made the comment so I hadn't gotten around to that yet. But it wouldn't quite make sense to /remove/ the comment unless both of us removed our comments :P – KdotJPG Oct 29 '14 at 03:11
  • Once again, thank you, i shall update you with some pics as soon as i get the time to work through all the values and implement textures for the biomes. I have a very good idea of how noise works now. I now have a basic map that looks somewhat realistic with beaches,seas and mountains. – Max Oct 29 '14 at 22:17
  • I guess it should be `&&` in "Rivers" section instead of `||`. As in [original comment's](https://stackoverflow.com/questions/26597987/smoothing-randomly-generated-terrain-in-java-for-a-realistic-effect/26599111#comment41849024_26599111) code. Tried to edit the post but it says "edit minimum 6 chars".. – Andrii M4n0w4R Mar 21 '20 at 20:49