Part 1 of this tutorial left off with just one column of tiles, and the ability to move along the Z axis infinitely. Here, we're going to build the full grid, and enable the same infinite movement across the X axis.
Step 1: Build up a grid
Just by changing the X position of the tiles, they will apear to the side of the camera:
Now that we already have one row moving forwards, we can create two more rows either side of it to make a total of 9 tiles in 3 rows x 3 columns. The changes now are that in the constructor at the top of the terrainMatrix.js file, I have provided definitions for the tile height, width and row number to make the rest of the code easier to understand:
function TerrainMatrix(){ this.floor = []; this.tileHeight=100; this.tileWidth=100; this.tileRowNumber = 3; }
Then, inside createTerrainMatrix(
), all we need to do to add two extra rows is wrap the main contents with a for loop that will force the funciton to execute three times. However in addition to this, we provide an alternative X position for each of the three rows.
The alternative X position is created towards the top of the function where an xPos is defined. Just in side the loop, we set this X position for each of the three row iterations:
var xPos=0; //we want a 3 by 3 matrix for(var x = 1; x<4; x+=1){ console.log(x) if(x==1){ xPos= -this.tileWidth; } else if(x==2){ xPos= this.tileWidth; } else if (x==3){ xPos = 0 }
Setting the X Position
- The first row (iteration of the loop) sets X to minus the tile width so it appears on the left
- The second row sets X to 0 so it appears in the center where the camera is
- The third row sets X to plus the tile width, so it appears to the right of the camera
Bringing it all together, the rest of the function is exactly the same, except the X position is altered with. ground.position.x = xPos
.
/** * Terrain functions */ TerrainMatrix.prototype={ constructor: TerrainMatrix, /** * createTerrainMatrix * @TODO: create the matrix of terrains - need to add 9 bits of terrain */ createTerrainMatrix:function(scene, perlinNoise){ var xPos=0; //we want a 3 by 3 matrix for(var row = 0; row<3; row+=1){ if(row==0){ xPos= -this.tileWidth; } else if(row==1){ xPos= this.tileWidth; } else if (x==2){ xPos = 0 } //every 100px on the z axis, add a bit of ground for ( var z= this.tileHeight; z > (this.tileHeight * -this.tileRowNumber); z-=this.tileHeight ) { //Create the perlin noise for the surface of the ground var perlinSurface = new PerlinSurface(perlinNoise, this.tileWidth, this.tileHeight); var ground = perlinSurface.surface; //rotate 90 degrees around the xaxis so we can see the terrain ground.rotation.x = -Math.PI/-2; // Then set the z position to where it is in the loop (distance of camera) ground.position.z = z; ground.position.y -=4; ground.position.x =xPos; //add the ground to the scene scene.add(ground); //finally push it to the floor array this.floor.push(ground); } } },
Step 2: Be less memory intensive
One major alteration is required here for this to work. At the moment, we're creating terrain using PlaneGeometry
in the PerlinSurface
function:
//create the plane geometry this.geometry = new THREE.PlaneGeometry(width,height,300,300);
This is too memory intensive to create 9 pieces of terrain. Therefore we need to modify this function to use BufferGeometry, which luckily for us can be done in one line:
var bufferGeometry = new THREE.BufferGeometry().fromGeometry( this.geometry)
Here, we use the fromGeometry()
method to convert PlaneGeometry
to BufferGemetry
.
At this point we've got a 3 x 3 grid, and are able to move forward and backwards, but not left and right (without reaching the edge).
Step 3: Moving along the X axis
Very similar code monitoring the Z axis can be used to detect when the camera moves left or right (along the X axis), allowing us to add terrain appropriately along the X axis.
Here is the code to be added to the moveWithCamera()
function under the last if clause:
//x positions else if((this.floor[i].position.x - this.tileWidth)>camera.position.x){ this.floor[i].position.x-=(this.tileWidth*2); } //if the camera has moved past the entire square in the opposite direction, move the square the opposite way else if((this.floor[i].position.x + this.tileWidth)<camera.position.x){ this.floor[i].position.x+=(this.tileWidth*2); }
It's exactly the same as the Z position checks, but monitors the X positions, and uses the tileWidth
rather than tileHeight
.
Now the user is trapped, as whatever direction they move, more terrain is 'created'.
Well done - but how can this be improved?
You should now have an infinite terrain, but I'm sure this could be improved. After creating this tutorial, I have experimented with the size and number of tiles used to create this effect, and it can be done using just a 2 x 2 grid. Creating and updating just 4 tiles is much less intensive on memory than 9.