Strange behavior with UpdateGraphsNoBlock() Function

Hi All,

Long time lurker, first time poster :slight_smile:

I’m getting some strange behavior on a game I’m mocking up while trying to use the UpdateGraphsNoBlock() function with a list of GraphNodes similarly to how it is being used in this other thread

The game is a 2d tile based game where each tile would correspond to a single node in the grid graph. In the picture below, the brown objects have already been placed while the red object is one that I’m trying to place. In order for an object’s placement to be valid, there must be a walkable path between each of the circled notches (or nodes) as well as a path to any of the blue tiles circled in the upper right hand corner. You can see that in this example, the placement is red for invalid because by placing the object there, it would block access for a number of the circled tiles, so it’s working correctly:

The problem I’m having is that if I were to move that object one tile to the right, I would expect that the placement would still be invalid because all of those tiles are still blocked, but now it’s showing that the placement is actually valid:

I plugged some Debug.Log statements into the UpdateGraphsNoBlock() function to see what I’m looking at there, and it looks like all of the correct points are getting added to the list of nodes to check, but it thinks that they’re still all in the same area.

If I were to then place the object down, however, all subsequent building will now return invalid, since one of the tiles that I would have expected to return invalid prior to placing the object is now correctly saying that it can’t reach one of the blue tiles.

