Stop path execution at some time, but loosely

Hello, at first - awesome project, thank you!

I would like to get advice on how to proceed with the following scenario (I thought it easy at first, but I am unable to fix it for days).
I’ve been struggling with a path distance limitation. I use hex node graph and turn-based approach, the units can cross nodes until they have no stamina, and the logic is implemented outside pathfinding (e.g. each node has associated script that handles the logic because many things were an obstacle using nodes for this, for example, BFS is cut on non-walkable nodes).

The problem is, I can’t get the unit to stop if the path is being too long to complete with the stamina available. I have a variable that knows whether next movement is possible (if stamina <= 0) kill, but can’t kill the path properly. I also modified the AIPath script to provide me a callback on each new node crossed so I can perform custom functionality on each path node (it is not called once the unit is exactly in the middle of the new node – I could not find out how to do that – but rather once a new node is closer than the previous node).

GraphNode lastVisited = null;
private void OnNewNodeEnteredInternal(Vector3 currentSimulatedPosition) {
        NNInfo nn = AstarPath.active.GetNearest(currentSimulatedPosition, NNConstraint.Default);
        if (nn.node != null && lastVisited != nn.node) {
                    lastVisited = nn.node;
                    OnNewNodeEntered(nn.node);
       }
}

Here’s what I’ve tried:
Custom ITraversalProvider implementation:

    public new bool CanTraverse(Path path, GraphNode node)
    {
        MapTile tile = Map.FromNode(node);
        //stamina variable was defined here and read through property alias in the unit
        return tile.IsWalkable() && stamina > 0 && base.CanTraverse(path, node);
    }

But nothing has changed.
I also tried to kill the path manually, using the custom new node callback:

if (stamina <= 0)
{
       currentPath = null; //currently traversed path
       path.isStopped = true; //associated AIPath script
       path.OnPathFinished((Vector3)newNode.position);
}
else
{
        stamina -= Map.FromNode(node).NodePrice;                  
}

but this approach kills the path earlier, that is the unit is not anymore on the node but stopped before reaching the node. I would like the unit between nodes C and D with stamina = 0 on a path from A to G to continue to the node D and stop once there, not on the halfway.

Thank you for any suggestions!

Ok, now I’ve noticed even the tile.IsWalkable() method is not called, so probably wrong provider usage?

         Path p = ABPath.Construct(from, dest);
         p.traversalProvider = traversalProvider;
         p.nnConstraint = new PathConstraint(p, traversalProvider);

I don’t see why the provider does not work…

PS: I also forgot to mention I extend BlockManager.TraversalProvider so that single node blocking feature still works…

PPS: So now I found out that overriding a method using new does not force the base class reference type to call it. Strange, I thought that unline C++ it is not necessary to make a method virtual in order to force-lookup specific implementations in children. So now the provider works – just copied out the provider and implementaing the provider interface.

Hi

Since you have a turn based game and you have a path cost limitation I would suggest that you modify the complete path instead.

You can do something like this:

using System.Linq;

void Search() {
     // Disable the agent's own path calculation logic
     ai.canSearch = false;
     ABPath path = ABPath.Construct(ai.position, ai.destination);
     AstarPath.SearchPath(path);
     // To make this example simpler, force the path to be calculated instantly
     path.BlockUntilCalculated();
     int nodeCount = 0;
     float remainingStamina = maxStamina;
     while(nodeCount < path.path.Count && remainingStamina > 0) {
           remainingStamina -= GetStaminaForNode(path.path[nodeCount]);
           nodeCount++;
     }
     var reducedPath = ABPath.FakePath(path.vectorPath.Take(maxIndex).ToList(), path.path.Take(maxIndex).ToList());
     ai.SetPath(reducedPath);
}

ITraversalProvider code is called for each node that the search traverses when the path is being calculated. It may search a lot more nodes than just the ones in the final path.

Using ‘new’ explicitly disables the overriding behaviour. Use ‘override’ when you want to override something. What you are saying is true of Java though, but not C#.

Oh. I completely missed the fake path feature. I was doing something similar but just pre-computed the path and then constructed new upon vertices created. So instead of stopping the path by force, I can re-create the path by parts. Cool. I couldn’t find this in any tutorials, though this is pretty crucial turn-based feature. I use many callback predicates for unit to determine whether the path should be stopped or not; so this FakePath comes very handy.

Using ‘new’ explicitly disables the overriding behaviour. Use ‘override’ when you want to override something. What you are saying is true of Java though, but not C#.

Thanks. I know java and c++, so I know both styles, just started with c#. Sometimes a bit confused :slight_smile: