The Flag Defense game is essentially an event-based game, which only needs a very simple game loop. This is because in this turn-based game, each action occurs sequentially and is based on events. So, our game loop essentially is very simple and involves only advancing of animation frames, moving characters, rendering the updated level, and switching few game states.
Consider the following code in the MainLevel
class:
private function loop(e:EnterFrameEvent):void { Starling.juggler.advanceTime(e.passedTime);diceAnim.update(e.passedTime); worldLayer.render(e.passedTime, groundImage); if(gameState == GameStates.GREEN_MOVE && worldLayer.noneWalking()){ clearPath(); switchGameState(GameStates.BLUE_DICE); }else if(gameState == GameStates.BLUE_MOVE && worldLayer.noneWalking(true)){ clearPath(); switchGameState(GameStates.GREEN_DICE); } }
The worldLayer.render
method calls the update
method of all soldiers, moves them if their walking
property is true
, and then draws the game world.
The following are the different game states that we have used in the game and their functionalities:
GREEN_DICE
: This indicates that the dice is thrown by the green player, tap to stop dice rollingGREEN_PATH
: This indicates the path drawing done by the green player by touching and draggingGREEN_PATHCOMPLETE
: This indicates the path completed by the green player, that is, the path now has the same number of moves as the dice valueGREEN_MOVE
: This indicates the automatic movement for the green player on the drawn pathBLUE_DICE
: This indicates an automatic dice throw for the blue playerBLUE_DICESTOP
: This indicates an automatic dice stop for the blue playerBLUE_PATH
: This indicates automatic pathfinding, path drawing for the blue playerBLUE_MOVE
: This indicates the automatic movement of the blue player on the found pathAll game states starting with BLUE
constitute the AI for this game. The GREEN_MOVE
game state can also be considered as an AI as the soldier needs to follow the path automatically.
The MainLevel
class listens to the touch events and makes sure that it responds accordingly based on the current game state. Double-tapping anywhere on the screen will toggle the UI on and off. For the game states GREEN_PATH
and GREEN_PATHCOMPLETE
, we track touches for drawing the path by a user. If we touch a green soldier, we start the path drawing and as we move the touch, the path array for that soldier is updated if the new tile is directly walkable from the previous touched tile. Path drawing stops when we touch a nonwalkable tile, when we release our touch, or when the path length becomes higher than the dice value. Arrows are drawn on the ground layer while the path is updated. It also makes sure that the game state switches to GREEN_PATHCOMPLETE
when we have enough path points.
In the game, we will need the soldiers to follow the path we draw for them as well as the enemy soldiers to find their own path. Let us first see how path following is done.
The soldier class (CustomIsometricCharacter
) is already wired to move in the direction specified by its facing
property if its walking
property is true
. For following a path, we set the walking
property to true
, find the next destination from its path
array, find the new facing
value by comparing the current position and destination. The path
array of a soldier will have tile coordinates in a sequential order. So, once the soldier reaches the current destination, we will update the destination to the next point from the path
array and the process is repeated till there are no more points in the array. Then the walking
property is turned off as we have followed our path to reach the final destination.
The important methods implementing the preceding functionality in the soldier
class are markDestination()
, findDirection()
, and move(delta:Number)
.
For the green soldiers, path finding is direct as the player himself does it by drawing the path. The code just makes sure that the newly marked tile is walkable and is either vertically or horizontally next to the previous valid position saved in the path
array. In our engine, we don't allow the soldiers to walk diagonally between the tiles.
For the blue soldiers, we need to find the path using a path finding algorithm. There are many standard algorithms for the same which can be applied to a 2D array to find a shortest path. Two of the major candidates are
Dijkstra's algorithm and A * algorithm. There are far too many standard implementations of these algorithms in AS3 that you are not advised to reinvent the wheel, but use one which suits the purpose. Essentially, most of all the implementations will need a collision array with 0
value for a walkable tile and 1
for an obstacle. We create the same in the createLevel
method of the MainLevel
class, shown as follows:
for(var i:int=0; i<groundLayer.rows; i++){ worldLayer.collisionArray[i] = new Array(); for(var j:int=0; j<groundLayer.cols; j++){ if(worldLayer.overlayArray[i][j] != "*" || groundLayer.groundArray[i][j] == "tile4.png") { worldLayer.collisionArray[i].push(1); }else{ worldLayer.collisionArray[i].push(0); } } }
The array has a value, 1
when there is an overlay item in that tile position or if the ground has tile4.png
which is the base for our castles. The ground tile is considered as the castle tile occupies multiple tiles, but can be an overlay value in only one of those tile positions.
We are using the Pathfinder
class by Joe Hocking, which is an A * implementation found at http://www.newarteest.com/flash/astar.html with some slight modification to remove the diagonal path finding.
We can call the Pathfinder
class to return an array of points in sequential order from a starting point to an end point by using the following code:
path=PathFinder.go(startPoint.x,startPoint.y,endPoint.x,endPoint.y,collisionArray);
For the blue soldiers, we calculate the path from their paintPoint
position to the green flag position, and splice the resulting array to only include as many points as decided by the dice throw. Once the path is found, the path following part is the same for all the soldiers as explained before.
Simulating AI-based decision making requires a bit of extra effort. We need to purposefully introduce delays in each step for making it realistic and to introduce a human element. Most of the AI decisions can be made instantly which does break the illusion of the human player that we are trying to convey. In our game, each AI decisions are made via methods which are called with a delay using the juggler
class as follows:
Starling.juggler.delayCall(switchGameState,2,[GameStates.BLUE_DICESTOP]);
18.227.111.197