As they said in the original Jurassic Park; hold on to your butts!
Except with less smoking, because eww.
The world generates point nodes based on distance from the tilemap beneath or above it. It is non-ordered, just creating them all willy nilly and connecting and costing based on distance. If there is a way for me to get you the array ID of the specific node causing issue, let me know.
My setup from that ends up giving me this;
NOTE: The game is not running, it is scaled time 0, thats why Goblinna over there isnt moving around. Shouldn’t matter though, because frames still framing.
Now, I create the rope, which then creates, connects, and costs the connections between nodes. We have placement 1;
And placement 2;
You can see that the single rope is creating a node on itself and a node above it.
Now, if I delete the rope from placement 1, it disconnects and turns back to original picture, without needing the disconnection. The kicker code is as follows and is the same for both executions. Its not really important how we got here, just that it will possibly delete 1 node, possibly both nodes, possibly neither. In the scenario above, it qualifies as deleting BOTH the above node and below node. Without the disconnection checker, it has no problem with placement 1. It WILL fail on placement 2.
public void NodeFlareRopeUnBuild(Vector3 _vec3, bool _ropeAbove, bool _ropeAboveAbove, bool _ropedownbuilt, bool _tiledown1, bool _tiledown2)
{
_vec3.x = _vec3.x + 0.05f; _vec3.y = _vec3.y + 0.05f; GraphNode nodeMe = AstarPath.active.data.pointGraph.GetNearest(_vec3, null, 0.4f).node;
Vector3 _vUp = new Vector3(_vec3.x, _vec3.y + 1.0f, _vec3.z); GraphNode nodeUp = AstarPath.active.data.pointGraph.GetNearest(_vUp, null, 0.4f).node;
Vector3 _vUpUp = new Vector3(_vec3.x, _vec3.y + 2.0f, _vec3.z); GraphNode nopeUpUp = AstarPath.active.data.pointGraph.GetNearest(_vUpUp, null, 0.4f).node;
if (!_ropeAboveAbove && !_ropeAbove) { if (_vUpUp != null) DeleteNode(_vUpUp, false, true); }
if (!_ropeAbove && !_tiledown1) { if (_vUp != null) DeleteNode(_vUp, false, true); } bool _deleted = false;
if (_tiledown1 && !_ropedownbuilt) { if (nodeMe != null) DeleteNode(_vec3, false, true); _deleted = true; }
if (!_tiledown2 && !_ropedownbuilt && !_deleted) { if (nodeMe != null) DeleteNode(_vec3, false, true); }
FlushIt();
Then it proceeds to move on to grabbing nodes in the area and costing them - after flushing the deleted nodes (and therefore severing connections). The error occurs before it gets to the flush.
What you can see from my code is that the deletions happen sequentially - add deletion work, either flush or dont, add deletion work, either flush or dont, then after all deletions, guaranteed flush.
The code with checker - shifting is just to check if the node being found is on the tilemap axis or the rope axis, so shifting to the 0.5 instead of int 0.
public void DeleteNode(Vector3 vec3toDelete, bool _flushNow, bool _alreadyShifted)
{
if (!_alreadyShifted) vec3toDelete = new Vector3(vec3toDelete.x + 0.55f, vec3toDelete.y + 0.55f);
GraphNode node1 = AstarPath.active.data.pointGraph.GetNearest(vec3toDelete, null, 0.4f).node;
PointNode _pointNode = node1 as PointNode;
var connections = new List<GraphNode>();
if (node1 != null) node1.GetConnections(connections.Add);
foreach (var connection in connections)
{
PointNode n1 = node1 as PointNode; PointNode n2 = connection as PointNode;
GraphNode g1 = node1 as GraphNode; GraphNode g2 = connection as GraphNode;
GraphNode.Disconnect(g1, g2);
}
if (node1 != null) { AstarPath.active.AddWorkItem(() => { _pointGraph.RemoveNode(_pointNode); }); }
if (_flushNow) FlushIt();
}
Still with me? Ok cool! If you DO NOT HAVE THE DISCONNECTION, placement 1 disconnects perfectly, both nodes are deleted, everyone is happy. Regardless of if I flush after each, or flush only on finale.
If you dont have it and placement 2 occurs;
nvalidOperationException: A node in a PointGraph contained a connection to a destroyed PointNode.
Pathfinding.HierarchicalGraph+JobRecalculateComponents+<>c.<FindHierarchicalNodeChildren>b__12_0 (Pathfinding.GraphNode neighbour, Pathfinding.HierarchicalGraph+JobRecalculateComponents+Context& context) (at ./Packages/com.arongranberg.astar/Core/Pathfinding/HierarchicalGraph.cs:413)
Pathfinding.PointNode.GetConnections[T] (Pathfinding.GraphNode+GetConnectionsWithData`1[T] action, T& data, System.Int32 connectionFilter) (at ./Packages/com.arongranberg.astar/Graphs/Nodes/PointNode.cs:83)
Pathfinding.HierarchicalGraph+JobRecalculateComponents.FindHierarchicalNodeChildren (Pathfinding.HierarchicalGraph hGraph, System.Int32 hierarchicalNode, Pathfinding.GraphNode startNode) (at ./Packages/com.arongranberg.astar/Core/Pathfinding/HierarchicalGraph.cs:415)
Pathfinding.HierarchicalGraph+JobRecalculateComponents.Execute () (at ./Packages/com.arongranberg.astar/Core/Pathfinding/HierarchicalGraph.cs:510)
Unity.Jobs.IJobExtensions+JobStruct`1[T].Execute (T& data, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at <e509afeff7384f24a8f0ac30527ff01c>:0)
This is the part of the delete node that allows it to delete without the cute little arm of failure for placement 2, but is bafflingly unnecessary for placement 1.
var connections = new List<GraphNode>();
if (node1 != null) node1.GetConnections(connections.Add);
foreach (var connection in connections)
{
PointNode n1 = node1 as PointNode; PointNode n2 = connection as PointNode;
GraphNode g1 = node1 as GraphNode; GraphNode g2 = connection as GraphNode;
GraphNode.Disconnect(g1, g2);
}