At this point in the lecture, we have a setup where you can keep adding enemies at a “start point” and it will either calculate a path or return an already cached path…
For the parameters of the game as designed, that’s probably fine, but…
What if…
- You want multiple spawn points?
- You want enemies to choose different routes because enemies are blocking the current path?
- You want a little more robustness in your game?
There are actually several solutions to the problem of multiple spawn points/paths. The most robust method is to separate the inventorying of world locations (the Dictionary) from the path calculation. For most games, the Waypoint list will actually only change ONCE. So I put the code to fill the dictionary with Waypoints in the Start() routine.
void Start() { LoadBlocks();}
Once that is done, the Enemy can request a path… in my case I arranged the pathfinding so that the enemy could specify any Start and End point for the request… If the enemy wants a path, one is bulit dynamically, and it is returned as a List(), to be traversed just like if we’d filled the list in the inspector. Within the Pathfinding class, that list is then discarded. Only the enemy needs it. The next enemy will get its own path generated.
List<Waypoint> GetPath(Waypoint Start, Waypoint Finish)
{
... find a path
return path;
}
This allows for multiple spawn points. Now I can have big nasty axe wielding trolls come out of one spawn point, and evil necromancers rushing out of another… Since the paths are generated dynamically, no harm, no foul.
It does create an interesting condition, though… it’s possible, in fact, bloody likely, that two different enemies starting in two different locations going to the same endpoint will wind up with paths that converge… Now, this isn’t always a problem, as long as they don’t both try to occupy the same waypoint at the same TIME, it shouldn’t look too odd, but sometimes you get logjams… (Especially, because in my game, those axe wielding trolls are slow little buggers, and the necromancers have magic, so they move faster [tradeoff, they can only take one hit from a tower]). Those necromancers don’t want to get stuck behind the boring trolls, so if they find that there is another enemy in the space they want to travel into, they simply ask the pathfinder for a new path.
That creates yet another “issue”… if the shortest path went where the slowpoke axe wielding troll whose too dumb to get out of the way, recalculating that path would invariably lead to the same result, because the shortest distance between two points does not generally change unless you can Tesser, and who wants to wind up in Camazotz? So I added an extra field to Waypoint… “occupied”… whenever an enemy moves from one waypoint to another, it tells the waypoint that it’s moving to that it’s “occupied”, and tells the waypoint it just left that it’s no longer occupied. So now, when I calculate a new path, I add a check for “occupied”… and it calculates the new path based on the dumb troll still standing around doing nothing and goes around it…
{
...path finding routine loops to checking tile
if(!testWaypoint.occupied) {queue.add(testWaypoint));
}
That creates yet another “issue”… it’s possible for all the tiles immediately surrounding the end point to be “occupied”, in which case the pathfinder will never get a path to the destination because it disregarded “occupied” locations. Now any enemy who asks for a path will just sit there looking dumb… So… the solution for that is…
{
... pathfinding stuff like above
if (!testWaypoint.occupied && distance(start, testWaypoint)<2)
{
queue.enque(testWaypoint);
}
}
Now the enemy will look for a path around the offending slowpoke troll if it is really close, but will ignore the fact that there is a slowpoke troll blocking the finish location (unless, of course, the Necromancer is really close to that smelly troll).
There are, of course, a couple of other issues brought up by my solution… one of which is a potential race condition (if you have an existing enemy on the board when the game starts, it’s entirely possible that the enemy will call your PathFinder.GetPath() function BEFORE the Pathfinder caches the locations of the waypoints… There are two simple solutions to that problem, one of which involves simply not putting any enemies on the board, and making sure that the spawner doesn’t put one in on the first turn, OR… having the enemy check to see if the Dictionary is loaded, and if it’s not, to subscribe to a Delegate function to ask for a path when it is loaded. I’ll not bore you with the details of that implementation.
Pardon my psuedocode above… mainly trying to convey the building blocks of what you would need to do to add these features.