0

I am trying to create a game with a Terraria like feel and I have browsed many threads/forums and can't seem to get anything working for myself. I've chosen the Simplex Noise algorithm to try and generate a side view game like Terraria but it's just a jumbled mess. I was wondering if someone could help me make a terrain generator using a Simplex Noise class I found online? I have blocks that are 32x32 that I want to spawn in for the terrain and then some I want at certain depths, etc. I'll post the code to the class below. I'm just starting with this random generation stuff and it is quite tricky for me.

import java.util.Random;

public class SimplexNoise {

    private static int grad3[][] = { {1,1,0},{-1,1,0},{1,-1,0},{-1,-1,0},
                                    {1,0,1},{-1,0,1},{1,0,-1},{-1,0,-1},
                                    {0,1,1},{0,-1,1},{0,1,-1},{0,-1,-1}};

    private static int p[] = { 151,160,137,91,90,15,
                            131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
                            190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
                            88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
                            77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
                            102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
                            135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
                            5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
                            223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
                            129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
                            251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
                            49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
                            138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};

    // To remove the need for index wrapping, double the permutation table length
    private static int perm[] = new int[512];
    static { 
        for(int i = 0; i < 512; i++) 
            perm[i] = p[i & 255]; 
    }

    // This method is a *lot* faster than using (int)Math.floor(x)
    private static int fastfloor(double x) {
        return x > 0 ? (int)x : (int)x - 1;
    }

    private static double dot(int g[], double x, double y) {
        return g[0] * x + g[1] * y; 
    }

    // 2D simplex noise
    public static double noise(double xin, double yin) {
        double n0, n1, n2;

        final double F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
        double s = (xin + yin) * F2;
        int i = fastfloor(xin + s);
        int j = fastfloor(yin + s);

        final double G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
        double t = (i + j) * G2;
        double X0 = i - t;
        double Y0 = j - t;
        double x0 = xin - X0;
        double y0 = yin - Y0;

        int i1, j1;
        if (x0 > y0) {
            i1=1; 
            j1=0;
        } else {
            i1 = 0;
            j1 = 1;
        }

        double x1 = x0 - i1 + G2;
        double y1 = y0 - j1 + G2;
        double x2 = x0 - 1.0 + 2.0 * G2;
        double y2 = y0 - 1.0 + 2.0 * G2;

        int ii = i & 255;
        int jj = j & 255;
        int gi0 = perm[ii + perm[jj]] % 12;
        int gi1 = perm[ii + i1 + perm[jj + j1]] % 12;
        int gi2 = perm[ii + 1 + perm[jj + 1]] % 12;

        double t0 = 0.5 - x0 * x0 - y0 * y0;
        if(t0 < 0) 
            n0 = 0.0;
        else {
            t0 *= t0;
            n0 = t0 * t0 * dot(grad3[gi0], x0, y0);
        }

        double t1 = 0.5 - x1 * x1 - y1 * y1;
        if(t1 < 0) 
            n1 = 0.0;
        else {
            t1 *= t1;
            n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
        }

        double t2 = 0.5 - x2 * x2 - y2 * y2;
        if(t2 < 0)
            n2 = 0.0;
        else {
            t2 *= t2;
            n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
        }

        return 70.0 * (n0 + n1 + n2);
    }

    public static void genGrad(long seed) {
        Random rnd = new Random(seed);
        for(int i = 0; i < 255; i++)
          p[i] = i;
        for(int i = 0; i < 255; i++) {
          int j = rnd.nextInt(255);
          int nSwap = p[i];
          p[i]  = p[j];
          p[j]  = nSwap;
        }

        for(int i = 0; i < 512; i++) 
            perm[i] = p[i & 255];
    }

}

Here is the new code I'm using and it prints out all the blocks at the same location:

Block[][] chunk = new Block[Chunk.CHUNK_WIDTH_BLOCKS][Chunk.CHUNK_HEIGHT_BLOCKS];
    float[][] positions = new float[Chunk.CHUNK_WIDTH_BLOCKS][Chunk.CHUNK_HEIGHT_BLOCKS];
    float frequency = 1.0f / (float) chunk.length; 

    for (int x = 0; x < chunk.length - 1; x++) 
    { 
        for (int y = 0; y < chunk[x].length - 1; y++) 
        { 
            positions[x][y] = SimplexNoise.Generate((float) x * frequency, (float) y * frequency);
            g.drawRect(positions[x][0], positions[0][y], Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT);
        } 
    } 

    for (int x = 0; x < Chunk.CHUNK_WIDTH_BLOCKS; x++)
    {
        for (int y = 0; y < Chunk.CHUNK_HEIGHT_BLOCKS; y++)
        {
            if (positions[x][y] < 0f)
                chunk[x][y] = new Block();
            if (positions[x][y] >= -0f)
                chunk[x][y] = new Block();
        }
    }