I’m including code below, trimming off some of the extraneous stuff that I don’t think would be as relevant. Any thoughts or help would be much appreciated!

    public void newtilehover(GameObject hovertile, bool manualoverride) {
        //Don't do anything if it's the same tile being hovered over
        if(hovertile == currenthover && manualoverride == false) {
            return;
        }


        currenthover = hovertile;
        OfficeTileAssignments.tile tile = tileAssignments.returntilebygameobject(hovertile);
        List<OfficeTileAssignments.tile> requiredtiles = new List<OfficeTileAssignments.tile>();
        requiredtiles = tileAssignments.returnalltilesforobject(objecttoplace, currentorientation, tile);
        silhouetteinstance.transform.position = returncenterpos(currentorientation, tile);
        updatesilhouettecollider(objecttoplace);

        
        if(checkvalid(requiredtiles, objecttoplace) == true) {
            setsilhouettesprite(objecttoplace, true);
            validplacement = true;
            currenthover = hovertile;
            return;
        }

        //If the proposed object is still actually on the map, throw down the invalid silhouette
        if(requiredtiles.Count == objecttoplace.width * objecttoplace.length) {
            setsilhouettesprite(objecttoplace, false);
        }
        
        validplacement = false;
    }

    //
    bool checkvalid(List<OfficeTileAssignments.tile> requiredtiles, placableobject proposedobject) {
        //If the list of tiles provided is shorter than expected, this means you're trying to build off the edge of the map
        if(requiredtiles.Count < proposedobject.width * proposedobject.length) {
            return false;
        }
        //Check to make sure that none of the required tiles are already occupied
        for(int i = 0; i < requiredtiles.Count; i++) {
            if(requiredtiles[i].typeoftile != OfficeTileAssignments.typeoftile.floor) {
                return false;
            }
        }
        //Check that all "next to chair" tiles will still have a path to the breakroom/eachother
        if(checkpathtobreakroom(requiredtiles, proposedobject, currentorientation, tileAssignments.returntilebygameobject(currenthover)) == false) {
            return false;
        }
        return true;
    }

    //Manually set polygonpollider2d points to align with the required tiles to build the object
    void updatesilhouettecollider(placableobject objecttoplace) {
        if(objecttoplace.objecttype == typeofobject.salesdesk || objecttoplace.objecttype == typeofobject.tradingdesk) {
            Vector2[] polypoints = new Vector2[8];
            polypoints[0] = new Vector2(3,0);
            polypoints[1] = new Vector2(1,0);
            polypoints[2] = new Vector2(1,-2);
            polypoints[3] = new Vector2(-1,-2);
            polypoints[4] = new Vector2(-1,0);
            polypoints[5] = new Vector2(-3,0);
            polypoints[6] = new Vector2(-3,2);
            polypoints[7] = new Vector2(3,2);
            silhouettecollider.points = polypoints;
        }
    }


    //This function compiles a list of all of the "next to desk" tiles and ensure that they will all 
    //still have a path to the breakroom if the new proposed object is placed
    bool checkpathtobreakroom(List<OfficeTileAssignments.tile> tilestouse, placableobject proposedobject, objectorientation orientation, OfficeTileAssignments.tile hovertile) {
        List<GraphNode> allnodes = new List<GraphNode>();
        OfficeTileAssignments.tile breakroomtile = tileAssignments.returnanybreakroomtile();
        var breakroomnode = AstarPath.active.GetNearest(breakroomtile.tilegameobject.transform.position).node;
        allnodes.Add(breakroomnode);

        //Cycle through all existing desks, and adds their "next to chair" nodes to the list of nodes to check
        Dictionary<DeskAssignments.desk, character> alldesks = deskAssignments.returnalldesks();
        foreach(KeyValuePair<DeskAssignments.desk, character> desk in alldesks) {
            OfficeTileAssignments.tile desknode_1 = null;
            OfficeTileAssignments.tile desknode_2 = null;
            if(desk.Key.orientation == objectorientation.left) {
                desknode_1 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x, desk.Key.chairlocation.y + 1});
                desknode_2 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x, desk.Key.chairlocation.y - 1});
            }
            else if(desk.Key.orientation == objectorientation.up) {
                desknode_1 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x + 1, desk.Key.chairlocation.y});
                desknode_2 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x - 1, desk.Key.chairlocation.y});
            }
            else if(desk.Key.orientation == objectorientation.down) {
                desknode_1 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x + 1, desk.Key.chairlocation.y});
                desknode_2 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x - 1, desk.Key.chairlocation.y});
            }
            else if(desk.Key.orientation == objectorientation.right) {
                desknode_1 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x, desk.Key.chairlocation.y + 1});
                desknode_2 = tileAssignments.returntilebycoordinates(new int[]{desk.Key.chairlocation.x, desk.Key.chairlocation.y - 1});
            }

            var startnode_1 = AstarPath.active.GetNearest(desknode_1.tilegameobject.transform.position).node;
            var startnode_2 = AstarPath.active.GetNearest(desknode_2.tilegameobject.transform.position).node;
            allnodes.Add(startnode_1);
            allnodes.Add(startnode_2);
        }


        //If the new object is an additional desk, add the two new "next to chair" nodes to the list of nodes to check
        if(proposedobject.objecttype == typeofobject.desk) {
            if(orientation == objectorientation.up) {
                int[] coordinates = new int[2];
                coordinates[0] = hovertile.x + 1;
                coordinates[1] = hovertile.y - 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);

                coordinates[0] = hovertile.x - 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);
            }
            else if(orientation == objectorientation.down) {
                int[] coordinates = new int[2];
                coordinates[0] = hovertile.x + 1;
                coordinates[1] = hovertile.y + 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);

                coordinates[0] = hovertile.x - 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);
            }
            else if(orientation == objectorientation.left) {
                int[] coordinates = new int[2];
                coordinates[0] = hovertile.x + 1;
                coordinates[1] = hovertile.y + 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);

                coordinates[1] = hovertile.y - 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);
            }
            else if(orientation == objectorientation.right) {
                int[] coordinates = new int[2];
                coordinates[0] = hovertile.x - 1;
                coordinates[1] = hovertile.y + 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);

                coordinates[1] = hovertile.y - 1;
                allnodes.Add(AstarPath.active.GetNearest(tileAssignments.returntilebycoordinates(coordinates).tilegameobject.transform.position).node);
            }
        }

        //Manually creating new bounds in case I decide to use some really funky shapes in the future
        float min_x = silhouettecollider.points.Min(point => point.x);
        float max_x = silhouettecollider.points.Max(point => point.x);
        float x_extent = 0;
            
        if(min_x >= max_x) {
            x_extent = min_x;
        }
        else {
            x_extent = max_x;
        }

        float min_y = silhouettecollider.points.Min(point => point.y);
        float max_y = silhouettecollider.points.Max(point => point.y);
        float y_extent = 0;
        if(min_y >= max_y) {
            y_extent = min_y;
        }
        else {
            y_extent = max_y;
        }

        Vector3 extents = new Vector3();
        extents.x = x_extent;
        extents.y = y_extent;
        extents.z = 0;

        Bounds updatearea = new Bounds();
        updatearea.center = silhouettecollider.transform.position;
        updatearea.extents = extents;

        Debug.Log("Bound for the update area are " + updatearea);


        //Creating new object area with the new bounds and checking if all nodes as still valid
        var guo = new GraphUpdateObject(updatearea);
        return GraphUpdateUtilities.UpdateGraphsNoBlock(guo, allnodes, alwaysRevert: true);
    }

Hi

One possible related thing has been fixed in the 3.4 beta (see A* Pathfinding Project)

Fixed UpdateGraphsNoBlock(GraphUpdateObject,GraphNode,GraphNode,bool) could return incorrect results if there were already some pending graph updates.

You could try to insert an AstarPath.active.FlushGraphUpdates() call right before your call to UpdateGraphsNoBlock and see if that changes anything.
Alternatively, you could try to upgrade to the beta version.