// Over a few chapters, we are working up to a "Battleship" style Websocket game.
// Ch.4
// Make two grids
// - my grid with randomised ships
// - map of opponent's grid (initially unknown)
// === start of code ===========================================================================================================
// --- const (constants) --------------------------------------
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 * 2 ; // distance from centre to start the camera at - actually we will override this below
const maxRadiusConst = MAXPOS * 10 ; // maximum distance from camera we will render things
const SKYCOLOR = 'lightyellow';
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
// --- Some cosmetic improvements --------------------------------------
// This section is not really important.
// Ancient Brain has a concept of a "run" that executes until n steps have passed. Also controls to pause that run.
// This is not that type of World. We can get rid of those controls.
AB.maxSteps = 1000000;
AB.drawRunControls = false;
ABWorld.drawCameraControls = false; // remove some controls to change the camera mode
// --- 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();
});
}
//--- two 2D grids -------------------------------------------------------------------------------
function translateMy ( i, j )
// the absolute 3D position of an (i,j) square on my grid is:
// width x = (i * squaresize)
// height y = 0
// depth z = (j * squaresize)
{
var v = new THREE.Vector3();
v.x = (i * squaresize) ;
v.y = 0 ;
v.z = (j * squaresize) ;
return v;
}
function translateOpponent ( i, j )
// put 2nd grid beside the 1st grid, offset on the x dimension
{
var v = new THREE.Vector3();
v.x = (i * squaresize) + MAXPOS ;
v.y = 0 ;
v.z = (j * squaresize) ;
return v;
}
function makeEverything()
{
makeGrids();
makeShips();
}
function makeGrids()
{
var i,j, shape, thecube, position, lookat;
shape = new THREE.BoxGeometry ( squaresize, squaresize, squaresize );
// make the two grids together:
for ( i = 0; i < gridsize ; i++ )
for ( j = 0; j < gridsize ; j++ )
if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) ) // if at one of the edges
{
thecube = new THREE.Mesh( shape );
thecube.material = new THREE.MeshBasicMaterial( { map: wall_texture } );
position = translateMy(i,j);
thecube.position.copy ( position );
ABWorld.scene.add(thecube);
thecube = new THREE.Mesh( shape );
thecube.material = new THREE.MeshBasicMaterial( { map: wall_texture } );
position = translateOpponent(i,j);
thecube.position.copy ( position );
ABWorld.scene.add(thecube);
}
// with two grids, need to figure where to put camera, and what to point it at
// point it at approximately middle of the two grids:
lookat = translateMy ( gridsize, gridsize/2 );
// experiment to find some camera position off to the bottom:
// position = new THREE.Vector3 (x, y, z);
position = new THREE.Vector3 ( MAXPOS, MAXPOS * 1.2, MAXPOS * 1.3 );
ABWorld.cameraCustom ( position, lookat ); // set up customised camera at this position to look at this point
}
function makeShips()
// make randomised ships
// only make them in my grid - opponent's grid starts off unknown
{
var p, i, j, shape, thecube, position;
shape = new THREE.BoxGeometry ( squaresize, squaresize, squaresize );
// make a few random blocks using a "for" loop that goes round 5 times
// would be nice to be able to say "do 5 times" but no easy way of saying that!
for ( p = 1; p <=5 ; p++ )
{
thecube = new THREE.Mesh( shape );
thecube.material = new THREE.MeshBasicMaterial( { map: ship_texture } );
i = AB.randomIntAtoB ( 1, gridsize-2 ); // positions 0 and (gridsize-1) are walls
j = AB.randomIntAtoB ( 1, gridsize-2 );
position = translateMy ( i, j );
thecube.position.copy ( position );
ABWorld.scene.add(thecube);
}
}
// === end of code ===========================================================================================================
// Exercises:
// Offset the 2nd grid to a new position.
// Move the camera to a new position.
// Change the point the camera looks at.
// Make a huge number of ships. Fill the grid.
// Make ships whose positions go "out of bounds" to see the value of bounds checking.
// Advanced exercise: Move the 2nd grid to be perpendicular to the 1st grid.
// Advanced exercise: Stop it putting 2 ships on the same square.
// Advanced exercise: Make ships that cover 2 or 3 squares in a row.
// Outcomes: Student can:
// Learn more about addressing a space with code, and how to offset objects to new positions.
// Learn how to address specific points in 3D space by creating 3D vectors in code.
// Learn more about "for" loops.
// Learn how to use random number generation functions.
// If they do the advanced exercises: Create a variable number of objects, and learn how to keep track of what you have created.