/* Connects a node to another (by creating an edge between them) * * param: node - node to connect * param: cost - the cost of the path between two nodes */ public void connect(AStarNode node, float cost) { // Create a weighted edge between this node and the specified node AStarEdge edge = new AStarEdge(this, node, cost); // Add the edge to this node's list of edges this.neighbours.Add(edge); // Add the edge to the other node's list of edges node.neighbours.Add(edge); }
/* Find the shortest path from a start to target position * * param: start - start position * param: target - end position * returns: List<Vector2> - list of positions corresponding to grid nodes * of the shortest path between start and target, null if start or target * not on the walkable part of the grid * * This method implements the A* algorithm. It maintains a list of open nodes (to * which path from starting position has been found). At the beginning, only the * starting node is added to the list of open nodes. In each iteration, connections from * all open nodes are considered, and one that gives the smallest cost + guess is the next * node chosen for travelling through. If the next node is not on open list, it gets added * to the open list. This continues until the next node is the target node. * * The paths are maintained by saving recording in each node the prevoius node in the path. * Tracing path of previous nodes from target to start node allows reconstruction of the path taken. * */ public List <Vector2> ShortestPath(Vector2 start, Vector2 target) { // List of nodes that have a path to from the start node List <AStarNode> open = new List <AStarNode> (); // List of nodes that are dead ends - cannot take a new path from // those nodes to the target List <AStarNode> closed = new List <AStarNode> (); //Find the closest node to the start position AStarNode startNode = closestNode(start); //Find the closest node to the target position AStarNode targetNode = closestNode(target); if (startNode == null || !startNode.walkable) { return(null); } if (targetNode == null || !targetNode.walkable) { return(null); } if (startNode == targetNode) { return(null); } //Reset the start node cost - clears the path links from the prevoius run of A* startNode.reset(); //Compute the guess value - Euclidean distance from start to target startNode.guess = Vector2.Distance(startNode.position, targetNode.position); //Add the start node to the list of nodes to consider for taking on the next //iteration open.Add(startNode); #if UNITY_EDITOR // Create a list of examined nodes - debugging only */ gizmos = new List <AStarNode>(); gizmos.Add(startNode); #endif //Keep traversing the grid until get to the target node while (true) { //Sort the list of open nodes according to path cost+guess - this assures // that open nodes with smallest total cost will be considered first open = open.OrderBy(x => x.cost + x.guess).ToList(); List <AStarNode> removeFromOpen = new List <AStarNode>(); AStarNode nextNode = null; //Examine connections of each open node. foreach (AStarNode node in open) { AStarEdge nextEdge = null; //For each connection of the open node... foreach (AStarEdge edge in node.neighbours) { AStarNode neighbour = edge.getNeighbour(node); //Check if there is a new path to consider. if (!closed.Contains(neighbour)) { if (!open.Contains(neighbour)) { nextEdge = edge; break; } else { float newGuess = Vector2.Distance(neighbour.position, targetNode.position); // If there is a new path to consider, compute its cost - save the edge // that gives new path with the lowest cost float newCost = node.cost + edge.cost; if ((newCost + newGuess < neighbour.cost + neighbour.guess)) { nextEdge = edge; break; } } } } //If no new paths were found, move the node from the opne nodes list //to the closed list (it won't be considered for new paths) if (nextEdge == null) { removeFromOpen.Add(node); closed.Add(node); } else { //If a new path has been found, take it, and update the cost in the //node nextNode = nextEdge.getNeighbour(node); #if UNITY_EDITOR /* Save new noded to the list of examined nodes - debugging only */ if (!gizmos.Contains(nextNode)) { gizmos.Add(nextNode); } #endif float newGuess = Vector2.Distance(nextNode.position, targetNode.position); float newCost = node.cost + nextEdge.cost; nextNode.prev = node; nextNode.cost = newCost; nextNode.guess = newGuess; //If the new path takes us to the target node, we are done - return //the path taken to this node if (nextNode == targetNode) { return(targetNode.getPath()); } else { break; } } } if (nextNode == null) { break; } //If the next node is not on open nodes list, add it to open //nodes list if (!open.Contains(nextNode)) { open.Add(nextNode); } //Clean up - remove any nodes that have been added to the closed //node list from te open nodes list foreach (AStarNode node in removeFromOpen) { open.Remove(node); } } return(null); }