Terrain Help

Hello! I have made games for a while and am running into a snare on one. Like many others, I am trying to make a Minecraft clone. Finding the right terrain generation took me a while until I found Kurio’s post about Mest up normals I forked his protect. His code is perfect for what I am trying to accomplish. The problem is it is random, but not infinite. I would like the terrain to be infinite and random but at the same time be able to memorize previous chunks that it rendered. This is a high project and it would be awesome if one of you smarter guys could help me out. I don’t know much about javascript but this is the only game engine available to me.

Thank you! :smile:

https://playcanvas.com/editor/scene/2084742

I am not very familiar with the code used, but is it possible that you could generate “chunks”? Little generate pieces, then you can have a script that infinitely generates these “Chunks” I use the word chunk loosely, not like a Minecraft chunk of 16x16 but maybe a biome.

1 Like

Yep. I thought the current script would repeat when the player crosses a boundary. Then, when the player recrosses that boundary, it reverts to the previous chunk layout. This is easier said than done. In terms of biomes, I was thinking of a temperature/heightmap system. What I mean is if a block is above a certain point the temperature changes. when the temperature changes then different blocks will be generated. I am not to worry about that as of right now. I am just interested in the terrane generation portion. I understand this is complicated, but I appreciated the response. :smile:

1 Like

No problem! Do you need any help with the code? I can try to help.

1 Like

Yeah, I am unfamiliar with the coding language and I would greatly appreciate your help. The Perlin noise algorithm is tricky to find. If you like I can post it here for you. I would suggest making a separate code so we can do some multithreading instead of one big code.(performing purposes)

var SpawnService = pc.createScript('spawnService');

