Keep a copy of node.Area outside Astar

Hello, I have a Node struct that has a walkable, tag and area field inside of it. I have a grid of these nodes on top of Astar’s grid graph nodes in order to be able to use it in bursted jobs for quick checks. Whenever I change the walkable or tag attribute of one of my own nodes, I update astar at the same time using

    void updateAstar(int x, int y, bool obstacle, uint tag) {

        AstarPath.active.AddWorkItem(new Pathfinding.AstarWorkItem(ctx => {

            var gg = AstarPath.active.data.gridGraph;

            var nodee = gg.nodes[y * gg.width + x];

            nodee.Walkable = !obstacle;
            nodee.Tag = tag;
            gg.CalculateConnectionsForCellAndNeighbours(x, y);

        }));

    }

Now I’d like my nodes to also have their ‘area’ field set to correspond to that of Astar’s nodes, so I can query for possible paths between 2 points in burst, just like PathUtilities.IsPathPossible does. However, I can’t figure out where and how node areas are set so I can update my own Node struct from there. There is this in the GraphNode class:

	public uint Area {
		get {
			return AstarPath.active.hierarchicalGraph.GetConnectedComponent(HierarchicalNodeIndex);
		}
	}

But there’s no set. So I looked into the HierarchicalGraph class but that looks like the areas are in their own array, instead of being a field inside the GraphNode class like the field ‘walkable’ is. If I were to say, build an enclosure, where in Astar’s code do I have to go to be able to set all of the nodes inside that enclosure to the new area generated by Astar?

So I think I found where, but there’s just one little issue.

    void updateMyNodesAreas(int i, int currentArea) {
        List<GraphNode> childs = children[i];
        if (childs.Count > 0) {
            AreaInfo areaInfo = new AreaInfo();
            areaInfo.area = (uint)currentArea;
            areaInfo.nodes = childs;
            myGrid.SendMessage("UpdateNodeAreas", areaInfo);
            Debug.Log(currentArea);
        }
    }
	/// <summary>Flood fills the graph of hierarchical nodes and assigns the same area ID to all hierarchical nodes that are in the same connected component</summary>
	void FloodFill () {
        if (myGrid == null)
            myGrid = GameObject.Find("Grid");
		for (int i = 0; i < areas.Length; i++) areas[i] = 0;

		Stack<int> stack = temporaryStack;
		int currentArea = 0;
       // Debug.Log(areas.Length);

		for (int i = 1; i < areas.Length; i++) {
            // Already taken care of

            if (areas[i] != 0) continue;

			currentArea++;
			areas[i] = currentArea;
            updateMyNodesAreas(i, currentArea);


            stack.Push(i);
			while (stack.Count > 0) {
				int node = stack.Pop();
                var conns = connections[node];
				for (int j = conns.Count - 1; j >= 0; j--) {
					var otherNode = conns[j];
					// Note: slightly important that this is != currentArea and not != 0 in case there are some connected, but not stongly connected components in the graph (this will happen in only veeery few types of games)
					if (areas[otherNode] != currentArea) {
                        areas[otherNode] = currentArea;
                        stack.Push(otherNode);
					}
				}
			}
		}

		NumConnectedComponents = System.Math.Max(1, currentArea + 1);
		version++;
	}

I have 3 enclosed areas on my map, so I get the areas 45, 46 and 47, but I also get an extra one, 7. The 7th one doesn’t show up on the astar debugger because it’s gray and blends with the rest of the gizmos, unlike the other 3 which are some shade of pink.

What’s up with that extra area? Why is it marked as disconnected from the rest? Is it safe to ignore?

Hi

I’m not quite sure how you are logging all of this, but the extra one could potentially be unwalkable nodes. The area of unwalkable nodes is generally undefined and is just leftover data.

I think it had something to do with me calling the method at the wrong time. I opted instead to call it on every single area after the floodfill has completed, which gave expected results. I might come back to it later and figure out a way to only call it on affected areas.

I have a new issue now.

So, I want to have doors in the game now, however, I can’t figure out how to best implement this. I’ve taken a look at all the door topics I could find plus the given examples, but they all involve the usage of tags. Using tags will not work for me because I have a system that searches for entities in a grid based on a number of factors, one of them being if the target is reachable. It checks if the target is reachable based on the area of the target and acting entity. If I were to use tags for doors, then a room would have the same area index as the outside world so the AI would think that any target inside the room is reachable even though it’s not and will try to target it (because it favors anything with a priority higher than that of a wall/door). It will then get stuck trying to reach it instead of targeting the door or walls.

I thought about not using tags and simply marking the door as an obstacle when opened and as non obstacle when closed, which would solve the issue above. But that would introduce an issue to friendly units. If I wanted to order a unit to move inside a room, it should be able to find a path through the door but it couldn’t since it’s marked as unwalkable now. One way to solve this would be to mark (all) the door(s) as walkable for one frame then close it the next so that the path request can go through only for friendly units. However this approach would cause way too many graph updates as the number of doors, friendly units and path requests goes up and might even overlap with path requests from enemies which would then think the door is open.

It seems to me like I’d have to change the way I check if a target is reachable or not if I want to use tags. But how can I do that?

Here’s 2 pictures of the first approach I described above (the pink square is a door):

Tag graph (Hard to see, but the door has a different tag than everything else):

Area graph:

Is there any way for the little dude outside to know, fast, if the little dude inside is not reachable with tags before actually trying to compute the path?

Would using 2 graphs without tags make sense? First graph would be used by friendly units and the door on the first graph is always marked as walkable even if the door is closed, though visually animations would still work as expected, while the second graph would be used by enemies and mark door walkable/non walkable depending if it’s open or not. The problem with this is that there’s now 2 graphs to take care of, so twice the computation for graph updates, even if they’re done at an extremely low level. So I’d like to know if there’s a better way :slight_smile:

Hi

Well, there is an overload of the IsPathPossible method that takes a tag mask.
https://arongranberg.com/astar/docs/pathutilities.html#IsPathPossible3

However it is quite slow relatively speaking since it does a search in the graph for if the target can actually be reached. However if your world is not that big then it might be feasible.

Thanks for the reply again! I just looked at the implementation of that method. It looks like it would be even slower than doing twice the graph updates since I’m dealing with thousands of entities. I’ll be going with the 2 graph solution instead.

1 Like