Search code examples
c#perlin-noiseprocedural-generation

Cliffs terrain generation in minecraft-like game


I want to generate something like this: cliffs
I use Perlin Noise with sharp curve, my code produces those cliffs: my cliffs .

    for (int x = 0; x < sizeX; x++)
    {
        for (int z = 0; z < sizeZ; z++)
        {
            int floorY = map.GetMaxYNotWater(x, z);
            float n = hillsNoise.Noise(x, z);
            int hillY = (int)(curveHills.Evaluate(n) * 80f);
            if (hillY > floorY + 5)
            {
                for (int y = hillY; y > floorY; y--)
                {
                    map.SetBlock(GetBlock(y), new Vector3i(x, y, z));
                }
            }
        }
    }

How can I "cut" them to make hanging things?

I tried to do it like this with additional curve:

    for (int x = 0; x < sizeX; x++)
    {
        for (int z = 0; z < sizeZ; z++)
        {
            int floorY = map.GetMaxYNotWater(x, z);
            float n = hillsNoise.Noise(x, z);
            int hillY = (int)(curveHills.Evaluate(n) * 80f);
            if (hillY > floorY + 5)
            {
                int c = 0;
                int max = hillY - floorY;
                max = (int)(max * curveHillsFull.Evaluate(n)) + 1;
                for (int y = hillY; y > floorY && c < max; y--, c++)
                {
                    map.SetBlock(GetBlock(y), new Vector3i(x, y, z));
                }
            }
        }
    }

But it produces flying islands. Flying islands So what can I do to achieve the first screenshot results?


Solution

  • I can't say how Minecraft does it, but from my own experience with voxel terrain, the best way to approach it is to think of the voxel grid as something like a cloud: each voxel has a density, and when that density is high enough, it becomes a 'visible' part of the cloud and you fill the voxel.

    So rather than calculating the min and max Y levels, work on calculating the density value, something like this:

        for (int x = 0; x < sizeX; x++)
        {
            for (int y = 0; y > sizeY; y--)
            {
                for (int z = 0; z < sizeZ; z++)
                {
                    //This means less density at higher elevations, great for turning 
                    //a uniform cloud into a terrain. Multiply this for flatter worlds
                    float flatWorldDensity = y;
    
                    //This calculates 3d Noise: you will probably have to tweak this 
                    //heavily. Multiplying input co-ordinates will allow you to scale 
                    //terrain features, while multiplying the noise itself will make the 
                    //features stronger and more or less apparent
                    float xNoise = hillsNoise.Noise(x, y);
                    float yNoise = hillsNoise.Noise(x, z);
                    float zNoise = hillsNoise.Noise(y, z);
                    float 3dNoiseDensity = (xNoise + yNoise + zNoise) / 3;
    
                    //And this adds them together. Change the constant "1" to get more or
                    //less land material. Simple!
                    float ActualDensity = flatWorldDensity + 3dNoiseDensity;
                    if (ActualDensity > 1)
                    {
                        map.SetBlock(GetBlock(y), new Vector3i(x, y, z));
                    }
                }
            }
        }