Strange problem when updating graph physics in code

Hello,

I’m trying to get the same “Update Physics” functionality as the GraphUpdateScene component, but without the extra stuff the component does such as updating surrounding nodes or resetting connections. I’m using a layered grid graph.

I have a script here that does update the physics (or at least seems to), but gives me a strange result that I can’t seem to fix. First, here’s a shortened version of the code I’m using:

private void Start()
    {
        AstarPath.active.AddWorkItem( new AstarWorkItem( ctx =>
        {
            LayerGridGraph graph = AstarPath.active.data.layerGridGraph;

            var nnInternalInfo = graph.GetNearest( transform.position );
            if( nnInternalInfo.node == null )
                return;

            var foundNode = nnInternalInfo.node;

            if( Vector3.Distance( transform.position, (Vector3)foundNode.position ) > .1f )
                return;

            LevelGridNode node = (LevelGridNode)foundNode;

            graph.collision.Initialize( graph.transform, graph.nodeSize );

            graph.RecalculateCell( node.XCoordinateInGrid, node.ZCoordinateInGrid, false, false );

        } ) );
    }

The code grabs the node at the game object’s position, makes sure the graph’s collision is initialized, and then recalculates the cell. I’ve attached this code’s component to a simple 1 unit cube with a box collider. Here is where it gets weird. The following image shows where it’s placing the new layer/node:

It’s placing the new layer at exactly 0.25 units above the lower layer (rather than on top of the collider, or 1 unit above the bottom layer, as I would expect). If I move the box collider up, the new layer created by my code also moves up… If I move the box collider’s center to y = 3.5 (the top of the box collider then being at 4), my code finally places the new layer in the graph at 1 unit… However, this is obviously not what I want.

Any idea what could be going on? This has been driving me crazy all day.

Thank you!
Alex

Hi

I believe this issue is caused precisely by not recalculating the connections.
Or rather, this is not actually an issue at all.

Your node on top of the box is still connected to all 8 surrounding nodes since you did not recalculate the connections of the node. When the graph is visualized it has to show a continuous surface and thus the vertices of the mesh (which are right at the midpoints between nodes) has to be some kind of average between them. In the corner of that box you will have a vertex which is right in between one node at y=1, and three nodes at y=0, thus the average is y=0.25. Typically this works out well and does not look weird at all because a connection between nodes typically implies that they are part of the same surface while in your case it seems to be a discontinuous step upwards (and it seems like it should be disconnected? but maybe I don’t know the specifics of your game).
There does still seem to be some weird things happening because the visualized grid graph in your screenshot is still not continuous, so there might be something else going on. But I think it might be just how the average is taken. There are some cases where it is geometrically impossible to preserve the continuity of the navmesh visualization (esp. with layered grid graphs) so some compromises had to be made when taking the averages.

Long story short: I think the node is actually correctly positioned, it just doesn’t look that way. Try to enable the ‘Connection’ visualization mode and see where the connections are coming from on the top node.
To me it seems like your really should recalculate those connections and the adjacent nodes, I’m not sure why you want to avoid that?

Your code looks good though (except the recalculation). Though I don’t think you need that call to initialize the collision class.

Thanks for the quick response, Aron!

The graph is a layered grid graph, so only 4 connections to surrounding nodes and I do in fact want it to be discontinuous. 2 separate layers until I use NodeLink2 components to link them together.

And good news, I think you helped me get it working now! Thanks!

I amended the code a bit, here’s the edited/shortened version that seems to be producing the correct layer on top of the cube, now with no connections to surrounding nodes. It’s a long story as to why I don’t want to edit existing connections (in short, I edit them elsewhere and I really don’t want the data stepped on if I can avoid it), but the new code only seems to edit the new layer/new node’s connections which is good.

private void Start()
{
    AstarPath.active.AddWorkItem( new AstarWorkItem( ctx =>
    {
        LayerGridGraph graph = AstarPath.active.data.layerGridGraph;

        var nnInternalInfo = graph.GetNearest( transform.position );
        if( nnInternalInfo.node == null )
            return;

        var foundNode = nnInternalInfo.node;

        if( Vector3.Distance( transform.position, (Vector3)foundNode.position ) > .1f )
            return;

        LevelGridNode node = (LevelGridNode)foundNode;

        graph.RecalculateCell( node.XCoordinateInGrid, node.ZCoordinateInGrid, false, false );

        LevelGridNode nextLayerNode = (LevelGridNode)graph.GetNode( node.XCoordinateInGrid, node.ZCoordinateInGrid, node.LayerCoordinateInGrid + 1 );
        graph.CalculateConnections( nextLayerNode );

    } ) );
}

Thanks again, Aron! I really appreciate it.
Alex

Hi

Great that you got it working.
However I would strongly advice you to calculate all relevant connections. Now you will have recalculated the connections for the node on top of the cube, so it will have no connections to the other nodes. However you did not recalculate the connections for the other nodes, so they will still think they are connected to the node on top of the cube. These will be one-way connections which are usually best to avoid and I don’t think you wanted them in this case.

You can use GridGraph.CalculateConnectionsForCellAndNeighbours to recalculate the connections for both that node and its neighbors.

Hmm, I didn’t see the issue you mentioned (the old nodes thinking they are connected to the new node, creating one way connections), but using CalculateConnectionsForCellAndNeighbors did solve the connection issue when placing multiple cubes with my code next to each other. Now the new nodes all connect to one another, so thank you! This may step on old, purposely removed connections but I’ll solve that later :slight_smile:

In testing this however, I have found a new and even weirder problem which exists in the same way with both my code and the GraphUpdateScene component. When placing an agent (orange cube) on top of the cube with my code or a GraphUpdateScene component (effectively doing a similar physics update), the agent cannot walk off the cube and reach the target (white sphere) in both the -x or -z axis (which is the intended outcome), shown here:

However, if the agent moves in the +x or +z direction, the agent will reach the edge, hang for a short time (several frames it appears), and then instantly snap to the ground directly below itself on the ground and move towards the target, shown here:

I’ve been playing around with settings of all kinds but I have no idea what’s causing this. It’s also a little scary considering the same problem exists with the GraphUpdateScene (I just hadn’t noticed this before).

Hi

If you want the agent to be able to walk off the cube, then it should have connections to the adjacent nodes. A connection means that it should be able to walk between the nodes.

What happens is that the agent reaches the edge, and then at some point due to floating point errors it happens to be slightly outside the edge of the cube. Then the physics part will make the agent fall down and suddenly it will be closer to those nodes on the ground. Then it will be able to find a path and will continue to move to the target. One can solve this in many ways, the easiest is to use a CharacterController as that allows the agent to stand right on the edge of the cube without falling off (since the character controller has a radius).

Right, in this case I do not want connections however. I’ll be manually adding them in later.

I must have been tired last night, I was trying everything except changing the agent itself… Your CharacterController suggestion made me change the AILerp component on the agent to the AIPath component (which has a radius), problem solved! This means some rewrites, but at least it’s working and I know what the problem is.

Thanks again Aron for your help! You’re a life saver, much appreciated.
Alex

1 Like