HTML5 Canvas [194]
The animatePlayer() function is called next. Its job is to move the player object to its new location while running through its animation frames. Here is the code from the animatePlayer() function:
player.x += player.dx*player.speed;
player.currentTile++;if (player.currentTile==playerTiles.length){
player.currentTile = 0;
}
renderPlayField();
if (player.x==player.destinationX && player.y==player.destinationY){
switchGameState(GAME_STATE_EVALUATE_PLAYER_MOVE);
}
First, the player object’s x and y locations are increased by the player.speed * player.dx (or dy). The tile size is 32, so we must use a speed value that is evenly divided into 32. The values 1, 2, 4, 8, 16, and 32 are all valid.
This function also runs though the playerTiles array on each game loop iteration. This will render the tank tracks moving, simulating a smooth ride from one tile to the next.
Next, let’s take a closer look at how we render the playField.
Rendering Logic Overview
Each time the game renders objects to the screen, it runs through the entire render() function. It does this to ensure that even the nonmoving objects are rendered back to the game screen. The render() function looks like this:
function renderPlayField() {
fillBackground();
drawPlayField();
drawPlayer();
drawEnemy();
drawExplosions();
}
First, we draw the plain black background, then we draw the playField, and after that we draw the game objects. drawPlayField() draws the map of tiles to the game screen. This function is similar to the functions in Chapter 4, but with some additions for our game. Let’s review how it is organized:
function drawPlayField(){
for (rowCtr=0;rowCtr<15;rowCtr++){
for (colCtr=0;colCtr<15;colCtr++) {
var sourceX = Math.floor((playField[rowCtr][colCtr]) % 8) * 32;
var sourceY = Math.floor((playField[rowCtr][colCtr]) /8) *32;
if (playField[rowCtr][colCtr] != roadTile){
context.drawImage(tileSheet, 0, 0,32,32,colCtr*32,rowCtr*32,32,32);
}
context.drawImage(tileSheet, sourceX, sourceY, 32,32,
colCtr*32,rowCtr*32,32,32);
}
}
}
The drawPlayField() function loops through the rows in the playField array, and then through each column inside each row. If the tile id number at playField[rowCtr][colCtr] is a road tile, it simply paints that tile at the correct location on the playField. If the tile id is a game object (not a road tile), it first paints a road tile in that spot and then paints the object tile in that spot.
Simple Homegrown AI Overview
The enemy tanks chase the player object based on a set of simple rules. We have coded those rules into the gameStateEnemyMove() function, which is one of the longest and most complicated functions in this book. Let’s first step through the logic used to create the function, and then you can examine it in Example 9-3.
This function starts by looping through the enemy array. It must determine a new tile location on which to move each enemy. To do so, it follows some simple rules that determine the order in which the testBounds() function will test the movement directions:
First, it tests to see whether the player is closer to the enemy vertically or horizontally.
If vertically, and the player is above the enemy, it places up and then down into the directionsToTest array.
If vertically, and the player is below the enemy, it places down and then up into the directionsToTest array.
NOTE
The up and then down, or down and then up, directions are pushed into the directionsTest array to simplify the AI. The logic here is if the player is “up” from the enemy, but the enemy is blocked by an object, the enemy will try the opposite direction first. In our game, there will be no instance where an object blocks the direction the enemy tank wants to move in. This is because the only illegal direction is trying to move off the bounds of the screen. If we add tiles to our playfield that “block” the enemy, this entire set of AI code suddenly becomes very useful and necessary. We have included this entire “homegrown chase AI” in our game in case more of these