It’s been a while since I’ve post something here and this one might get pretty lengthly. As and old saying goes: if you can’t explain it, then you don’t master it. This post will serve as a starting point to others that might be on the same path I was a few months ago: trying to understand the basic concept of efficient pathfinding.
Pathfinding is a domain in computing science that is widely known to be complex since most of the paper/explanation comes from expert/mathematician/AI veterans. Applied to games, it get more complex as you get in optimizations more often than basic explanations. True, it is a complicated subject: it need to be efficient and also be accurate.
When you try to achieve both efficiency and accuracy, you will certainly need some very heavily-optimized code. So for games, we’ll need to state some compromises. Do we actually need the shortest path or having a path at all will suffice? Can our level/terrain can be simplified into a basic layout? Those two questions are the fundamentals of the compromises: sufficient and simplistic. For games, an AI moving toward an objective will be sufficient enough to be believable. And to obtain good performances and efficient work flow, we’ll need to break down our level into a more simplistic representation.
Pathfinding is a complex process that we can split down into three components: the spacial representation, the goal estimation and the agent. The spacial representation, also known as the graph, is a means to describe a network of inter-connected walkable zones (roads, floors, …). The goal estimation, known as an heuristic, is a general representation to where might be the goal. This is a mere estimation that is needed to speed things up. Finally, the agent is the one responsible to actually searching through the spacial representation based on the goal estimation.
Putting together you get an agent searching for a goal on a spacial representation with some hints to where it might be. Simple enough? But why do we need these three components? Why can’t we just use a spacial representation and iterate through all the zones till we find our goal? Yes you could, but it depend on what you are trying to achieve. What if you got different type of enemies? Say we have a soldier and a tank heading toward the player. A tank cant go in thigh spaces and can not make sharp turn. How would you represent both in a one component? With the three components above, both units are walking on the same terrain, looking for the same goal, the player, but have their own limitations: you got a spacial representation, a goal and two agent. That is why pathfinding is always explained as three distinct components.
There is several ways to represent a graph of walkable zones and I’ll describing the three most used throughout my research.
The first one, the most basic and easiest to understand is the grid. This is the most explained type of graph when it come to pathfinding, go ahead and google A star search algo and you’ll most likely find a evenly spaced grid that cover a square room/space. Lets take a look on a simple “H” shape.
As you can see, a uniform grid has been applied on the shape creating a network of node (pink and black dot). Yet effective and simple, a lot of memory is waste on keeping data for a network that is not within our shape. To overcome this detail we could simply remove black dot from the graph and voilà. Here’s an other catch: from a to b would require a search through all the mid to south node, ±96nodes, to find a path of ±19nodes. The search algorithm is wasting precious time and power to go from 96nodes to 19. You could also add diagonal connection between nodes to reduce a bit the path. An other catch: most of the implementations I saw relies on raycasting from the sky (to detect nodes that are outside of geometry) thus making it useless (or complex workaround) when dealing with environment that have a ceiling. The point is, this graph is inefficient because too much data is representing the same zone (we’ll get into that in a moment).
The waypoints graph is the most used (based on pure observation in games). It does give you an alternative to the grid since more than 4 connections (8 with diagonal) between nodes can be made. Nodes are place around the level with care to cover most of the walk-able areas without creating too much noise for the search algorithm.
This graph can be place along your geometry (doesn’t care if it has a ceiling) and also reduce the nodes count: ±31 mid-south nodes and a path from a to b of ±11 nodes. That is a lot better than the grid-based graph but still has a major issue (like the grid graph). The resulting network from these two point-line-connections is stripping away part of your actual walk-able area. See the image below for a better understanding.
The network contain data only about the connections between nodes, thus stripping away the actual floor of our geometry. Multiple problem emerge from this fact: paths will have angular turns, an agent could never land on the a or b since they are not on the network and the paths can not be reduce with some direct-line-of-sight. Because these type of network only consider the walk-able area to be dots (with a radius) and their connections, the outside of the network can only be unwalk-able area or at least not safe (like ditch, or narrow passage). So cutting a corner or trying to go in strait lines between nodes may be risky. I say may be because it depend on your level and your network. You could place your nodes to take account of the fact that agents will cut corner and maybe your level doesn’t have ditches/holes/obstacles.
For those that are still with me, the solution to the above problem’s lie within our actual geometry, meshes, commonly referred as navigation mesh or simply navmesh. The mesh of your floor geometry is pretty much all you’ll ever need to build your network: connected vertices form triangles, put in our terms, interconnected nodes.
As you can see, by giving each nodes an actual surface instead of point, the network reflect the actual floor. We also reduce the network from mid-south ±31nodes to 10 and a path of ±11 nodes to 8. What now? Well we can reduce a bit more our number of nodes by merge triangle into convex polygon. Why convex? A point and triangle are some kind of convex polygon and we can all affirm that two point that lies within a convex shape can be linked with a strait line? Yes. In a convex polygon, for every point that lies on the edges to an other point on an other edge can be linked with a line that is contained within the polygon. In other word, an agent coming from the left side of a rectangle can cross over the right side without worrying to collide with walls.
Once more we reduced our network significantly, 4 nodes total with a 3 nodes to our goal. As you can see, based on some simple geometry assumption, we were able to build a very efficient network for our “H” shape. I hope now you do understand that grid graph are very useless compared to a navmesh: we saved 3200% (131nodes to 4nodes) of processing power for our search algorithm. As intelligent being, we do this process every time we plot a path in a known surface: we simplify the environment into basic shape to find an path that is satisfying (we aren’t computer that are looking for the most-optimized-path).
Navmesh grid is the most efficient and accurate method to describe spacial areas and that is what we are looking for.
The heuristic is an information that is provided to each node of your graph to direct the search algorithm toward the target(s). Acting like a heat map, your search algorithm will look into this map to prioritize nodes.
Multiple algorithm can be used to generate a heat map. In fact, it all depend on your game design, level design, platform, etc. For example, for level that don’t have overlapping floors, the euclidean distance (distance between two point) will do the job (like above diagram). The heuristic is a game changer in pathfinding, without it, performance would drop since the search would need to look up every node till it find the target. In the other hand, with a heuristic that isn’t adapted to your game, it would result in false estimation and your search algorithm could waste some valuable time in the opposite direction of your target.
If your graph is relatively small (node count) and your target platform possess enough memory, the best heuristic would be as follow: each nodes contains a list of all other nodes in the graph with their corresponding depth. So node #1 would knows that it’s 4 nodes away from #56, and thus providing the best estimation for the search algorithm – moving from lowest depth connections of each nodes would inevitably result in the shortest path.
This is where the search algorithm is performed. With a network and heuristic, a basic A* (pronounced “a star”) algorithm can find the shortest path between two nodes. A* algorithm is really well documented and is pretty simple to implement (under 50 lines of code), so I don’t have anything more to say on that topic.
At this point, the agent found a path of nodes toward the target. As stated in my previous posts of April, Pathfinding 101 and Pathfinding 102-ish, we’ll need to find a way to optimized the path into a list of waypoints. This is where I struggled the most, I tried to find a algorithm that would optimized the path based on my understanding of the Funnel algorithm. And then I found a simple implementation in C++ (first result on google). It does work perfectly and it only require a list of portal to walk through, so it will work with any graph you decide to use.
Tech demo that combine everything discussed in this post.
Implementations in Unity3D
- Aron Granberg Pathfinding v3.0 is looking awesome; grid and navmesh graph + Recast; free and pro licence (not yet released)
- Alex King Simple Path; grid graph only, doe not support height properly; pro licence only (60$)
- Unity3d road map include Recast + Pathfinding + Crowd simulation for it’s v3.5 (fall 2011)
- Your own implementation (to be released)