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.