I wrote an article before about avoiding pathfinding if you can determine beforehand that the destination is unreachable. This can be done by labeling your tiles or nodes with same integer value if they are connected. Before querying for a path, you can identify if a destination is reachable by checking if its label is the same as the starting position.

This method has helped our game tremendously. It prevented longer A* query wait time when agents try to go to an unreachable path. However, we still haven’t fixed the UX issue in which the agent indicates that it can’t reach its destination but the player wouldn’t know which tile is it? This issue is always reported to us a bug but upon investigation, the problem is just an unreachable path that is not very clear where it is or why.

The ideal fix is for the agent to still try to reach its unreachable destination then show the indicator that it can’t reach its target. This way, the player can at least deduce why the agents are showing such indicator in only specific tiles.

The programming problem then is how to determine a path in which the last position is the closest to the agent’s destination. The solution is actually very simple that I feel so stupid why I haven’t thought of it before. It can be implemented inside the A* algorithm itself.

Least H

I’m going to assume that you’re familiar with A*. Remember that in A*, we store the H value in each node which is the heuristic cost or distance of such node to the destination. During processing of nodes in the open set, we can store the unblocked node that has the least H value, which means the closest. When there are no more nodes in the open set (which implies that the path is unreachable), we can construct a path using the node with least H as destination instead of ending the algorithm without a path. Note that you can always construct a path from any node in the open set given that you set a parent node to each of them.

Here’s the snippet of such code in our pathfinding framework:

float minH = float.MaxValue; AStarNode<T> minHNode = null; this.openSet.Add(startNode); while (!this.openSet.IsEmpty()) { AStarNode<T> current = this.openSet.GetNodeWithLeastF(); // returns the AStar node with the lowest F value if (IsGoal(current)) { // Path to destination is found ConstructPath(current, this.executionPath); this.path.MarkAsReachable(); return; } this.openSet.RemoveNodeWithLeastF(); ProcessNode(current, this.goal, this.calculator, this.reachability); this.closeSet.Add(current.GraphNode); // We save the node with the least H so we could still try to locate // the nearest position to the destination if (current.H < minH) { minHNode = current; minH = current.H; } } // It's unreachable path.Reachable = false; // We create a path from the node with the least F so that agent can still // have a path towards the destination albeit it's not reachable if (minHNode != null) { ConstructPath(minHNode, path); }

Jump Point Search

The method I described might still take longer to execute in a generic A* algorithm. The generic algorithm would need to process all nodes in the node space after all. Fortunately, we’re using the Jump Point Search (JPS) variant. In JPS, identifying an unreachable node is fairly fast. Not all nodes would be pushed to the open set.

In our code, we added an additional case for a node to be considered as a jump point (aka candidate for open set). It’s a very simple case. Any node that has all the following properties is a jump point:

The node has the same x or y position to the destination node. The adjacent node going to the direction of the destination is a blocker.



For example in the following image, the circled tiles are jump points (say the chair is the destination).

We needed these as jump points because we want nodes in the open set that would be approximately closest to the destination. Without these, the nodes in the open set will be the ones identified by the original JPS algorithm which are not always the closest positions.

The jump point consideration logic we used wouldn’t always identify the closest position but the approximation is good enough.