Use SingleNodeBlocker to programmatically block target positions to implement multi agent movement

I have a “Free mode” in my Turn Base Game where I want select 3-4 Units and move them to a single LevelGridNode. Currently, I sequential calculate the path, assign it to AILerp and every Unit moves to the same node.

Now I want to create some sort of avoidance. My train of though:

  • Click on the Target Node
  • Calculate the path for the first unit
  • Make the node unwalkable with a programmatically SingleNodeBlocker
  • Calculate the path for the second unit. Now that the original target node is unwalkable, I use GetNearest with walkable constraints to get next one.
  • And so one

Is this a good approach?
If yes, how do I create a block at the target position? Should I instantiate a new SingleNodeBlocker at the target position and add it to my obstacles? This seems cumbersome.
Or maybe I think too complicated

Hi

Sorry for the late reply.
Yes, this seems like a good solution. If you don’t want to use SingleNodeBlocker components you can implement your own ITraversalProvider and use whichever datastructure you desire (e.g. just a simple list of nodes). The interface only requires you to provide a few methods like “is this node walkable”.

See the bottom of this page for more details: Utilities for turn-based games - A* Pathfinding Project

1 Like

Ah, that’s a good idea. I created a simple TraversalProvider that only contains a simple list of blocked nodes. On CanTraverse I just check return !blockedNodes.Contains(node);. This is my implementation to get multiple targetNodes:

    public Dictionary<BaseUnit, GraphNode> GetNodesInRadius(LevelGridNode targetGridNode, List<BaseUnit> selectedUnits)
    {
        // My traversalProvider
        UnitTraversalProvider traversalProvider = new();
        // This will be returned, when done, every Unit will have it's own targetNode
        Dictionary<BaseUnit, GraphNode> nodes = new Dictionary<BaseUnit, GraphNode>();

        // I iterate over every unit to calculate the the actual targetNode
        foreach (BaseUnit unit in selectedUnits)
        {
            // for my first unit, the target is the actual targetGridNode (already checked if walkable)
            // the next iteration MUST NOT use the actual targetGridNode, here I need the nearest walkable
            Path path = FindPath(unit.transform.position, (Vector3)targetGridNode.position, traversalProvider);
            // I get the last node of the path
            GraphNode lastNode = path.path.Last();
            // add the node to my traversalProvider, because for the next unit, this node is blocked
            traversalProvider.AddNode(lastNode);
            // add the node to my return Dictionary
            nodes.Add(unit, lastNode);
        }

        return nodes;
    }

This works only for the first unit, but I don’t know how to get the nearest walkable node, but where can I inject my TraversalProvider? This is my current code to get the nearest walkable node

    public LevelGridNode GetNearestWalkableNode(Vector3 position)
    {
        var constraint = NNConstraint.None;
        constraint.constrainWalkability = true;
        constraint.walkable = true;

        return (LevelGridNode)AstarPath.active.graphs[0].GetNearest(position, constraint).node;
    }

Maybe I can get the surrounding neighboring nodes and loop over them until I found enough walkable nodes for all units