Multiple Paths

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.

3 Likes

love how you are tackiling implementing your advanced features. You mention including “occupied” states on finding shortest paths but the occupied states are time dependendant, so a position could be occupied but not by the time your necromancer reaches it. So you would have to “predict” the occupied state on calculating shortest path by determining all the other enemy paths. But then another issue will arise that a new enemy might be spawned that will occupy that position that cannot be predicted. Unless you spawn enemies to avoid such collisions…you have a great chain of events to tackle :slight_smile:

How about embrace the collisions by having Necromancers impatiently knocking off the trolls if they get in their way, maybe for comical effect. Of course that robs you of the challenge with avoiding collisions.

I’m not too worried about predicting future paths of other entities… If an entity finds oneself blocked, it’s not really that much cost to generate a new path on the fly. (I actually made a version where the enemies choose a new path every turn, and there wasn’t that much of a hit on performance). Predicting all paths of all enemies at once would indeed incur a performance hit (or require a lot more caching of path data).

I am thinking of implementing “teams”, though… perhaps the enemy could have a base to defend as well, and you need to have soldiers head out to attack… if entities from opposing teams meet up, they should attack each other.

2 Likes

Privacy & Terms