tshepang
  • 12,111
  • 21
  • 91
  • 136

1 Answers1

0

I just found this LINK to explain how to use Perlin noise in 2d terrain generation like terraria.

Here is the code for the noise class:

public class Noise
    {
        /// <summary>
        /// 1D simplex noise
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static float Generate(float x)
        {
            int i0 = FastFloor(x);
            int i1 = i0 + 1;
            float x0 = x - i0;
            float x1 = x0 - 1.0f;
            float n0, n1;
            float t0 = 1.0f - x0 * x0;
            t0 *= t0;
            n0 = t0 * t0 * grad(perm[i0 & 0xff], x0);
            float t1 = 1.0f - x1 * x1;
            t1 *= t1;
            n1 = t1 * t1 * grad(perm[i1 & 0xff], x1);
            // The maximum value of this noise is 8*(3/4)^4 = 2.53125
            // A factor of 0.395 scales to fit exactly within [-1,1]
            return 0.395f * (n0 + n1);
        }
        /// <summary>
        /// 2D simplex noise
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static float Generate(float x, float y)
        {
            const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0)
            const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0
            float n0, n1, n2; // Noise contributions from the three corners
            // Skew the input space to determine which simplex cell we're in
            float s = (x + y) * F2; // Hairy factor for 2D
            float xs = x + s;
            float ys = y + s;
            int i = FastFloor(xs);
            int j = FastFloor(ys);
            float t = (float)(i + j) * G2;
            float X0 = i - t; // Unskew the cell origin back to (x,y) space
            float Y0 = j - t;
            float x0 = x - X0; // The x,y distances from the cell origin
            float y0 = y - Y0;
            // For the 2D case, the simplex shape is an equilateral triangle.
            // Determine which simplex we are in.
            int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
            if (x0 > y0) { i1 = 1; j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1)
            else { i1 = 0; j1 = 1; }      // upper triangle, YX order: (0,0)->(0,1)->(1,1)
            // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
            // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
            // c = (3-sqrt(3))/6
            float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
            float y1 = y0 - j1 + G2;
            float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords
            float y2 = y0 - 1.0f + 2.0f * G2;
            // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
            int ii = i % 256;
            int jj = j % 256;
            // Calculate the contribution from the three corners
            float t0 = 0.5f - x0 * x0 - y0 * y0;
            if (t0 < 0.0f) n0 = 0.0f;
            else
            {
                t0 *= t0;
                n0 = t0 * t0 * grad(perm[ii + perm[jj]], x0, y0);
            }
            float t1 = 0.5f - x1 * x1 - y1 * y1;
            if (t1 < 0.0f) n1 = 0.0f;
            else
            {
                t1 *= t1;
                n1 = t1 * t1 * grad(perm[ii + i1 + perm[jj + j1]], x1, y1);
            }
            float t2 = 0.5f - x2 * x2 - y2 * y2;
            if (t2 < 0.0f) n2 = 0.0f;
            else
            {
                t2 *= t2;
                n2 = t2 * t2 * grad(perm[ii + 1 + perm[jj + 1]], x2, y2);
            }
            // Add contributions from each corner to get the final noise value.
            // The result is scaled to return values in the interval [-1,1].
            return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary!
        }

        public static float Generate(float x, float y, float z)
        {
            // Simple skewing factors for the 3D case
            const float F3 = 0.333333333f;
            const float G3 = 0.166666667f;
            float n0, n1, n2, n3; // Noise contributions from the four corners
            // Skew the input space to determine which simplex cell we're in
            float s = (x + y + z) * F3; // Very nice and simple skew factor for 3D
            float xs = x + s;
            float ys = y + s;
            float zs = z + s;
            int i = FastFloor(xs);
            int j = FastFloor(ys);[attachment=11149:perlinBug.png]
            int k = FastFloor(zs);
            float t = (float)(i + j + k) * G3;
            float X0 = i - t; // Unskew the cell origin back to (x,y,z) space
            float Y0 = j - t;
            float Z0 = k - t;
            float x0 = x - X0; // The x,y,z distances from the cell origin
            float y0 = y - Y0;
            float z0 = z - Z0;
            // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
            // Determine which simplex we are in.
            int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
            int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
            /* This code would benefit from a backport from the GLSL version! */
            if (x0 >= y0)
            {
                if (y0 >= z0)
                { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // X Y Z order
                else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } // X Z Y order
                else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } // Z X Y order
            }
            else
            { // x0<y0
                if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; } // Z Y X order
                else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; } // Y Z X order
                else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } // Y X Z order
            }
            // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
            // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
            // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
            // c = 1/6.
            float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
            float y1 = y0 - j1 + G3;
            float z1 = z0 - k1 + G3;
            float x2 = x0 - i2 + 2.0f * G3; // Offsets for third corner in (x,y,z) coords
            float y2 = y0 - j2 + 2.0f * G3;
            float z2 = z0 - k2 + 2.0f * G3;
            float x3 = x0 - 1.0f + 3.0f * G3; // Offsets for last corner in (x,y,z) coords
            float y3 = y0 - 1.0f + 3.0f * G3;
            float z3 = z0 - 1.0f + 3.0f * G3;
            // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds
            int ii = i % 256;
            int jj = j % 256;
            int kk = k % 256;
            // Calculate the contribution from the four corners
            float t0 = 0.6f - x0 * x0 - y0 * y0 - z0 * z0;
            if (t0 < 0.0f) n0 = 0.0f;
            else
            {
                t0 *= t0;
                n0 = t0 * t0 * grad(perm[ii + perm[jj + perm[kk]]], x0, y0, z0);
            }
            float t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1;
            if (t1 < 0.0f) n1 = 0.0f;
            else
            {
                t1 *= t1;
                n1 = t1 * t1 * grad(perm[ii + i1 + perm[jj + j1 + perm[kk + k1]]], x1, y1, z1);
            }
            float t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2;
            if (t2 < 0.0f) n2 = 0.0f;
            else
            {
                t2 *= t2;
                n2 = t2 * t2 * grad(perm[ii + i2 + perm[jj + j2 + perm[kk + k2]]], x2, y2, z2);
            }
            float t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3;
            if (t3 < 0.0f) n3 = 0.0f;
            else
            {
                t3 *= t3;
                n3 = t3 * t3 * grad(perm[ii + 1 + perm[jj + 1 + perm[kk + 1]]], x3, y3, z3);
            }
            // Add contributions from each corner to get the final noise value.
            // The result is scaled to stay just inside [-1,1]
            return 32.0f * (n0 + n1 + n2 + n3); // TODO: The scale factor is preliminary!
        }
        private static byte[] perm = new byte[512] { 151,160,137,91,90,15,
              131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
              190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
              88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
              77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
              102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
              135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
              5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
              223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
              129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
              251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
              49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
              138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180,
              151,160,137,91,90,15,
              131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
              190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
              88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
              77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
              102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
              135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
              5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
              223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
              129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
              251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
              49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
              138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
            };
        private static int FastFloor(float x)
        {
            return (x > 0) ? ((int)x) : (((int)x) - 1);
        }
        private static float grad(int hash, float x)
        {
            int h = hash & 15;
            float grad = 1.0f + (h & 7);   // Gradient value 1.0, 2.0, ..., 8.0
            if ((h & 8) != 0) grad = -grad;      // Set a random sign for the gradient
            return (grad * x);         // Multiply the gradient with the distance
        }
        private static float grad(int hash, float x, float y)
        {
            int h = hash & 7;     // Convert low 3 bits of hash code
            float u = h < 4 ? x : y;  // into 8 simple gradient directions,
            float v = h < 4 ? y : x;  // and compute the dot product with (x,y).
            return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -2.0f * v : 2.0f * v);
        }
        private static float grad(int hash, float x, float y, float z)
        {
            int h = hash & 15;   // Convert low 4 bits of hash code into 12 simple
            float u = h < 8 ? x : y; // gradient directions, and compute dot product.
            float v = h < 4 ? y : h == 12 || h == 14 ? x : z; // Fix repeats at h = 12 to 15
            return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -v : v);
        }
        private static float grad(int hash, float x, float y, float z, float t)
        {
            int h = hash & 31;    // Convert low 5 bits of hash code into 32 simple
            float u = h < 24 ? x : y; // gradient directions, and compute dot product.
            float v = h < 16 ? y : z;
            float w = h < 8 ? z : t;
            return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -v : v) + ((h & 4) != 0 ? -w : w);
 }
}

