Regression: AIPath followed way too late with "stop when destination is crowded"

Often our units (which use AIPath on a GridGraph) compute a path but wait around 5 seconds until they follow the path, while other units already started moving.

I can reproduce this issue on an empty map with 5 units:

First two of the units are ordered to move to one location, and they moved there:

After that all 5 units are ordered to move to a new location.
Result is that the two units who had moved earlier are waiting for around 5 seconds,
but the other units move immediately.

These 5 seconds can be a lot of time if the units are battling an enemy, or trying to run away.

I don’t think I have seen this bug a year ago, so it might be a regression.

There was a release that claims to have fixed such a bug, perhaps it is related.

4.3.47 (2021-09-04)

Fixed some edge cases which could cause an ai with the ‘stop when destination is crowded’ to not start moving until after a few seconds after it got a new destination.

I can confirm that disabling “stop when destination is crowded” makes the bug go away.

We are using A* Pathfinding Project Pro 4.3.48.

Whoa! This might be the problem I’ve been up against! I’ll look for this setting and try messing with it.

I tested backwards going up to 4.3.41 and that one had the bug too.
Perhaps it wasn’t a regression but it might have been there all along.

The Bug can also be reproduced in the Example scene 16 RVO 2D.
One or two of the blobs are moving immediately, the rest is not moving until seconds later.

This bug is severe, everyone who played our new demo reported that so far.

Yeah, this is an issue with the current implementation.
To improve responsiveness you can call

(ai as AIBase).rvoDensityBehavior.ClearDestinationReached();

when you give the unit an order. That will make it follow immediately.
Optionally, you can also call ai.SearchPath to make it recalculate its path immediately as well.

Thanks, this works like a charm!

I saw this piece of code appear in Example 18 only, the function ClearDestinationReached is called nowhere else (in particular the mentioned Example 16).

I guess this isn’t automatically called in the pathfinding library because the code can’t distinguish the situation when an movement order was issued compared to every other frame where the target position is followed.

If so, one could try to document this function a bit better, for example recommend the use in the AIDestinationSetter script and in the documentation in Movement scripts - A* Pathfinding Project

Perhaps one could also add a function to AIDestinationSetter that would change the target and then do these two commands. This way it might be more exposed to the user of the API and it would also avoid duplication of these commands.

Thanks a lot for the fix!

The SearchPath call sometimes triggers this LogWarn

Path Failed : Computation Time 0.04 ms Searched Nodes 26
Error: Canceled by script (Seeker.CancelCurrentPathRequest)
Path Number 1126 (unique id)
UnityEngine.Debug:LogWarning (object)
AstarPath:LogPathResults (Pathfinding.Path) (at Packages/com.arongranberg.astar@4.3.48/Core/AstarPath.cs:838)
AstarPath:<InitializePathProcessor>b__122_1 (Pathfinding.Path) (at Packages/com.arongranberg.astar@4.3.48/Core/AstarPath.cs:1306)
Pathfinding.PathProcessor:CalculatePathsThreaded (Pathfinding.PathHandler) (at Packages/com.arongranberg.astar@4.3.48/Core/Misc/PathProcessor.cs:512)
Pathfinding.PathProcessor/<>c__DisplayClass26_0:<.ctor>b__0 () (at Packages/com.arongranberg.astar@4.3.48/Core/Misc/PathProcessor.cs:147)
System.Threading.ThreadHelper:ThreadStart ()

Isn’t this a situation that the code should handle, rather than an error?

We had to patch it out with a workaround:

diff --git a/Packages/com.arongranberg.astar@4.3.48/Core/AstarPath.cs b/Packages/com.arongranberg.astar@4.3.48/Core/AstarPath.cs
index eb3ab94bb..accfb004f 100644
--- a/Packages/com.arongranberg.astar@4.3.48/Core/AstarPath.cs
+++ b/Packages/com.arongranberg.astar@4.3.48/Core/AstarPath.cs
@@ -835,7 +835,9 @@ public class AstarPath : VersionedMonoBehaviour {
                        if (logPathResults == PathLog.InGame) {
                                inGameDebugPath = debug;
                        } else if (path.error) {
-                               Debug.LogWarning(debug);
+                               /// SearchPath calls trigger this error that isn't an error
+                               if (!debug.Contains("Canceled by script"))
+                                   Debug.LogWarning(debug);
                        } else {
                                Debug.Log(debug);
                        }

A cleaner solution would be to use a new PathCompleteState enum value for this case which does not indicate an error (if cancelling paths via scripts is really never an error).

I think it’s good that users know about this happening. A very common error is that users make the seeker recalculate its path way too often, before checking if the previous path request was even finished. Doing that sometimes when you have just changed the destination is fine, but doing it too often is kinda wasteful because you are aborting a lot of half-finished path requests.

You can disable path logging altogether if you want using A* Inspector → Settings → Path Log Mode.