// Over a few chapters, we are working up to a "Battleship" style Websocket game.
// Ch.3
// A 2D grid of boxes. (Our own 2D space inside the bigger Three.js 3D "pixel" space.)
// Introduces a "for" loop.
// this introduces a "for" loop, inside of which is another "for" loop - slightly advanced
// so we suggest you take a break first and go to w3schools and experiment with a basic "for" loop
// https://www.w3schools.com/js/js_loop_for.asp
// === start of code ===========================================================================================================
// --- const (constants) --------------------------------------
// we are going to define sizes of things all in one place, so they can be easily changed
// rather than having them scattered through code
const gridsize = 15; // number of squares along side of world
const squaresize = 100; // size of square in pixels
const MAXPOS = gridsize * squaresize; // length of one side in pixels
const startRadiusConst = MAXPOS * 1 ; // distance from centre to start the camera at
const maxRadiusConst = MAXPOS * 5 ; // maximum distance from camera we will render things
const SKYCOLOR = 'lightyellow'; // define sky colour in this section
const TEXTURE_WALL = '/uploads/chapters/stone.png' ;
const TEXTURE_SHIP = '/uploads/chapters/ship.png' ;
// credit:
// https://commons.wikimedia.org/wiki/File:Square_stone_brick_Texture.jpg
// https://commons.wikimedia.org/wiki/File:Regina_Maris.JPG
// --- var (variables) --------------------------------------
var wall_texture, ship_texture;
ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
loadTextures();
AB.msg ( "Drag and scroll the mouse to move and zoom the camera.");
function loadTextures()
{
var loader1 = new THREE.TextureLoader();
var loader2 = new THREE.TextureLoader();
loader1.load ( TEXTURE_SHIP, function ( texture )
{
ship_texture = texture;
if ( wall_texture ) makeEverything(); // if both textures have loaded we can make everything
});
loader2.load ( TEXTURE_WALL, function ( texture )
{
wall_texture = texture;
if ( ship_texture ) makeEverything();
});
}
//--- my 2D grid system -------------------------------------------------------------------------------
// Three.js uses (x,y,z) coordinates
// first I want a 2D grid so height parameter will always be zero
// in fact this is the y parameter
// second, if, say, I have 15 blocks on each side, I will do numbering 0 to 14
// so I will give my blocks locations (0,0), (0,1) ... (0,14), (1,0), (1,1) ... (14,14)
// we need to map my addressing system to the Three.js addressing system
function translate ( i, j ) // convert point (i,j) in my 2D system to a point in Three.js space
{
var v = new THREE.Vector3();
v.y = 0;
v.x = i * squaresize ;
v.z = j * squaresize ;
return v;
}
function makeEverything()
{
makeWalls();
makeShips();
}
function makeWalls()
{
var i,j, shape, thecube, position;
shape = new THREE.BoxGeometry ( squaresize, squaresize, squaresize );
// to make the walls we could say:
// make wall at (0,0)
// make wall at (0,1)
// ...
// make wall at (0,14)
// ... etc.
// but this is a lot of repetitive code
// a "for" loop can help to make the code much shorter
// a "for" loop typically: goes through all items, executing some code for each one
// https://www.w3schools.com/js/js_loop_for.asp
// also seen here is an "if" statement using the "or" operator ||
// https://www.w3schools.com/js/js_comparisons.asp
for ( i = 0; i < gridsize ; i++ ) // for i taking values (0,1, .. gridsize-1), for example (0,1, .. 14)
for ( j = 0; j < gridsize ; j++ )
{ // --- start of block of code to run for every i,j combination
if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) ) // if i or j indicate we are at one of the edges
{
thecube = new THREE.Mesh( shape );
thecube.material = new THREE.MeshBasicMaterial( { map: wall_texture } );
position = translate(i,j); // translate this position to Three.js space
thecube.position.copy ( position );
ABWorld.scene.add(thecube);
}
} // --- end of block of code to run for every i,j combination
}
function makeShips() // make one or more ships
{
var shape, thecube, position;
shape = new THREE.BoxGeometry ( squaresize, squaresize, squaresize );
thecube = new THREE.Mesh( shape );
thecube.material = new THREE.MeshBasicMaterial( { map: ship_texture } );
position = translate ( 4, 8 ); // put ship somewhere in the grid - can change this
thecube.position.copy ( position );
ABWorld.scene.add(thecube);
}
// === end of code ===========================================================================================================
// Exercises:
// Change the size of the grid.
// Change the "for" and "if" statements to make other configurations of wall blocks.
// Make ship bigger than one box. Put ship image on 3 to 5 boxes in a row
// Advanced exercise: Change the ship to a position that changes as the grid size changes. Like 1/3 of the way down, 2/3 of the way across.
// Advanced exercise: Make the wall layout actually 3D. Make a cube of walls using a "for" loop.
// Outcomes: Student can:
// Learn how to use a "for" loop.
// Learn how to use "if" with an "or" operator.
// Learn how to use equality and less than / greater than checks.
// Learn how to make one's own addressing on a 2D grid.
// Learn how to map a 2D grid to a 3D space like Three.js.