We use a grid graph in our project. The project includes a terraforming mechanic, so after a terraforming input was executed, we are rescanning the parts of the graph that may be affected. All nodes start out with Tag = 1 at the moment, so we were confused to find that after doing said rescan, all tags of the affected nodes of the rescan are reset to 0. This is the code used:
areaToUpdate = new Bounds( Vector3.zero, 10f * Vector3.one );
GraphUpdateObject graphUpdateObject = new() { bounds = areaToUpdate };
AstarPath.active.UpdateGraphs( graphUpdateObject );
I put areaToUpdate bigger than the actual gridGraph to investigate the issue further, but it happens either way.
I have also looked into the method inside the GridGraph class it is using to generate the new nodes, and it seems to just generate new nodes and never transfer the old tags into context.data.nodes, so I don’t understand where this is even supposed to happen usually.
The lines where I would have expected that were in the GridGraph file, 1806-1809.
We are using version 5.0.5.
I don’t get why, because the GraphUpdateObject specifically has modifyTags, and that is set to false, I have doublechecked. Am I misunderstanding something?
Thank you in advance for your help!
Hi
Yes, this is expected. When calling UpdateGraphs, it will recalculate all touched nodes from scratch. This includes resetting all fields to their default values.
You can set graphUpdateObject.resetPenaltyOnPhysics = false;
to prevent this. Despite the name, it also affects tags.
Eventually, I want to switch from a procedural graph modification system, to a declarative one, in which it will be much easier to do these kinds of things, and in a much less roundabout way.
Thank you for your answer, I have modified the code to this:
areaToUpdate = new Bounds( Vector3.zero, 10f * Vector3.one );
GraphUpdateObject graphUpdateObject = new() { bounds = areaToUpdate };
graphUpdateObject.resetPenaltyOnPhysics = false;
AstarPath.active.UpdateGraphs( graphUpdateObject );
The result unfortunately is the same, all affected nodes still have their tags set back to 0.
Do you have an idea why this isn’t working or what I am doing wrong? @aron_granberg
That’s strange. I just tested it in my project, and it works just fine.
Are you sure you aren’t doing any other graph updates too?
I have triplechecked, it is only updating this one area at that time.
I would like to know where in the code it is supposed to copy the original tags into the new tags for the updated nodes.
I have found this line in the GridGraph script (line 1865)
newNodes.CopyFrom(context.data.nodes, graphUpdateObject != null ? graphUpdateObject.resetPenaltyOnPhysics : true, dependencyTracker);
which I think is maybe the one, but the curious thing is that I can’t find where context.data.nodes is supposed to get the original tags from. Maybe you can enlighten me about that. As far as I can see it, context.data is just newly created and nowhere are the tags ever transmitted from the GridGraph variable “graph”? Also unexpected for me: The graph variable, when looking at graph.nodes, has all the correct old tags as I would want them, but graph.nodeData doesn’t, they are all 0? Is that related to the issue?
For your information, I have fixed my issue in the GridGraph script to produce the intended behaviour as far as I can tell with the following settings on the GraphUpdateObject:
graphUpdateObject.resetPenaltyOnPhysics = false;
graphUpdateObject.updatePhysics = true;
This is what I needed to do (lines 1852 - 1871, added CHANGED: comments to where I changed something):
if (recalculationMode == RecalculationMode.RecalculateMinimal) {
//CHANGED: Needed to comment this back in
context.data.nodes = context.data.nodes.ReadFromNodesAndCopy(graph.nodes, new Slice3D(nodeArrayBounds, readBounds), nodesDependsOn, graph.nodeData.normals, graphUpdateObject != null ? graphUpdateObject.resetPenaltyOnPhysics : true, dependencyTracker);
var newNodes = new GridGraphNodeData {
bounds = readBounds,
numNodes = readBounds.volume,
layeredDataLayout = layeredDataLayout,
allocationMethod = allocationMethod,
};
newNodes.AllocateBuffers(dependencyTracker);
// If our layer count is increased, then some nodes may end up with uninitialized normals if we didn't do this memset
newNodes.normals.MemSet(float4.zero).Schedule(dependencyTracker);
newNodes.walkable.MemSet(false).Schedule(dependencyTracker);
newNodes.walkableWithErosion.MemSet(false).Schedule(dependencyTracker);
newNodes.CopyFrom(graph.nodeData, true, dependencyTracker);
//CHANGED: Needed to toggle the resetPenaltyOnPhysics bool here
newNodes.CopyFrom(context.data.nodes, graphUpdateObject != null ? !graphUpdateObject.resetPenaltyOnPhysics : false, dependencyTracker);
context.data.nodes = newNodes;
}
Maybe this helps, I would really like to know why this works, and also why it works out of the box for you but not for me.
Hey, we are also using grid graph in our game and we are also having troubles with the tags sometimes being reset even with:
graphUpdateObject.resetPenaltyOnPhysics = false;
We expirience two different behaviors in our game. One is after creation of new game and one after load of existing game. After creation of the new game everything works as expected, but after load of existing game all tags of walkable nodes are reset while the unwalkable nodes keep their tags.
I think the main difference between these is that during creation of new game we make all nodes unwalkable in Awake and then make some of them walkable in first call of LateUpdate. While during the load of the existing game we make only some nodes unwalkable in Awake and the rest are kept walkable, if we call GraphUpdateObject with resetPenaltyOnPhysics = false, the nodes that were made unwalkable update to a walkable state and keep their tags, while the walkable nodes stay walkable but reset their tags. The tags for nodes are in both cases set in Awake.
Update 1:
It isn’t unwalkable nodes that keep their tags. It is only nodes on the edges of GraphUpdateObject.
Update 2:
For now, I fixed this problem by setting all nodes to unwalkable even after loading, and then instantly setting the nodes we need back to walkable. After this, GraphUpdateObject works as expected.
If you are editing tags directly, make sure to recalculate the connections in the area. Otherwise, the GraphUpdateObject will reset the tags even with resetPenaltyOnPhysics = false;
.
@TFConny Are you modifying the graph manually in any way using code? I.e. not using a GraphUpdateObject?
I am modifying the tags by doing node.Tag = x, because I can not do that through a GraphUpdateObject as far as I can see without making one for each node (I am setting the tag depending on the y-value of the deepest point on the node, as I am tagging those underneath my water level as water and those above as something else). Is RecalculateAllConnections the method I need to use after updating the tags in that case?
@aron_granberg Is RecalculateAllConnections the method I need to use after updating the tags in that case?