The way that he uses it is like so:

private void CreatePerlinWorld()
        {
            world = new Tile[_maxWidth, _maxHeight];
            diamond = new float[_maxWidth, _maxHeight];
            for (int x = 0; x < world.GetLength(0) - 1; x++)
            {
                for (int y = 0; y < world.GetLength(1) - 1; y++)
                {
                    diamond[x,y] = Noise.Generate(x, y);
                }
            }
        }
        private void GeneratePerlinWorld()
        {
            for (int x = 0; x < _maxWidth; x++)
            {
                for (int y = 0; y < _maxHeight; y++)
                {
                    if (diamond[x, y] < 0f)
                        world[x, y] = new Tile(TileType.None, TileCollision.Passable, ToolType.None);
                    if (diamond[x, y] >= -0f)
                        world[x, y] = new Tile(TileType.Dirt, TileCollision.Impassable, ToolType.Pickaxe);
                }
            }
        }
Domecraft
  • 1,645
  • 15
  • 26
  • He said that he had problems and it randomly spawned blocks. – Person Person Oct 01 '13 at 02:33
  • My point is that it is a flawed code and just randomly spawns blocks everywhere, but thanks let me try to plug it in and see what happens – Person Person Oct 01 '13 at 02:35
  • Should that make it more stable? – Person Person Oct 01 '13 at 02:42
  • Alright I will try to plug this into a simple program and see what happens. I'll report back soon – Person Person Oct 01 '13 at 02:44
  • I'm so close to getting this but I can't seem to figure out how to render it. I made a multi-dimensional and I don't know how to render it. Do I print out the blocks[x][y] or do I use a for loops x and y? – Person Person Oct 01 '13 at 02:55
  • I think you would print out the blocks, but you could also try using a loop if that doesn't work because it needs to iterate through each block. If it works but the blocks look weird, try taking a screenshot of it and upload the image. – Domecraft Oct 01 '13 at 03:16
  • Do I use the float[][] diamond array to map the positions? If you could give me a bit of code that would be really helpful because I can't a single block to render – Person Person Oct 01 '13 at 03:21
  • for (int x = 0; x < world.GetLength(0) - 1; x++) { for (int y = 0; y < world.GetLength(1) - 1; y++) { diamond[x,y] = Noise.Generate(x, y); – Domecraft Oct 01 '13 at 03:26
  • The following code above will reference the noise class that was created, thus printing a block on the screen randomly based on a perlin noise. If you are having trouble compiling the code, try looking through the errors and seeing if maybe you happened to misspell anything or didn't properly reference to the right class. – Domecraft Oct 01 '13 at 03:28
  • I didn't know where to render it because I thought you used the noise to generate and store the positions and then render elsewhere. Do I reference just the x, like diamond[x][0] does that work? – Person Person Oct 01 '13 at 03:34
  • Yeah it should work, second code will reference the first one, which creates different perlin noises. Then, the second code will use the perlin noise generated by the first class to generate positions and "print" out the positions of the blocks. – Domecraft Oct 01 '13 at 03:36
  • Could you edit your original question and add the code you have now? It would be better if I took a look at the code that you are using. Thanks. – Domecraft Oct 01 '13 at 03:42
  • You have multiple problems with your code. For instance: for (int x = 0; x < chunk.length - 1; x++) That should be: for (int x = 0; x < chunk.length; x++) Also, consider the following: g.drawRect(positions[x][0], positions[0][y], Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT); That will not use all values in "positions[x][y]".... I think that what you will want is for the array to be 3d... for instance: – Domecraft Oct 01 '13 at 04:41
  • float[][][] positions = new float[Chunk.CHUNK_WIDTH_BLOCKS][Chunk.CHUNK_HEIGHT_BLOCKS][2]; That way: posistions[x][y][0] is the value for x, and positions[x][y][1] is the value for y.... g.drawRect(positions[x][y][0], positions[x][y][1], Block.BLOCK_WIDTH, Block.BLOCK_HEIGHT); – Domecraft Oct 01 '13 at 04:42
  • Well then I can't use generate noise because I have a third dimension – Person Person Oct 01 '13 at 13:19
  • Plus in that thread that guy doesn't have to use a 3D array and he said that it was working fine when he fixed the for loop. It is a problem with the rendering. – Person Person Oct 01 '13 at 13:22
  • No because I still don't know how to render it. There is nothing to fix I meant the first for loop that guy put and didn't use frequency. – Person Person Oct 01 '13 at 16:13