HTML5 Canvas [191]
Turn-Based Game Flow and the State Machine
Our game logic and flow is separated into 16 discrete states. The entire application runs on a 40 frames per second interval timer:
switchGameState(GAME_STATE_INIT);
const FRAME_RATE = 40;
var intervalTime = 1000/FRAME_RATE;
setInterval(runGame, intervalTime )
As with the other games, in Chapter 8 and earlier in this chapter, we will use a function reference state machine to run our current game state. The switchGameState() function will be used to transition to a new game state. Let’s begin by discussing this function briefly, and then moving through the rest of the game functions.
NOTE
We will not reprint each line of code or dissect it in detail here. Use this section as a guide for perusing the entire set of game code included at the end of this chapter (in Example 9-3). By now, you have seen most of the code and ideas used to create this game logic. We will break out the new ideas and code in the sections that follow.
GAME_STATE_INIT
This state loads in the assets we need for our game. We are loading in only a single tile sheet and no sounds for Micro Tank Maze.
After the initial load, it sends the state machine to the GAME_STATE_WAIT_FOR_LOAD state until the load event has occurred.
GAME_STATE_WAIT_FOR_LOAD
This state simply makes sure that all the items in GAME_STATE_INIT have loaded properly. It then sends the state machine to the GAME_STATE_TITLE state.
GAME_STATE_TITLE
This state shows the title screen and then waits for the space bar to be pressed. When this happens, it sends the state machine to GAME_STATE_NEW_GAME.
GAME_STATE_NEW_GAME
This state resets all of the game arrays and objects and then calls the createPlayField() function. The createPlayField() function creates the playField and enemy arrays for the new game, as well as sets the player object’s starting location. Once it has finished, it calls the renderPlayField() function a single time to display the initial board on the game screen.
Once this completes, the state machine is now ready to start the real game loop. This is done by moving the game state machine to the GAME_STATE_WAIT_FOR_PLAYER_MOVE state.
GAME_STATE_WAIT_FOR_PLAYER_MOVE
This state waits for the player to press one of the four arrow buttons. Once the player has done so, the switch statement checks to see which arrow was pressed. Based on the direction pressed, the checkBounds() function is called.
NOTE
This state contains a bit of the new code for tile movement logic that we have not seen previously in this book. See the upcoming section Simple Tile Movement Logic Overview for more details on these concepts.
The checkBounds() function accepts in three parameters:
The number to increment the row the player is currently in
The number to increment the column the player is currently in
The object being tested (either the player or one of the enemy tanks)
The sole purpose of this function is to determine whether the object being tested can move in the desired direction. In this game, the only illegal moves are off the side of the screen. In games such as Pac-Man, this would check to make sure that the tile was not a wall tile. Our game does not do this because we want the player and the enemy objects to be able to move mistakenly onto the wall tiles (and be destroyed).
If a valid move is found for the player in the direction pressed, the setPlayerDestination() function is called. This function simply sets the player.destinationX and player.destinationY attributes based on the new tile location.
checkBounds() sets the player.nextRow and player.nextCol attributes. The setPlayerDestination() function multiplies the player.nextRow and the player.nextCol by the tile size (32) to determine the player.destinationX and player.destinationY attributes. These will be used to move the player to its new location.
GAME_STATE_ANIMATE_PLAYER is then set as the current game state.
GAME_STATE_ANIMATE_PLAYER
This function moves the player