Keffinel
Keffinel

Reputation: 21

Unity A* Pathfinding Crashes

So I was trying to implement the A* algorithm. The Basic Logic is almost complete but there is this one problem I just cant fix.

When request a path Unity stops responding and my PC keeps getting slower until it hangs and I have to force shut down.

Here is the simplified code:

public static List<Node> ReturnPath(Vector3 pointA, Vector3 pointB)
{
    /* A Bunch of initializations */

    while (!pathFound) 
    {
        //add walkable neighbours to openlist
        foreach (Node n in GetNeighbours(currentNode)) 
        {
            if (!n.walkable)
                continue;
            n.gCost = currentNode.gCost + GetManDist (currentNode, n);
            n.hCost = GetManDist (n, B);
            openList.Add (n);
            n.parent = currentNode;

        }

        //check openList for lowest fcost or lower hCost
        for (int i = 0; i < openList.Count; i++) 
        {
            if ((openList [i].fCost < currentNode.fCost) || (openList [i].fCost == currentNode.fCost && openList [i].hCost < currentNode.hCost)) 
            {
                //make the currentNode = node with lowest fcost
                currentNode = openList [i];
            }
            openList.Remove (currentNode);
            if(!closedList.Contains(currentNode))
                closedList.Add (currentNode);
        }

        //Check if the currentNode it destination
        if (currentNode == B) 
        {
            Debug.Log ("Path Detected");
            path = RetracePath (A, B);
            pathFound = true;
        }

    }
    return path;
}

This works fine as long as the destination is two nodes away. If it's any more than that, the problem mentioned above occurs. Why is that? My first guess that i was putting too much into the openList.

NOTES: Im breaking a 30 x 30 unit platform(floor) into 1x1 squares called "nodes" GetManDist() Gets the Manhattan Distance between 2 nodes.

UPDATE: Here's the working code. Was too long for a comment

    public List<Node> ReturnPath(Vector3 pointA, Vector3 pointB)
{
    List<Node> openList = new List<Node>();
    List<Node> closedList = new List<Node>();
    List<Node> path = new List<Node> ();

    Node A = ToNode (pointA);
    Node B = ToNode (pointB);
    Node currentNode;

    bool pathFound = false;

    A.hCost = GetManDist(A, B);
    A.gCost = 0;

    openList.Add (A);

    while (!pathFound) // while(openList.Count > 0) might be better because it handles the possibility of a non existant path
    {
        //Set to arbitrary element
        currentNode = openList [0];

        //Check openList for lowest fcost or lower hCost
        for (int i = 0; i < openList.Count; i++) 
        {
            if ((openList [i].fCost < currentNode.fCost) || ((openList [i].fCost == currentNode.fCost && openList [i].hCost < currentNode.hCost))) 
            {
                //Make the currentNode = node with lowest fcost
                currentNode = openList [i];
            }
        }

        //Check if the currentNode is destination
        if (currentNode.hCost == 0) //Better than if(currentNode == B)
        {
            path = RetracePath (A, B);
            pathFound = true;
        }

        //Add walkable neighbours to openlist
        foreach (Node n in GetNeighbours(currentNode)) 
        {
            //Avoid wasting time checking unwalkable and those already checked
            if (!n.walkable || closedList.Contains(n))
                continue;

            //Movement Cost to neighbour
            int newGCost = currentNode.gCost + GetManDist (currentNode, n);

            //Calculate g_Cost, Update if new g_cost to neighbour is less than an already calculated one
            if (n.gCost > newGCost || !openList.Contains (n)) 
            {
                n.gCost = newGCost;
                n.hCost = GetManDist (n, B);
                n.parent = currentNode; //So we can retrace
                openList.Add (n);
            }
        }
        //We don't need you no more...
        openList.Remove (currentNode);

        //Avoid redundancy of nodes in closedList
        if(!closedList.Contains(currentNode))
            closedList.Add (currentNode);

    }

    return path;
}

Upvotes: 0

Views: 351

Answers (1)

Keffinel
Keffinel

Reputation: 21

The problem was with the value of currentNode. Since we are checking for the node with the lowest f_Cost or lower h_Cost in the openlist against currentNode, in some cases when the pathfinding encounters a wall, it has to go back or take a turn which results in increasing f_Cost and h_Cost (both greater than that of the currentNode). Therefore there is no longer any node in the openlist with a lower f_Cost/h_Cost resulting in an infinite loop. The simple solution was setting the currentNode to an arbitrary element in the openList everytime.

Adding

currentNode = openlist[0];

at the beginning of the loop.

Upvotes: 1

Related Questions