seeker.StartMultiTargetPath() not returning shortest path

I’ve got a boundary condition where MultiTargetPath has a chosen index that is NOT the shortest path - when calculating using pathsForAll = true, it returns all paths with a shorter length for the path NOT selected than for the path it selected.

Reproduction verified on v5.3.8:

  • Using RichAI on a RecastGraph
  • Using FunnelModifier to simplify the calculated paths
  • Very specific agent & target placements (see screenshot)

It appears to be selecting a path based on the node-to-node path, before applying path modifiers.
(Understandable, because we want to stop calculating once we’ve found the shortest path!)
… but it DOES still apply modifiers to the returned path, giving a very unexpected result where it appears that it can’t measure the path length properly.

See this screenshot for setup positioning:

  • Guy with the white shirt, upper centre is the pathfinding agent. He began near the green cross.
  • Starting near the corner of a large triangle on the navmesh, with a nearby target that’s also near the corner of the large triangle (guy with blue hoodie, lower left corner), and a further away target that is NEARER to the centre of the large triangle than the correct target (woman, lower right corner)
  • The calculated paths, without modifiers, go to the centre of the large triangle (approx. where the second green X is) and then to the target destinations. The correct path has a few extra steps through those long thin triangles, but the remaining path is approx. straight.
  • Calculated path to the wrong target, drawn in blue, is shorter than the correct path because the woman is standing nearer that first waypoint at the node position.
  • Correct path, drawn in yellow, is longer because of the additional zig-zagging.
  • If the correct path is selected the NPC will still walk to that target via a shorter path (even without modifiers) because of turning towards the next waypoint before reaching the current waypoint.
  • If a funnel modifier is added to the seeker, it returns two straight-line paths to each target but selects the longer path.
  • The red line drawn by a Gizmo is my selected path after I used my workaround to manually chose the shorter of the two returned paths. It is already a straight line because of the FunnelModifier.

My current workaround is to return all paths & not trust the selected path. I then check the length of each path & update the returned MultiTargetPath’s chosenTarget, path & vectorPath.

Potential fixes:
-It’s understandable why it doesn’t apply the modifiers, because how can it if we want to stop after finding the first/shortest path - calculating all paths to do this check would lose the benefit of doing it all in one query…but I couldn’t see anything in the documentation mentioning this boundary condition or warning it could happen.

  • Applying filters before selecting the path when pathsForAll is true would be bad if it returns a different path to when only requesting the shortest path. Swapping one inconsistency for another.

When pathsForAll is false, would it be possible to continue searching for paths for some minimum distance or pathlength percentage? e.g. after finding a path of whatever length, continue searching for paths until you’ve checked for anything 1.5x the path length?
Then once you have the paths, whether or not pathsForAll is true or false, apply the filters first and then check for the shortest path.
This should give a result that:

  • Gives consistent results for the best path whether searching for one or multiple paths
  • Limits the amount of searching if there’s one clearly shortest path
  • Selects based on the same path that gets returned to the user (so results will match the agent’s path)

If this isn’t considered a bug, or not worth fixing or otherwise a problem - please put a warning check in the code or update the documentation.

We really need at least a warning because it creates what seems like an intermittent bug.
Because of the specific agent/target placement & the variability of recast graphs nodes (which are invisible in the game view), it’s hard to replicate this in builds.
It causes a lot of frustration for the QA & Dev team because they’d test & think that it was working due to slightly different placement.

Hopefully this helped!

I haven’t categorized this for RecastGraph because I’m not sure if similar conditions can happen on other graph types.

Cheers,
Tony

Hi

I recommend trying out the 5.4 beta version. It has significant improvements for pathfinding on recast/navmesh graphs, which should improve accuracy in cases like this.

Awesome thanks! I’ll give it a try & report back :slight_smile:

Oh snap! It looks like I’ve got A* pro through another organization & don’t have the email with the invoice/serial number.
I can get to it through package manager, but only up to 5.3.8

So… thanks & apologies. I won’t be able to test/verify if this happens in the newer version.

But for me, my workaround is fine for now.
I’ll add a todo comment into my code telling myself to check if it’s still needed when 5.4 hits the asset store and THEN I can report back :slight_smile:

It’s hit the Unity store & I’ve finally had the chance to upgrade to 5.4.4 :slight_smile:

  • Alas, it still seems to be returning the longer path (the shortest path pre-smoothing, but not necessarily the shortest after smoothing is applied.)

(But the update is still great for the other improvements! :folded_hands: )

  • I ran into a new (related) problem in v5.3.8 where the path would be stuck at pathPending.
  • It only appears to happen when calculating a MultiTargetPath
  • I think it’s to do with my workaround, which was to calculate all paths, check for the shortest, and return the index of the best pick. The calling code would then set the ai.destination.
  • I wasn’t setting ai.canSearch = false while calculating the MultiTargetPath.

I’m not sure why that was happening. Perhaps it was getting called a second time and cancelled the ai.destination from the previous path. Or perhaps it returns a result and then does something after a delay that can interfere if you set the new path immediately. Dunno!

At any rate, with v5.4.4 I’ve slightly updated the code to avoid needing the seeker, now we can do it through the ai :slight_smile:
I’m setting ai.canSearch = false, doing the MultiTargetPath.Construct(), setting pathsForAll true/false, doing AstarPath.StartPath(), awaiting the path.WaitForPath(), and then setting the richAi.autoRepath.mode to dynamic (because setting canSearch = true sets it to EveryNSeconds)
… and it seems to work.
I haven’t managed to make it “get stuck” in pathPending, even though it’s still setting the destination immediately after returning that result.

I haven’t yet verified if the bug is fixed & it’s returning the shortest path