(function(global){
  var module = global.noise = {};

  function Grad(x, y, z) {
    this.x = x; this.y = y; this.z = z;
  }
  
  Grad.prototype.dot2 = function(x, y) {
    return this.x*x + this.y*y;
  };

  Grad.prototype.dot3 = function(x, y, z) {
    return this.x*x + this.y*y + this.z*z;
  };

  var grad3 = [new Grad(1,1,0),new Grad(-1,1,0),new Grad(1,-1,0),new Grad(-1,-1,0),
               new Grad(1,0,1),new Grad(-1,0,1),new Grad(1,0,-1),new Grad(-1,0,-1),
               new Grad(0,1,1),new Grad(0,-1,1),new Grad(0,1,-1),new Grad(0,-1,-1)];

  var 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
  var perm = new Array(512);
  var gradP = new Array(512);

  // This isn't a very good seeding function, but it works ok. It supports 2^16
  // different seed values. Write something better if you need more seeds.
  module.seed = function(seed) {
    if(seed > 0 && seed < 1) {
      // Scale the seed out
      seed *= 6553645829745902;
    }

    seed = Math.floor(seed);
    if(seed < 256) {
      seed |= seed << 8;
    }

    for(var i = 0; i < 256; i++) {
      var v;
      if (i & 1) {
        v = p[i] ^ (seed & 255);
      } else {
        v = p[i] ^ ((seed>>8) & 255);
      }

      perm[i] = perm[i + 256] = v;
      gradP[i] = gradP[i + 256] = grad3[v % 12];
    }
  };

  module.seed(0);

  /*
  for(var i=0; i<256; i++) {
    perm[i] = perm[i + 256] = p[i];
    gradP[i] = gradP[i + 256] = grad3[perm[i] % 12];
  }*/

  // Skewing and unskewing factors for 2, 3, and 4 dimensions
  var F2 = 0.5*(Math.sqrt(3)-1);
  var G2 = (3-Math.sqrt(3))/6;

  var F3 = 1/3;
  var G3 = 1/6;

  // 2D simplex noise
  module.simplex2 = function(xin, yin) {
    var n0, n1, n2; // Noise contributions from the three corners
    // Skew the input space to determine which simplex cell we're in
    var s = (xin+yin)*F2; // Hairy factor for 2D
    var i = Math.floor(xin+s);
    var j = Math.floor(yin+s);
    var t = (i+j)*G2;
    var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed.
    var y0 = yin-j+t;
    // For the 2D case, the simplex shape is an equilateral triangle.
    // Determine which simplex we are in.
    var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
    if(x0>y0) { // lower triangle, XY order: (0,0)->(1,0)->(1,1)
      i1=1; j1=0;
    } else {    // upper triangle, YX order: (0,0)->(0,1)->(1,1)
      i1=0; j1=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
    var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
    var y1 = y0 - j1 + G2;
    var x2 = x0 - 1 + 2 * G2; // Offsets for last corner in (x,y) unskewed coords
    var y2 = y0 - 1 + 2 * G2;
    // Work out the hashed gradient indices of the three simplex corners
    i &= 255;
    j &= 255;
    var gi0 = gradP[i+perm[j]];
    var gi1 = gradP[i+i1+perm[j+j1]];
    var gi2 = gradP[i+1+perm[j+1]];
    // Calculate the contribution from the three corners
    var t0 = 0.5 - x0*x0-y0*y0;
    if(t0<0) {
      n0 = 0;
    } else {
      t0 *= t0;
      n0 = t0 * t0 * gi0.dot2(x0, y0);  // (x,y) of grad3 used for 2D gradient
    }
    var t1 = 0.5 - x1*x1-y1*y1;
    if(t1<0) {
      n1 = 0;
    } else {
      t1 *= t1;
      n1 = t1 * t1 * gi1.dot2(x1, y1);
    }
    var t2 = 0.5 - x2*x2-y2*y2;
    if(t2<0) {
      n2 = 0;
    } else {
      t2 *= t2;
      n2 = t2 * t2 * gi2.dot2(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 70 * (n0 + n1 + n2);
  };

  // 3D simplex noise
  module.simplex3 = function(xin, yin, zin) {
    var n0, n1, n2, n3; // Noise contributions from the four corners

    // Skew the input space to determine which simplex cell we're in
    var s = (xin+yin+zin)*F3; // Hairy factor for 2D
    var i = Math.floor(xin+s);
    var j = Math.floor(yin+s);
    var k = Math.floor(zin+s);

    var t = (i+j+k)*G3;
    var x0 = xin-i+t; // The x,y distances from the cell origin, unskewed.
    var y0 = yin-j+t;
    var z0 = zin-k+t;

    // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
    // Determine which simplex we are in.
    var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
    var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
    if(x0 >= y0) {
      if(y0 >= z0)      { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; }
      else if(x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; }
      else              { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; }
    } else {
      if(y0 < z0)      { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; }
      else if(x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; }
      else             { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; }
    }
    // 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.
    var x1 = x0 - i1 + G3; // Offsets for second corner
    var y1 = y0 - j1 + G3;
    var z1 = z0 - k1 + G3;

    var x2 = x0 - i2 + 2 * G3; // Offsets for third corner
    var y2 = y0 - j2 + 2 * G3;
    var z2 = z0 - k2 + 2 * G3;

    var x3 = x0 - 1 + 3 * G3; // Offsets for fourth corner
    var y3 = y0 - 1 + 3 * G3;
    var z3 = z0 - 1 + 3 * G3;

    // Work out the hashed gradient indices of the four simplex corners
    i &= 255;
    j &= 255;
    k &= 255;
    var gi0 = gradP[i+   perm[j+   perm[k   ]]];
    var gi1 = gradP[i+i1+perm[j+j1+perm[k+k1]]];
    var gi2 = gradP[i+i2+perm[j+j2+perm[k+k2]]];
    var gi3 = gradP[i+ 1+perm[j+ 1+perm[k+ 1]]];

    // Calculate the contribution from the four corners
    var t0 = 0.6 - x0*x0 - y0*y0 - z0*z0;
    if(t0<0) {
      n0 = 0;
    } else {
      t0 *= t0;
      n0 = t0 * t0 * gi0.dot3(x0, y0, z0);  // (x,y) of grad3 used for 2D gradient
    }
    var t1 = 0.6 - x1*x1 - y1*y1 - z1*z1;
    if(t1<0) {
      n1 = 0;
    } else {
      t1 *= t1;
      n1 = t1 * t1 * gi1.dot3(x1, y1, z1);
    }
    var t2 = 0.6 - x2*x2 - y2*y2 - z2*z2;
    if(t2<0) {
      n2 = 0;
    } else {
      t2 *= t2;
      n2 = t2 * t2 * gi2.dot3(x2, y2, z2);
    }
    var t3 = 0.6 - x3*x3 - y3*y3 - z3*z3;
    if(t3<0) {
      n3 = 0;
    } else {
      t3 *= t3;
      n3 = t3 * t3 * gi3.dot3(x3, y3, z3);
    }
    // Add contributions from each corner to get the final noise value.
    // The result is scaled to return values in the interval [-1,1].
    return 32 * (n0 + n1 + n2 + n3);

  };

  // ##### Perlin noise stuff

  function fade(t) {
    return t*t*t*(t*(t*6-15)+10);
  }

  function lerp(a, b, t) {
    return (1-t)*a + t*b;
  }

  // 2D Perlin Noise
  module.perlin2 = function(x, y) {
    // Find unit grid cell containing point
    var X = Math.floor(x), Y = Math.floor(y);
    // Get relative xy coordinates of point within that cell
    x = x - X; y = y - Y;
    // Wrap the integer cells at 255 (smaller integer period can be introduced here)
    X = X & 255; Y = Y & 255;

    // Calculate noise contributions from each of the four corners
    var n00 = gradP[X+perm[Y]].dot2(x, y);
    var n01 = gradP[X+perm[Y+1]].dot2(x, y-1);
    var n10 = gradP[X+1+perm[Y]].dot2(x-1, y);
    var n11 = gradP[X+1+perm[Y+1]].dot2(x-1, y-1);

    // Compute the fade curve value for x
    var u = fade(x);

    // Interpolate the four results
    return lerp(
        lerp(n00, n10, u),
        lerp(n01, n11, u),
       fade(y));
  };

  // 3D Perlin Noise
  module.perlin3 = function(x, y, z) {
    // Find unit grid cell containing point
    var X = Math.floor(x), Y = Math.floor(y), Z = Math.floor(z);
    // Get relative xyz coordinates of point within that cell
    x = x - X; y = y - Y; z = z - Z;
    // Wrap the integer cells at 255 (smaller integer period can be introduced here)
    X = X & 255; Y = Y & 255; Z = Z & 255;

    // Calculate noise contributions from each of the eight corners
    var n000 = gradP[X+  perm[Y+  perm[Z  ]]].dot3(x,   y,     z);
    var n001 = gradP[X+  perm[Y+  perm[Z+1]]].dot3(x,   y,   z-1);
    var n010 = gradP[X+  perm[Y+1+perm[Z  ]]].dot3(x,   y-1,   z);
    var n011 = gradP[X+  perm[Y+1+perm[Z+1]]].dot3(x,   y-1, z-1);
    var n100 = gradP[X+1+perm[Y+  perm[Z  ]]].dot3(x-1,   y,   z);
    var n101 = gradP[X+1+perm[Y+  perm[Z+1]]].dot3(x-1,   y, z-1);
    var n110 = gradP[X+1+perm[Y+1+perm[Z  ]]].dot3(x-1, y-1,   z);
    var n111 = gradP[X+1+perm[Y+1+perm[Z+1]]].dot3(x-1, y-1, z-1);

    // Compute the fade curve value for x, y, z
    var u = fade(x);
    var v = fade(y);
    var w = fade(z);

    // Interpolate
    return lerp(
        lerp(
          lerp(n000, n100, u),
          lerp(n001, n101, u), w),
        lerp(
          lerp(n010, n110, u),
          lerp(n011, n111, u), w),
       v);
  };

})(this);



Chunk.prototype.initialize = function() {
    var node = new pc.GraphNode();
    node.setPosition(this.entity.getPosition());

    var mesh = this.createMeshInstance();
    var instance = new pc.MeshInstance(mesh, this.app.assets.find('Textures', 'material').resource, node);
    instance.node = node;

    var model = new pc.Model();
    model.meshInstances.push(instance);

    var land = new pc.Entity();
    land.addComponent('collision', {type: 'mesh', model: model});
    land.addComponent('script');
    land.script.create('coll');
    this.entity.addChild(land);

    this.entity.addComponent('render', {meshInstances: [instance]});
};

Chunk.prototype.createMeshInstance = function(){
    var verts = [];
    var norms = [];
    var uvs = [];
    var inds = [];

    var s = 1/12;
    var uv = [
        {x: s, y: s},
        {x: s*2, y: s},
        {x: s*3, y: s},
        {x: s*4, y: s},
        {x: s*5, y: s},
        {x: s*6, y: s},
        {x: s*7, y: s},
        {x: s*8, y: s},
        {x: s*9, y: s},
        {x: s*10, y: s},
        {x: s*11, y: s},
        {x: s*12, y: s}
    ];

    function clamp(num,min,max){
        return Math.min(Math.max(num, min), max);
    }

    function ramp(x){
        return Math.sin(2*x)/2+0.5;
    }

    function caves(x,y,z){
        return clamp(Math.round(0.2 + noise.perlin3(x/20+0.3, y/8+0.3, z/20+0.3)), 0, 1);
    }

    function base(x,y,z){
        var num = Math.abs(Math.round(32 * ramp(noise.perlin2(x/80+0.3,z/80+0.3)) + noise.perlin2(x/5+0.1,z/5+0.1) + 28));
        if(y == 0){
            return 12;
        }
        if(y <= num){
            if(y > num - 3 && y <= 32){
                return 5;
            }
            if(y > num - 1){
                return 1;
            } else if(y > num - 8){
                return 2;
            }
            return 3;
        }
        return 0;
    }

    function gen(x,y,z){
        var b = base(x,y,z);
        var c = caves(x,y,z);
        if(c > 0 && y != 0){
            if(b != 10){
                return 0;
            }
        }
        return b;
    }

    function tface(x,y,z){
        verts.push(x,y,z, x+1,y,z, x+1,y,z+1, x+1,y,z+1, x,y,z+1, x,y,z);
        norms.push(0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0);
        uvs.push(b.x,b.y,b.x-s,b.y,b.x-s,b.y-s,b.x-s,b.y-s,b.x,b.y-s,b.x,b.y);
        inds.push(inds.length+1,inds.length,inds.length+2,inds.length+4,inds.length+3,inds.length+5);
    }
    function fface(x,y,z){
        verts.push(x,y,z, x+1,y,z, x+1,y-1,z, x+1,y-1,z, x,y-1,z, x,y,z);
        norms.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1);
        uvs.push(b.x,b.y,b.x-s,b.y,b.x-s,b.y-s,b.x-s,b.y-s,b.x,b.y-s,b.x,b.y);
        inds.push(inds.length,inds.length+1,inds.length+2,inds.length+3,inds.length+4,inds.length+5);
    }
    function rface(x,y,z){
        verts.push(x+1,y,z, x+1,y,z+1, x+1,y-1,z+1, x+1,y-1,z+1, x+1,y-1,z, x+1,y,z);
        norms.push(1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0);
        uvs.push(b.x,b.y,b.x-s,b.y,b.x-s,b.y-s,b.x-s,b.y-s,b.x,b.y-s,b.x,b.y);
        inds.push(inds.length,inds.length+1,inds.length+2,inds.length+3,inds.length+4,inds.length+5);
    }
    function lface(x,y,z){
        verts.push(x,y,z+1, x,y,z, x,y-1,z, x,y-1,z, x,y-1,z+1, x,y,z+1);
        norms.push(-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0);
        uvs.push(b.x,b.y,b.x-s,b.y,b.x-s,b.y-s,b.x-s,b.y-s,b.x,b.y-s,b.x,b.y);
        inds.push(inds.length,inds.length+1,inds.length+2,inds.length+3,inds.length+4,inds.length+5);
    }
    function baface(x,y,z){
        verts.push(x+1,y,z+1, x,y,z+1, x,y-1,z+1, x,y-1,z+1, x+1,y-1,z+1, x+1,y,z+1);
        norms.push(0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1);
        uvs.push(b.x,b.y,b.x-s,b.y,b.x-s,b.y-s,b.x-s,b.y-s,b.x,b.y-s,b.x,b.y);
        inds.push(inds.length,inds.length+1,inds.length+2,inds.length+3,inds.length+4,inds.length+5);
    }
    function boface(x,y,z){
        verts.push(x,y-1,z, x+1,y-1,z, x+1,y-1,z+1, x+1,y-1,z+1, x,y-1,z+1, x,y-1,z);
        norms.push(0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0);
        uvs.push(b.x,b.y,b.x-s,b.y,b.x-s,b.y-s,b.x-s,b.y-s,b.x,b.y-s,b.x,b.y);
        inds.push(inds.length,inds.length+1,inds.length+2,inds.length+3,inds.length+4,inds.length+5);
    }


    for(let x = 0; x < 16; x++){
        for(let y = 0; y < 64; y++){
            for(let z = 0; z < 16; z++){
                var pos = this.entity.getPosition();
                var X = x + pos.x;
                var Y = y + pos.y;
                var Z = z + pos.z;

                var block = gen(X,Y,Z);
                var b = uv[block-1];

                if(block != 0 && block != 10){
                    var top = gen(X,Y+1,Z);
                    var front = gen(X,Y,Z-1);
                    var right = gen(X+1,Y,Z);
                    var left = gen(X-1,Y,Z);
                    var back = gen(X,Y,Z+1);
                    var bottom = gen(X,Y-1,Z);

                    //Top
                    if(top == 0 || y == 63 || top == 10){
                        tface(x,y,z);
                    }
                    //Front
                    if(front == 0 || front == 10){
                        fface(x,y,z);
                    }
                    //Right
                    if(right == 0 || right == 10){
                        rface(x,y,z);
                    }
                    //Left
                    if(left == 0 || left == 10){
                        lface(x,y,z);
                    }
                    //Back
                    if(back == 0 || back == 10){
                        baface(x,y,z);
                    }
                    //Bottom
                    if(bottom == 0 || y == 0 || bottom == 10){
                        boface(x,y,z);
                    }
                }
            }
        }
    }

    return new pc.createMesh(this.app.graphicsDevice, verts, {
        normals: norms,
        uvs: uvs,
        indices: inds
    });
};

SpawnService.extend({
    
    initialize: function() {
        this._reset();
        this._bindEvents();
        this._registerService();
    },
    
    startSpawn: function() {
        // spawn the initial tile
        var tile = this.app.poolService.getObject();
        this._spawnTile(tile);
        
        // spawn player
        this.app.root.findByName('Player').enabled = true;
    },
    
    spawnTiles: function(currentTile) {
        if (! currentTile) return;
        
        // Spawn at available positions
        var globalPosition = currentTile.getPosition();
        // var positions = currentTile.traits.positions;
        
        this._positions.forEach(function(pos) {
            
            this._targetGlobalPosition = globalPosition.clone().add(pos);
            
            var index = this._tiles.findIndex(function(tile) {
                return this._targetGlobalPosition.equals( tile.traits.globalPosition );
            }, this);
            if (index >= 0) return;

            var tile = this.app.poolService.getObject();
            tile.traits.globalPosition = this._targetGlobalPosition;
            tile.setPosition( this._targetGlobalPosition );
            
            this._spawnTile(tile);
            
            if (this._tiles.length > 6) {
                var oppositePosition = globalPosition.clone().sub(pos).sub(pos);
                if (this._targetGlobalPosition.x === globalPosition.x) {
                    // mark Z for reclaim
                    this._markAxisForReclaim('z', oppositePosition.z);
                } else {
                    // mark X for reclaim
                    this._markAxisForReclaim('x', oppositePosition.x);
                }
            }        
        }, this);
        
        while (this._rowToReclaim.length) {
            this._despawnTile( this._rowToReclaim.pop() );
        }
        
    },
    
    _markAxisForReclaim: function(axis, value) {
        var candidates = this._tiles.filter(function(tile) {
            return tile.traits.globalPosition[axis] === value;
        });
        candidates.forEach(function(tile) {
            this._despawnTile(tile);
        }, this);
    },
    
    _spawnTile: function(tile) {
        // spawn tile
        tile.enabled = true;
        this._tiles.push(tile);
    },

    _despawnTile: function(tile) {
        this.app.poolService.reclaimObject(tile);
        var index = this._tiles.findIndex(function(t) {
            return t.traits.globalPosition === tile.traits.globalPosition;
        });
        if (index >= 0)
            this._tiles.splice(index, 1);
    },
    
    _bindEvents: function() {
        this.app.on('game:start', this.startSpawn, this);
    },
    
    _reset: function() {
        this._tiles = []; // stores spawned tiles, new ones places at the beginning of the array
        this._targetGlobalPosition = new pc.Vec3();
        this._rowToReclaim = [];
        
        
        var p = new pc.Vec3();
        var pos = {
            center: p.clone(),
            north: p.clone().set(0, 0, -4),
            west: p.clone().set(-4, 0, 0),
            south: p.clone().set(0, 0, 4),
            east: p.clone().set(4, 0, 0)
        };
        this._positions = [
            pos.center,
            pos.north,
            pos.west,
            pos.south,
            pos.east,
            pos.north.clone().add(pos.west),
            pos.north.clone().add(pos.east),
            pos.south.clone().add(pos.west),
            pos.south.clone().add(pos.east)
        ];
    },
    
    _registerService: function() {
        this.app.spawnService = this;
        this.app.fire('service:update', { name: 'Spawn Service', init: true });
    }
    
});

I haven’t used this before, ngl. I will do some research and get back to you soon!

1 Like

Instead of having to write a perlin noise algortihm, just use noise.js noisejs - npm

1 Like

Wait a minute you just copied the noise js code. You can import it by going into the game settings and adding https://cdn.jsdelivr.net/npm/noisejs@2.1.0/index.min.js
into the external scripts.

1 Like

So @Nathaniel_Garland I looked through the code, what do you want to do next with it?

1 Like

I would like to make it so it would regenerate a new chunk when the player crosses a boundary. that is problem one. Problem two is the regeneration prosses. regenerating previous chunks.

Yes, it does work. if you want to see the example the link is above. the problem is that it is random, but not infinite. The goal is to make a Minecraft terrane generator for an MC clone.

Do you know how to use collision or triggers? We might be able to use that to trigger the new chunk generation.

I dont know for sure, but we could probably create a new entity for each generated peice and then we can disable/renable them.

1 Like

Triggers would be perfect. As for disabled and reliable, well, that would work, the problem is I don’t think the Perlin noise algorithm will let that happen. The reason is, every time it remakes itself or enables itself the terrane will be random. This will not allow previous chinks to load the same. We would have to pull previous data from the game to recreate previous chunks right? We can try it but I am unsure of the results. I love the feedback and your ideas!

No probelm, I like helping new people, I was new once, lol.

Exactly. If we make it so the colision makes a new entity, and the new entity has a script to generate new terrain, all we have to do is save that entity because it holds all that generation. We can then disable and re-enable that entity as needed.

1 Like

This sounds Great! I don’t know where to start on that though. Do you know of any examples? If not we could make a script together. I am recruiting some more smart people to this conversation. Maybe they can add something to this as well. The more the merrier. This could be the biggest Playcanvas project yet! You guys are a huge help!

I could also give you guys ownership if necessary as well. That might help.

I of course will make sure to add you guys in the game as well.

If you can make the script genrate entities in a certain area, I can give you the code for colision/triggers and creating entities.
One bonus would be if you could get the entities to generate as teh child of teh entity th script is on.

FYI I am going to fork your project and mess around with it.