In the previous lesson we learnt about the A-Star algorithm and produced a test program. In this lesson we will apply it to the game to allow every enemy to track the player when in range. There will be no movement in this lesson, just the application of the A-Star algorithm.


If you are manually creating the project make file then use the one from lesson 8 and add the file game_astar.cpp.
The following files will be changed in this lesson:


The idea for the enemy car movement is that they will remain where they are until the player comes in range (about 40 tiles - 3 screens worth). When in range an enemy will start using the path finding algorithm to track and follow the user.
Because the player is moving most of the time, we need to update each enemy's path, otherwise it will never find the player. Obviously doing so every frame is overkill and will slow down the game too much, so the Animation's custom countdown timer will be used to redo the path every few seconds. Having this at a random amount may give it a human touch as it will appear that the computer is not that good if the delay is large, and if there are multiple enemies on screen at once, they will drive differently.
The code to be written will be similar to that which we used in the previous lesson for the path finding, so hopefully will make perfect sense :)
To see what it is that this code will do, compile and run the program. Remember that on the national map the enemies are in blue. Finding two or more close together will show how they can all generate their own paths. Below is a screenshot of the path-finding in action. found

Code Walkthrough of game_Vars.cpp/game_transam.h

In the previous lesson we discussed that we need a simple integer based (1-dimensional) array to represent open and blocked tiles (we aren't using weighted tiles, e.g. we could add extra weight to the water, grass, mud, etc). In game_Vars.cpp we create our global reference to this as int* AstarMap
In the game_transam.h file we add the game_astar.h header file and a function, RegenerateAstarMap. This function populates our A-Star array, AstarMap, from our TranzAm Mappy map. We will be calling this function (i.e. regenerating the 1-dimensional array) whenever the map is loaded and whenever it changes. It only changes when the player dies (when the player dies a gravestone is added to the game that acts as a collision item - which was done a few lessons previously).

game_car.cpp/h Code Walkthrough)

The A-Star algorithm is wrapped up in the class AStarClass. We will be giving each enemy their own AStarClass to track the player. It will be initialised within the constructor and destroyed in the destructor. We will also be storing a flag to show that we have a path for the enemy car.
The updates to the enemy class (within the game_car.h header file) are as follows:
bool NextMove(Car* player); EnemyCar() { Route=NULL; } ~EnemyCar() { if(Route) delete Route; } bool RouteGenerated; AstarClass* Route;

As you can see, Route is the name we will give the enemy's AStarClass. The method NextMove has been updated from the previous lesson, by passing in the Player's details. This is so the enemies can find out where the player is.

Code Walkthrough of game_car.cpp

Firstly a minor update. The Mappy block variable 'user6' is used to determine if a tile makes the player bounce back. The original idea was to set the damage amount in the data, hence using the integer user6. However, all damage is to be the same, regardless of the tile type, so this is changed to use the bit field 'tl' (top-left tick box) instead.
In the SetupCar method for the Enemy, we create our AStarClass object:
if(Route) delete Route; Route=new AstarClass(AstarMap,mapwidth,mapheight,mapblockwidth);
This initialises the AStarClass using the global map array we mentioned earlier.
In the EnemyCar method 'NextMove' (what is called every frame and updates the cars, collision detection, etc), it's position is checked against the Player. If it is within about 45 squares (no need to be exact, so no need for time consuming trig) then the A-Star system is invoked.
If the A-Star route has not been generated for the car a new path is created immediately and a timer is set for one second (this uses the animation object customer countdown timer).
If the A-Star route has been generated and the countdown timer has elapsed (i.e. one second has passed) then the path is regenerated and the timer reset back to one second.
This can be seen within the EnemyCar::NextMove method by the statements
this->car->SetCustomCountDown(1,1000); Route->NewPath(mapx-(int)mapx%mapblockwidth,mapy-(int)mapy%mapblockwidth,player->mapx-(int)player->mapx%mapblockwidth,player->mapy-(int)player->mapy%mapblockwidth);
Which should, hopefully, be familiar. NewPath is the A-Star method for generating a path from A to B (in our case from the enemy to the player), and SetCustomCountDown creates a countdown timer for the animation object.
If the A-Star route has been generated and the timer has not finished we call a method that we haven't seen yet: ManualTimerUpdate()
Basically, in our Player's car class we call NextMove() on the Car object which updates all the collisions and animations, etc. But for our enemy cars we don't really need this overhead. So because we aren't calling the animation NextMove method we need a method of updating the timers. Which is what ManualTimerUpdate does.

game_game.cpp Code Walkthrough

Whenever a new level is initialised (new game, lost life) we regenerate our AStar version of the Mappy map by calling RegenerateAstarMap(). As with our previous lesson, this function loops through our game's array and creates a one-dimensional array of either 1's or 0's. An array element is set to 1 when a collidable tile is found and to 0 when it isn't.
Ultimately the Enemies will give chase when the player is in range. For this lesson we will simply draw the found path to the player using a series of rectangles for each of the nodes in the path. This is found as follows:
if(Enemies[i]->RouteGenerated && abs(Enemies[i]->mapx-Player->mapx)<mapblockwidth*30 && abs(Enemies[i]->mapy-Player->mapy)<mapblockwidth*30)
This means the following code will be called if a route has been generated and the player is within about 30 square tiles (again, avoiding using pythag. as it isn't needed, just an approximation).
As with our A-Star example, we loop through all the nodes (using NextNode) until the end has been reached (ReachedGoal). For each node we get the x and y and translate it to the screen, taking into account the offsets. We also draw a rectangle where the node is.


Again, not much Allegro apart from drawing a rectangle. Most of the work was done by the A-Star and Animation classes. All that was done was we added an AStarClass object to each enemy, and whenever the player is within range, we updated the path and generated a one second (approximate) delay.


The next lesson we will remove the temporary drawing code for the AStar and move the enemy car. Which should, hopefully, be the last lesson (bar closing comments).