The always up-to-date definitive guide to terrain generation: part four - hills!

Discussion in 'Resources' started by jtjj222, Aug 17, 2012.

Thread Status:
Not open for further replies.
  1. Offline


    Part one
    Part Two
    Part Three
    So far, we have created a world generator that has made a flat landscape. That is pretty boring. What we need is hills! How do we make hills? More importantly, how do we make hills that persist across multiple chunks? The answer is a noise generator (no, not that 15 year old drummer down the street).
    A noise generator is a piece of code that takes in a value, and outputs a different value based on pseudo-randomness. So, you might ask, what is the difference between a noise generator and a random number generator? Well, nothing, really. However, the noise generator does something called interpolation: it generates a whole bunch of random values, then "smooths out" the values between them. When we cover other types of noise, I will explain this in more depth, but for now this definition should suffice. Luckily, bukkit includes a built-in noise generator, so we don't have to go into a lot of detail as to how noise generators work. For now, just remember: it takes any number of values in, and outputs a corresponding value, using a seed. If you give it the same seed and input values, it will give you the same output value. The values that it generates aren't complete nonsense; If you give the noise value two similar input values, the output values will be similar. It is really cool how noise generators work, but that is for another section. If you are interested in the topic, I would recommend you read this.

    We will use bukkit's SimplexOctaveGenerator class. Bukkit provides other noise generators, but we can explore those later. Start by creating a new SimplexOctaveGenerator in your GenerateBlockSections method:
    1. SimplexOctaveGenerator gen1 = new SimplexOctaveGenerator(world,8);

    The noise generator takes an instance of our world so that it can use the world seed to seed it's random number generator, and the number of octaves it should use. Don't worry about that too much for now.

    Next, we set the scale. The scale is how "far apart" the random values are placed. Let's stop right here so I can explain something to you. You can use any number of input values for a noise generator, but 2d and 3d noise is the only useful kind in terrain generation (2 and 3 inputs, respectively). Why? Well, to make hills and valleys, we give the noise generator the x and z co-ordinates of a place in the world, and the noise generator gives us the maximum "height" of that location in the world. In this case, the scale is how far apart the peaks of the hills are. The peeks and valleys are the most extreme values, and the noise generator interpolates, or smooths out the values in between. In 3d noise, we give the x,y and z co-ordinates of a place in the world, and the noise generator gives us a density. We can use this to determine whether the block should be stone, grass or air. The scale in this case would determine how far apart the centers of the blobs it creates would be. The next section will be devoted entirely to 3d noise, but for now, let's focus on 2d noise.
    The scale must be a number between 0 and 1. The easiest way to do this is to set the scale as the reciprocal of the number of blocks distance we want between the peeks of the hills.
    1. gen1.setScale(1/64.0);

    Then, we loop through all of the x and z co-ordinates in the chunk, find the max heights of them, and set all the blocks beneath that height to stone. Note that the noise generator needs the real coordinates of the block, not the chunk coordinates. Remember that the noise generator gives us a value between -1 and 1, so we have to multiply it so that we actually see the hills. Don't worry about the frequency/amplitude.
    2. for (int x=0; x<16; x++) {
    3. for (int z=0; z<16; z++) {
    5. int realX = x + ChunkX * 16; //used so that the noise function gives us
    6. int realZ = z + ChunkZ * 16; //different values each chunk
    7. double frequency = 0.5; // the reciprocal of the distance between points
    8. double amplitude = 0.5; // The distance between largest min and max values
    9. int multitude = 64; //how much we multiply the value between -1 and 1. It will determine how "steep" the hills will be.
    10. int sea_level = 64;
    12. double maxHeight = gen1.noise(realX, realZ, frequency, amplitude) * multitude + sea_level;
    13. for (int y=0;y<maxHeight;y++) {
    14. setBlock(x,y,z,chunk,Material.STONE); //set the current block to stone
    15. }
    16. }
    17. }

    Make sure you delete the grass populator, then build and test it.
    Just in case you are lost, here is the project so far, and here is a picture:
    For those of you who wanted to see, at scale 1/64.0, there is ( sqrt(20 squared + 59 squared) = 62.3) blocks distance between those peaks. Pretty damn close to 64.
    The peeks are so steep because we set the multitude to 64. Set to 32, it looks like this:
    (note I had fov=quake pro for that pic)
    You can tinker with the values later, but now it's time to learn how to use multiple noise generators together. There are many ways to combine two or more generators, but this way is the simplest:
    1. Math.max(gen1.noise(...),gen2.nosie(...));

    Combining two generators with multitudes of 32 and 16, and scales 128 and 64 respectively, I get this result:
    Stay tuned for the next part, which will cover 3d noise!

    (placeholder for links to newer sections and errors)
    Part 5:

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
    Last edited by a moderator: May 27, 2016
    PDKnight, mactso, Hellgast and 7 others like this.
  2. Offline


    Cant wait for next tut
  3. Offline


    The world generates completely flat, what am I doing wrong? D:
    2. public byte[][] generateBlockSections(World world, Random rand, int ChunkX, int ChunkZ, BiomeGrid biome) {
    4. byte[][] chunk = new byte[world.getMaxHeight() / 16][];
    5. long seed = world.getSeed();
    6. SimplexOctaveGenerator sog = new SimplexOctaveGenerator(world, 8);
    7. int sogScale = 64;
    8. int realX, realZ; //different values each chunk
    9. double frequency = 0.5; // the reciprocal of the distance between points
    10. double amplitude = 0.5; // The distance between largest min and max values
    11. int multitude = 64; //how much we multiply the value between -1 and 1. It will determine how "steep" the hills will be.
    12. int sea_level = 64;
    14. sog.setScale(1/sogScale);
    16. for(int x = 0; x < 16; x++) {
    17. for(int z = 0; z < 16; z++) {
    19. realX = z + ChunkZ * 16;
    20. realZ = z + ChunkZ * 16;
    22. double maxHeight = sog.noise(realX, realZ, frequency, amplitude) * multitude + sea_level;
    24. for(int y = 0; y < maxHeight; y++) {
    26. setBlock(x, y, z, chunk, Material.STONE);
    28. }
    30. if(biome.getBiome(x, z) != Biome.EXTREME_HILLS)
    31. biome.setBiome(x, z, Biome.EXTREME_HILLS);
    33. }
    34. }
    36. return chunk;
    38. }
  4. Offline


    Socscale is being rounded; make it a double instead :D
    Limeth likes this.
  5. Offline



    I just found this series of tutorials. All of them are awesome! Thanks a lot!!!

    When playing around with the values of this part a bit, I suddenly got an Error when the server tried to generate a new chunk. It turned out, that the peak of one of the "mountains" was higher than the maps max (256). So I would suggest to change line 13 in your code to

     for (int y=0; y<maxHeight && y<world.getMaxHeight(); y++) {
    And one Question: will there be a part 7, 8, 9, ... ? Because I would love to lean more of this great stuff! Especially the pros, cons and Posibillities of different noise generators.

    regards, minoneer
  6. Offline


    Fixed both in the latest tutorial:
Thread Status:
Not open for further replies.

Share This Page