// Cloned by Przemyslaw Majda on 5 Dec 2022 from World "Shooting Range" by Enhanced
// Please leave this clone trail here.
// Cloned by Enhanced on 13 Jun 2018 from World "Enhancing Shooting Range (Space To Shoot)" by Mathias Bazin
// Please leave this clone trail here.
// Cloned by Mathias Bazin on 5 Jun 2018 from World "Shooting Range (Space To Shoot)" by Adrian Rabbitte
// Please leave this clone trail here.
//Correct This world
///Shooting Range (Space To Shoot)
/*
Adrian James Rabbitt 14500447
I created a game where the user controls the agent (gun), and its objective is to shoot the 2 animals (Swan,Duck) in the shooting zone. Using AI I designed the movements of duck and the swan so that there aim is to
move to a safe position, The animals will try hide behind a stone or go to the corners to safety. The user must not hit a stone or they lose.
DESIGN
1) The world consists of a firing range and a shooting zone, a small wall is placed as a barrier between them, The agent and the animals are not able to pass this barrier.
The firing range is a quater size of the the entire zone.
The shooting zone is looked upon as a lake/pond repesented by a bluecolour, surrounded by timber walls.
2) Position of Stones:
I placed the stones constant across the shooting zone to make the game more difficult to shoot the animals.
3) Logical Moves of Animals:
I created the logic for the movements of the animals so that they are always moving away from the gun(agent), and always trying to hide behind a stone.
The animals will attract towards the corners as it is the furthest point away from the agent. The animal will stay in the corner to hide, here the user (gun) should move
back to the corner of its shooting range to hide from the animal , so that the animal will move back out. When an animal is shot a message is displayed on the screen and the animal will
stop moving. In some cases the animals moves a number of spaces in one move, this is to create a vision that the animal can fly to certain spots.
From studying A* algorithms I wanted to do the reverse to find the shortest route away from the agent.
4) Logical Move of Agent.
The movement of the agent is controled by the the directional pad on the keyboard. I did not use rotations on this as the gun is always shooting forward along the i axis.
5)MOVE WIDTH.
I changed the camera view of the agent so it is only showing the tip of the gun. The gun will point first to the duck, when the duck is shot, it points the camera towards the swan.
This leaves completing the game much easier.
6)Bullet.
When creating the bullet, I used the same detail as if I was making another enemy(animal) starting at the coordinates of the agent and moving forward by decrementing its j value.
This means that you can shoot a number of bullets during a set interval of time.
7)Senors.
Using i and j corrdinates I was able to get the game to show if the gun shot a stone or an animal by matching its coordanites.
8)Music
When the gun is shot there is audio in the background when the bullet is created.This makes the game more realistic.
PROBLEMS FACED
1) Before i undertook implementing the project I spent a lot of time studying javascript , however by actually playing around with worlds I was able to see my actions visually and fix them
helped me a lot, by doing this project my knoledge and condfidence with javascript improved rapidly. I have created a world which could do with a lot off improvement and which i would love
to do in my own spare time.
2) Shooting : when you press space multiple of times it creates a number of bullets that stay still. Here I created the game when you press space it creates a new intancee of bullet each time, this stops the previos bullets.
I hope to fix this in the future.
3)Rotations : I left the rotations to last which turned out to be a problem faced due movements of the animals. I was implementing the rotations so the animals would move in an arc shap to
give the impression they are swimming.
4)Loading GTA files.
5)Creating objects.
TESTING.
1)If an animal is in a corner and doesnt want to come out, move the gun back to the edge of the shooting range(on the same i axis) to hide, The animal will then move out.
2)Shoot Maze to Lose.
3)Shoot the 2 animals to win.
4)Shoot an animal that is on front of a stone(You Lose)
5)Move the gun to the corner of the shooting range, the animals will move together to the opisite side.
6)MOVE WIDTH , If there is a level of diffuculty in shooting the animals by clicking MOVE WIDTH it will show the camera from the Gun.
OVERALL VIEW.
Overall I enjoyed implementing this project from its learning outcomes to its creation. At the start of the project i had a plan and stuck to it, However I could have did a few improvements
such as the rotations and the logical moves. I had the logical moves working quite good, but then changed them around to suit different scenarios. The size of the world I could have made bigger
to add more animals to target and to set a set interval of time to shoot each animal. I hope to keep changing the world in future to create a proper game with a lot more competion in it.
CODE
Below is the code. I started off using Snow World where I then implemented my own version to Shooting Range.
I have commented out the code in short sentences. The code is a bit messy as i did not have time at the end to fix .
theagent = gun , theSheep = swan , theenemy = duck.
CREDITS.
http://computing.dcu.ie/~humphrys/ca318/.
http://tf3dm.com/3d-model.
www.google.ie
CHANGES
added smooth movement, music, can shoot more than 1 bullet, more feedback when targets get hit
*/
threeworld.drawCameraControls = false;
AB.drawRunControls = false;
//---- global constants: -------------------------------------------------------
const CLOCKTICK = 100; // speed of run - move things every n milliseconds
const MAXSTEPS = 1000; // length of a run before final score
const SCREENSHOT_STEP = 50;
const LIGHTCOLOR = 0xffffff;
const shortDistance = 30;
const gridsize = 20 ; // number of squares along side of world
const NOBOXES = 8
const squaresize = 30; // size of square in pixels
const MAXPOS = gridsize * squaresize; // length of one side in pixels
const SKYCOLOR = 0x009933; // a number, not a string
const BLANKCOLOR = 0x009933 ;
const grey = 0x808080;
const red = SKYCOLOR // make objects this color until texture arrives (from asynchronous file read)
const show3d = true; // Switch between 3d and 2d view (both using Three.js)
const startRadiusConst = MAXPOS * 0.8 ; // distance from centre to start the camera at
const skyboxConst = MAXPOS * 3 ; // where to put skybox
const maxRadiusConst = MAXPOS * 10 ; // maximum distance from camera we will render things
const BACKWALLPOS = -299;
//--- Mind can pick one of these actions -----------------
const ACTION_LEFT = 0;
const ACTION_RIGHT = 1;
const ACTION_UP = 2;
const ACTION_DOWN = 3;
const ACTION_STAYSTILL = 4;
// in initial view, (smaller-larger) on i axis is aligned with (left-right)
// in initial view, (smaller-larger) on j axis is aligned with (away from you - towards you)
// contents of a grid square
const GRID_BLANK = 0;
const GRID_WALL = 1;
const GRID_MAZE = 2;
const GRID_BOTTOM = 3;
const GRID_BOTTOM2 = 4;
const GRID_STONE = 5
// --- some useful random functions -------------------------------------------
function randomfloatAtoB ( A, B )
{
return ( A + ( Math.random() * (B-A) ) );
}
function randomintAtoB ( A, B )
{
return ( Math.round ( randomfloatAtoB ( A, B ) ) );
}
function randomBoolean()
{
if ( Math.random() < 0.5 ) { return false; }
else { return true; }
}
//---- start of World class -------------------------------------------------------
function World() {
// most of World can be private
// regular "var" syntax means private variables:
var BOXHEIGHT; // 3d or 2d box height
var GRID = new Array(gridsize);
var GRID2 = new Array(gridsize);
var GRID3 = new Array(gridsize);// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array
var WALLS = new Array ( 4 * gridsize );
var WALLS2 = new Array ( 4 * gridsize );// need to keep handles to wall and maze objects so can find them later to paint them
var MAZE = new Array ( NOBOXES );
var STONE = new Array ( NOBOXES );
var BOTTOM = new Array (gridsize ) // created an array for the bottom of the shooting zone.
var BOTTOM2 = new Array (gridsize )// created an array for the bottom of the firing range.
var theagent, theenemy,theSheep;// inializing agent enemy sheep
var agentVelocity = new THREE.Vector3(0,0,0);
var animals = [];
var bullets = [];
var meatShape = new THREE.BoxGeometry ( squaresize, squaresize, squaresize);
var meatTexture = new THREE.ImageUtils.loadTexture ( "/uploads/mathias/meat.jpg" );
meatTexture.minFilter = THREE.LinearFilter;
const DEATHQUACK = "/uploads/mathias/deathQuack.mp3"
var quack = new Audio(DEATHQUACK);
const MUSIC = "/uploads/mathias/kalimba.mp3"
var music = new Audio(MUSIC);
// enemy and agent position on squares
var ei, ej, ai, aj, si, sj,bi,bj; // coordinates for agent enemy sheep and bullet.
var badsteps;
var goodsteps;
var step;
var self = this; // needed for private fn to call public fn - see below
// regular "function" syntax means private functions:
function initGrid()
{
for (var i = 0; i < gridsize ; i++)
{
GRID[i] = new Array(gridsize); // each element is an array
for (var j = 0; j < gridsize ; j++)
{
GRID[i][j] = GRID_BLANK ;
}
}
}
function initGrid2()
{
for (var i = 0; i < gridsize ; i++)
{
GRID2[i] = new Array(gridsize); // each element is an array for the bottom part of the grid.
for (var j = 0; j < gridsize ; j++)
{
GRID2[i][j] = GRID_BLANK ;
}
}
}
function occupied ( i, j ) // is this square occupied
{
if ( ( ei == i ) && ( ej == j ) ) return true; // variable objects
if ( ( ai == i ) && ( aj == j ) ) return true;
if ( ( si == i ) && ( sj == j ) ) return true; // booleans to check id sppace on the map is free.
if ( GRID[i][j] == GRID_WALL) return true;
if ( GRID[i][j] == GRID_MAZE) return true;// fixed objects // added booleans for Maze and Stone.
if ( GRID[i][j] == GRID_STONE) return true;
return false;
}
// logically, coordinates are: y=0, x and z all positive (no negative)
// logically my dimensions are all positive 0 to MAXPOS
// to centre everything on origin, subtract (MAXPOS/2) from all dimensions
function translate ( x )
{
return ( x - ( MAXPOS/2 ) );
}
//--- skybox ----------------------------------------------------------------------------------------------
function initSkybox()
{
// x,y,z positive and negative faces have to be in certain order in the array
// mountain skybox, credit:
// http://stemkoski.github.io/Three.js/Skybox.html
// var materialArray = [
//( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/adrian/trees.jpg" ), side: THREE.BackSide } ) ),
//( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture("/uploads/adrian/trees.jpg" ), side: THREE.BackSide } ) ),
//( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/adrian/sky.jpeg" ), side: THREE.BackSide } ) ),
//( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/adrian/grass.jpg" ), side: THREE.BackSide } ) ),
//( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/adrian/trees.jpg" ), side: THREE.BackSide } ) ),
//( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/adrian/trees.jpg" ), side: THREE.BackSide } ) ),
// ];
var materialArray = [
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/seanhutchinson/skyrender0001.bmp" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/seanhutchinson/skyrender0004.bmp" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/seanhutchinson/skyrender0003.bmp" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/seanhutchinson/skyrender0006.bmp" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/seanhutchinson/skyrender0005.bmp" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0002.bmp"), side: THREE.BackSide } ) ),
];
var skyGeometry = new THREE.CubeGeometry ( skyboxConst, skyboxConst, skyboxConst );
var skyMaterial = new THREE.MeshFaceMaterial ( materialArray );
var theskybox = new THREE.Mesh ( skyGeometry, skyMaterial );
threeworld.scene.add( theskybox ); // We are inside a giant Cube
}
// This does the file read the old way using loadTexture.
// (todo) Change to asynchronous TextureLoader. A bit complex:
// Make blank skybox. Start 6 asynch file loads to call 6 return functions.
// Each return function checks if all 6 loaded yet. Once all 6 loaded, paint the skybox.
function loadtextures2()
{
var loader8 = new THREE.TextureLoader();
loader8.load ( '/uploads/adrian/silver.png', function ( thetexture ) { // created a second load function seperate for the bullet itself.
thetexture.minFilter = THREE.LinearFilter;
bullet.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
}
function loadTextures()
{
var manager = new THREE.LoadingManager(); // loading 3d objects onto map.
var loader = new THREE.OBJLoader( manager );
loader.load( "/uploads/adrian/duck.obj", buildenemy ); // duck Object
loader.load( "/uploads/adrian/swan.obj", buildsheep ); // swan Object
loader.load( "/uploads/adrian/gunn.obj", buildagent ); // gun object
var loader1 = new THREE.TextureLoader();
loader1.load ( '/uploads/adrian/woodp.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter; //loading colour of walls.
paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader6 = new THREE.TextureLoader();
loader6.load ( '/uploads/adrian/waves2.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintBottom2 ( new THREE.MeshBasicMaterial( { map: thetexture } ) ); //loading colour of water
} );
var loader2 = new THREE.TextureLoader();
loader2.load ( '/uploads/adrian/sand3.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter; //loading colour of stone.
paintMaze ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
//var loader3 = new THREE.TextureLoader();
//loader3.load ( '/uploads/adrian/cat.jpg', function ( thetexture ) {
// thetexture.minFilter = THREE.LinearFilter;
// theagent.material = new THREE.MeshBasicMaterial( { map: thetexture } );
// } );
var loader4 = new THREE.TextureLoader();
loader4.load ( '/uploads/adrian/mouce.png', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
theenemy.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
var loader5 = new THREE.TextureLoader();
loader5.load ( '/uploads/adrian/bm.png', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
theSheep.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
}
function buildenemy ( object )
{
object.scale.multiplyScalar (150,150,150); // make 3d object n times bigger
object.traverse( paintEnemy );
theenemy = object;
threeworld.scene.add( theenemy ); // adding enemy to the world
}
function buildagent ( object )
{
object.scale.multiplyScalar (.7,.7,.7); // make 3d object n times bigger
object.traverse( paintAgent );
theagent = object;
threeworld.scene.add( theagent ); // adding agent to the world
}
function buildsheep ( object )
{
object.scale.multiplyScalar ( 150,150,150 ); // make 3d object n times bigger
object.traverse( paintSheep );
theSheep = object;
threeworld.scene.add( theSheep ); // adding sheep to the world
}
function paintEnemy ( child )
{
if ( child instanceof THREE.Mesh ) // paining the enemy
{
child.material.map = THREE.ImageUtils.loadTexture( "/uploads/adrian/feathers.jpg" );
}
}
function paintAgent ( child )
{
if ( child instanceof THREE.Mesh ) // painting the agent.
{
child.material.map = THREE.ImageUtils.loadTexture( "/uploads/adrian/blackg.png" );
}
}
function paintSheep ( child )
{
if ( child instanceof THREE.Mesh ) //paining the sheep.
{
child.material.map = THREE.ImageUtils.loadTexture( "/uploads/adrian/duck.jpg" ); // painting the sheep.
}
}
function drawEnemy() // given ei, ej, draw it
{
if ( theenemy )
{
var x = translate ( ei * squaresize );
var z = translate ( ej * squaresize ); // drawing The enemy.
var y = ( 11);
theenemy.position.x = x;
theenemy.position.y = y;
theenemy.position.z = z;
}
}
// --- add fixed objects ----------------------------------------
function initLogicalWalls() // set up logical walls in data structure, whether doing graphical run or not
{
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < gridsize ; j++)
if ( ( i===0 ) || ( i==gridsize-1 ) || ( j===0 ) || ( j==gridsize-1 ) ) // boolean to cheack if there is a wall.
{
GRID[i][j] = GRID_WALL ;
}
}
function initLogicalBottom() // set up logical walls in data structure, whether doing graphical run or not
{
for (var i = 0; i < gridsize ; i++)
for (var j = 14; j < gridsize ; j++)
GRID2[i][j] = GRID_BOTTOM;
}
function initLogicalBottom2() // set up logical walls in data structure, whether doing graphical run or not
{
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < 14 ; j++)
GRID2[i][j] = GRID_BOTTOM2;
}
function initThreeWalls() // graphical run only, set up blank boxes, painted later
{
var t = 0;
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < gridsize ; j++)
if ( GRID[i][j] == GRID_WALL )
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 30;
threeworld.scene.add(thecube);
WALLS[t] = thecube; // save it for later
t++;
}
}
function initThreeBottom() // graphical run only, set up blank boxes, painted later
{
var t = 0;
for (var i = 0; i < gridsize ; i++)
for (var j = 14; j < gridsize ; j++)
if ( GRID2[i][j] == GRID_BOTTOM )
{
var shape = new THREE.BoxGeometry( squaresize, 5, squaresize );
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( red );
thecube.position.x = translate ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 12;
threeworld.scene.add(thecube);
BOTTOM[t] = thecube; // save it for later
t++;
}
}
function initThreeBottom2() // graphical run only, set up blank boxes, painted later
{
var a = 0;
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < 14 ; j++)
if ( GRID2[i][j] == GRID_BOTTOM2 )
{
var shape = new THREE.BoxGeometry( squaresize, 5, squaresize );
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 12;
threeworld.scene.add(thecube);
BOTTOM2[a] = thecube; // save it for later
a++;
}
}
function paintWalls ( material )
{
for ( var i = 0; i < WALLS.length; i++ ) // paining the walls
{
if ( WALLS[i] ) WALLS[i].material = material;
}
}
function paintBottom2 ( material )
{
for ( var i = 0; i < BOTTOM2.length; i++ )
{
if ( BOTTOM2[i] ) BOTTOM2[i].material = material; // paining the bottom of the world.
}
}
function initLogicalMaze()
{
GRID[2][4] = GRID_MAZE ; // hardcoding the positions of the stones.
GRID[4][4] = GRID_MAZE ;
GRID[8][6] = GRID_MAZE ; // I did this as I wanted the stones positioned constantly.
GRID[12][3] = GRID_MAZE ;
GRID[18][7] = GRID_MAZE ;
GRID[10][2] = GRID_MAZE ;
GRID[4][10] = GRID_MAZE ;
GRID[16][10] = GRID_MAZE ;
}
function initLogicalStone()
{
for( var i = 0; i < 20; i ++)
GRID[i][14] = GRID_STONE ;
}
function initThreeMaze()
{
var t = 0;
for (var i = 0; i < gridsize ; i++)
for (var j = 0; j < 13 ; j++)
if ( GRID[i][j] == GRID_MAZE )
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize ); //setting the box hieght of the stones
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize );
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 30;
threeworld.scene.add(thecube);
MAZE[t] = thecube; // save it for later
t++;
}
}
function initThreeStone()
{
var t = 0;
for (var i = 0; i < gridsize ; i++)
for (var j = 13; j < gridsize ; j++)
if ( GRID[i][j] == GRID_STONE )
{
var shape = new THREE.BoxGeometry( 30, 10, 30 ); //setting the small stone wall in front of the gun.
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( grey );
thecube.position.x = translate ( i * squaresize );
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 15;
threeworld.scene.add(thecube);
STONE[t] = thecube; // save it for later
t++;
}
}
function paintMaze ( material )
{
for ( var i = 0; i < MAZE.length; i++ )
{
if ( MAZE[i] ) MAZE[i].material = material; // painting the stones in the world.
}
}
// --- enemy functions -----------------------------------
function drawSheep() // given ei, ej, draw it
{
console.log("2")
var x = translate ( si * squaresize ); //drawing the sheep.
var z = translate ( sj * squaresize );
var y = 17;
theSheep.position.x = x;
theSheep.position.y = y;
theSheep.position.z = z;
threeworld.scene.add(theSheep);
}
function initLogicalEnemy()
{
// start in random location:
var i, j;
do
{
i = 12;
j = 9;
}
while ( occupied(i,j) ); // search for empty square
ei = i;
ej = j;
}
function initLogicalSheep()
{
// start in random location:
var i, j;
do
{
i = 8;
j = 9;
}
while ( occupied(i,j) ); // search for empty square
si = i;
sj = j;
}
function initThreebullet()
{
var shape = new THREE.BoxGeometry( 5, 5, 5 ); //creating the layout for the bullet.
bullet = new THREE.Mesh( shape );
bullet.material.color.setHex( BLANKCOLOR );
bullet.position.set(theagent.position.x, 35 ,theagent.position.z );
bullets.push(bullet);
threeworld.scene.add(bullet);
}
function initThreeSheep()
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize )// creating the layout for sheep.
theSheep = new THREE.Mesh( shape );
theSheep.material.color.setHex( BLANKCOLOR );
drawSheep();
}
var x =0;
// --- second enemy functions -----------------------------------
function moveLogicalEnemy()
{
// move towards agent
// put some randomness in so it won't get stuck with barriers
var i, j;
if ( ei > ai ) // if the agents i value is smaller than the enemys i value.
{
i = randomintAtoB(ei, ei+1); // setting randomness.
theenemy.rotation.set ( 0, 1 * (Math.PI / 2), 0 ); // setting the rotation of the enemy.
}
if ( ei == ai )
{ // if the coordanites are equal
theenemy.rotation.set ( 0, 3, 0 ); // set rotation to oppisite.
i = ei;
}
if ( ei < ai ) // if the enemys i value is smaller than the agents i value.
{
theenemy.rotation.set ( 0, 3 * (Math.PI / 2), 0 ); // set new rotation.
i = randomintAtoB(ei-1, ei); // getting randomness.
}
if ( ej > aj )
{
theenemy.rotation.set ( 0, 9, 0 );
}
if ( ej == aj )
{
j = randomintAtoB(ej-1, ej)
theenemy.rotation.set ( 0, 3 * (Math.PI / 2), 0 );
}
if ( ej < aj )
{
j = randomintAtoB(ej-1, ej);
}
if (ei == 1 && ej < 3) //if th enemy moves to a corner move it back out to give the agent a better chance.
{
ei = 3; // new coordanites of the enemy.
ej = 6;
theenemy.rotation.set ( 0, 2 * (Math.PI / 2), 0 ); // set new rotation.
if(ei == gridsize -1)
theenemy.rotation.set ( 0, 0 * (Math.PI / 2), 0 );
}
if(ei == gridsize -2 && ej == 7) // if the enemy is constantly moving up along the walls move it out.
if (ei == 1 && ej < 3)
{
ei = 4; // new position of enemy
ej = 5;
}
if (ei == gridsize -1 && ej < 3)
{
ei = 17; // if the enemy is in the corner and the j value is smaller than 3 move to new position on world.
ej = 5;
theenemy.rotation.set ( 0,2* (Math.PI / 2) , 0 ); // set new rotation of the enemy.
}
if (ej == 1 && ei < 3)
{
ei = 3; // setting new coordanites if the enemy is in a constant position.
ej = 6;
}
if (ej == 1 && ei > 17 )
{
ei = 16;
ej = 6;
theenemy.rotation.set ( 0, 0, 0 );
}
if ( ! occupied(i,j) ) // if no obstacle then move, else just miss a turn
{
ei = i;
ej = j;
}
}
function moveLogicalbullets()
{
for(let b of bullets)
{
if(b.position.z < BACKWALLPOS)
{
threeworld.scene.remove(b);
removeBullet(b);
}
else if ( hitMaze(b) )
{
threeworld.scene.remove(b);
removeBullet(b);
}
else
{
b.position.z -= 30;
}
}
}
function hitMaze(b)
{
for (let m of MAZE)
{
if (b.position.distanceTo(m.position) < shortDistance) return true;
}
return false;
}
function moveLogicalSheep()
{
// move towards agent
// put some randomness in so it won't get stuck with barriers
var i, j;
if ( si > ai )
{
theSheep.rotation.set ( 0, 1.5, 0 ); //*note= same logic as enemy.
i = randomintAtoB(si, si+1);
}
if ( si == ai )
{
i = si;
theSheep.rotation.set ( 0, 4, 0 );
}
if ( si < ai )
{
theSheep.rotation.set ( 0, 5, 0 );
i = randomintAtoB(si-1, si);
}
if ( sj > aj )
{
j = randomintAtoB(sj, sj+1);
}
if ( sj == aj ) j = sj;
if ( sj < aj ) j = randomintAtoB(sj-1, sj); //moving the Sheep trough the maze and setting new loactions for the sheep.
if (si == 1 && sj < 3)
{
si = 3;
sj = 2;
theSheep.rotation.set ( 0, 1, 0 );
}
if (si == gridsize - 2 && sj > 17)
{
si = 17;
sj = 5;
theSheep.rotation.set ( 0, 6, 0 );
}
if (sj == 1 && si < 3)
{
si = 3;
sj = 6;
theSheep.rotation.set ( 0, 1, 0 );
}
if (sj == 1 && si > 17)
{
si = 17;
sj = 5;
theSheep.rotation.set ( 0, 1, 0 );
}
if ( ! occupied(i,j) ) // if no obstacle then move, else just miss a turn
{
si = i;
sj = j;
}
}
if (si > 16 )
{
si = 10;
sj = 10;
}
// --- agent functions -----------------------------------
function drawAgent() // given ai, aj, draw it
{
if ( theagent )
{
var x = translate ( ai * squaresize ); // drawing new agent to the scene
var z = translate ( aj * squaresize );
var y = ( 25 );
theagent.position.x = x;
theagent.position.y = y;
theagent.position.z = z;
threeworld.follow.copy ( theagent.position ); // follow vector = agent position (for camera following agent)
threeworld.follow.y = ( 60 ); // set location of the camra to the point of the gun.
// put camera higher up
}
}
function initLogicalAgent()
{
ai = 9;
aj = 15;
}
function initThreeAgent()
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
theagent = new THREE.Mesh( shape );
theagent.material.color.setHex( BLANKCOLOR );
drawAgent();
}
function moveLogicalAgent( a ) // this is called by the infrastructure that gets action a from the Mind
{
if(aj > ej)
theagent.rotation.set ( 0, 2 * (Math.PI / 2), 0 ); // setting the rotation of the agent constant.
var i = ai;
var j = aj;
if ( a == ACTION_LEFT ) agentVelocity.x = -1; // ithis functions will recieve a parameter indicating its next move by the keyhandler.
else if ( a == ACTION_RIGHT ) agentVelocity.x = 1;
else if ( a == ACTION_UP ) agentVelocity.z = 1;
else if ( a == ACTION_DOWN ) agentVelocity.z = -1;
}
function keyHandler(e)
// user control
// Note that this.takeAction(a) is constantly running at same time, redrawing the screen.
{
if (e.keyCode == 37) moveLogicalAgent( ACTION_LEFT );
if (e.keyCode == 38) moveLogicalAgent( ACTION_DOWN );
if (e.keyCode == 39) moveLogicalAgent( ACTION_RIGHT );
if (e.keyCode == 40) moveLogicalAgent( ACTION_UP );
if (e.keyCode == 32) // if the keycode is space.
{
initThreebullet(); // setting up bullet
//initLogicalbullet(); // bullet start location
playBang();
loadtextures2(); // load the bullets textures.
}
}
function onKeyUp(e)
{
agentVelocity.set(0,0,0);
}
function removeBullet(b)
{
let index = bullets.indexOf(b);
if (index != -1) bullets.splice(index,1);
else console.log("error removing bullet");
}
// --- score: -----------------------------------
function badstep() // is the enemy within one square of the agent
{
if ( ( Math.abs(ei - ai) < 2 ) && ( Math.abs(ej - aj) < 2 ) ) return true;
else return false;
}
var eb = false; // setting a boolean for if shot.
function enemyBlocked() // if the enemy is hit by the bullet return true.
{
for(let b of bullets)
{
if (b.position.distanceTo(theenemy.position) < shortDistance && !eb)
{
eb = true;
threeworld.scene.remove(b);
removeBullet(b);
threeworld.scene.remove(theenemy);
var theobject = new THREE.Mesh ( meatShape );
theobject.material = new THREE.MeshBasicMaterial ( { map: meatTexture } );
theobject.position.set(theenemy.position.x,theenemy.position.y,theenemy.position.z);
threeworld.scene.add(theobject);
quack.play();
return true;
}
}
return false;
}
var sb = false; // boolean for sheep shot.
function sheepBlocked() // if the sheep is hit by the bullet return true.
{
for(let b of bullets)
{
if (b.position.distanceTo(theSheep.position) < shortDistance && !sb)
{
sb = true;
threeworld.scene.remove(b);
removeBullet(b);
threeworld.scene.remove(theSheep);
var theobject = new THREE.Mesh ( meatShape );
theobject.material = new THREE.MeshBasicMaterial ( { map: meatTexture } );
theobject.position.set(theSheep.position.x,theSheep.position.y,theSheep.position.z);
threeworld.scene.add(theobject);
quack.play();
return true;
}
}
return false;
}
var bm = false; // boolean to cheack if the bullet hit the maze.
function updateCamera()
{
if (theagent.position.distanceTo(theSheep.position) < theagent.position.distanceTo(theenemy.position))
{
threeworld.lookat = theSheep.position;
}
else
{
threeworld.lookat = theenemy.position;
}
}
function count()
{
var x = x + 1;
}
function updateStatusBefore(a)
// this is called before anyone has moved on this step, agent has just proposed an action
// update status to show old state and proposed move
{
}
function updateStatusAfter() // agent and enemy have moved, can calculate score
{
}
//--- public functions / interface / API ----------------------------------------------------------
this.endCondition = false; // If set to true, run will end.
this.newRun = function()
{
// (subtle bug) must reset variables like these inside newRun (in case do multiple runs)
this.endCondition = false;
badsteps = 0;
goodsteps = 0;
step = 0;
// for all runs:
initGrid();
initGrid2(); // setting up all aspects of the world.
initLogicalWalls();
initLogicalMaze();
initLogicalStone();
initLogicalBottom();
initLogicalBottom2();
initLogicalAgent();
if( eb != true && bm != true) // if the enemy was not shot and the maze was not hit
{
initLogicalEnemy(); // move the enemy
}
if( sb != true && bm!= true) // if the sheep was not shot and the maze was not hit
{
initLogicalSheep(); //move the sheep.
}
// for graphical runs only:
if ( true )
{
// var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
// thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
// threeworld.scene.add(thelight);
if ( show3d )
{
BOXHEIGHT = squaresize;
threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
// thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
threeworld.scene.add(thelight);
}
else
{
BOXHEIGHT = 1;
threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
}
initSkybox();
// Set up objects first:
initThreeWalls();
initThreeMaze();
initThreeStone();
initThreeBottom();
initThreeBottom2(); // setting up cubes for the objects.
$("#user_span5").html("<p>Use arrow keys to move and space to shoot.</p>");
// Then paint them with textures - asynchronous load of textures from files.
// The texture file loads return at some unknown future time in some unknown order.
// Because of the unknown order, it is probably best to make objects first and later paint them, rather than have the objects made when the file reads return.
// It is safe to paint objects in random order, but might not be safe to create objects in random order.
loadTextures();
let meat = new THREE.Mesh( meatShape, meatTexture );
meat.position.set(0,50,0)
threeworld.scene.add(meat);
document.onkeydown = keyHandler;
document.onkeyup = onKeyUp;
music.loop = true;
music.volume = 0.4;
music.play();
threeworld.cameraMove();
}
};
this.getState = function()
{
var x = [ ai, aj, ei, ej, si ,sj ]; // linking the mind to the varibles of the agent , enemy and the sheep.
return ( x );
};
this.nextStep = function()
{
var a = 4;
var i = ai+ agentVelocity.x;
var j = aj+ agentVelocity.z;
if ( ! occupied(i,j) )
{
ai = i; // if the space is not occupied set new location of the agent.
aj = j;
}
step++;
if ( true )
updateStatusBefore(a);
// show status line before moves
if(sb != true || eb != true || bm !=true) // if the sheep is not shot or the enemy is not shot, move the agent
{
moveLogicalAgent(a);
}
if ( ( step % 3 ) == 0 && !eb ) //if enemy is not shot move the enemy.
{ // slow the enemy down to every nth step
moveLogicalEnemy();
}
moveLogicalbullets(); // moving the bullet at speed.
if(enemyBlocked() || sheepBlocked())
{
x = x+1
if(eb == true && sb == true) // if both the enemy and the sheep is shot end the game
{
this.endCondition = true;
}
}
if ( ( step % 3 ) == 0 && sb != true ) // if the sheep was not shot move the sheep.
moveLogicalSheep();
drawAgent(); // if the bullet was initalized dwar the bullet.
if( sb != true && bm != true) // if the sheep was not shot and the stone was not hit draw the new position of the sheep.
{
drawSheep();
}
if( eb != true && bm != true) // if the enemy was not shot and the stone was not shot draw the new position of the enemy.
{
drawEnemy();
}
updateCamera();
updateStatusAfter(); // show status line after moves
if ( enemyBlocked() || sheepBlocked() ) // if agent blocked in, game over
{
$("#user_span6").html( " <font color=red> <B> Shot animal 1 :)! </B> </font> " );
goodsteps = 0; // you score zero as far as database is concerned
if ( true )
{
musicPause();
soundAlarm();
}
}
};
this.endRun = function()
{
if ( true )
{
if ( this.endCondition )
$("#user_span6").html( " <font color=red> <B> Shot Animal 2, YOU WIN </B> </font> " );
music.pause();
}
};
this.getScore = function()
{
return ( ( goodsteps / step ) * 100 );
};
}
//---- end of World class -------------------------------------------------------
// --- music and sound effects ----------------------------------------
// credits:
// http://www.dl-sounds.com/royalty-free/defense-line/
// http://soundbible.com/1542-Air-Horn.html
function playBang()
{
// put music element in one of the spans
var x = new Audio("/uploads/adrian/gun.mp3");
x.volume = 0.5;
x.play();
}