public IntVector2[] ToPath() { List <IntVector2> asList = new List <IntVector2>(); IntVector2AsAStarNode current = this; while (current != null) { asList.Add(current.coord); current = current.previous; } asList.Reverse(); return(asList.ToArray()); }
// -------------------------------------------------------------------------------------------- /// <summary> /// Finds the best path for a unit to a target coordinate using A* /// </summary> public bool TryGetBestPathForUnit(Unit unit, IntVector2 startCoord, IntVector2 targetCoord, int movementExhausted, out IntVector2[] bestPath) { bestPath = new IntVector2[0]; if (movementExhausted >= unit.MoveRange) { // return empty array if we've already exahusted the list return(false); } // A* implementation for best path HashSet <IntVector2AsAStarNode> open = new HashSet <IntVector2AsAStarNode>(); HashSet <IntVector2AsAStarNode> closed = new HashSet <IntVector2AsAStarNode>(); open.Add(new IntVector2AsAStarNode { coord = startCoord, previous = null, f = 0, g = 0, h = 0, }); int moveRange = unit.MoveRange - movementExhausted; bool succeeded = false; while (open.Count > 0) { List <IntVector2AsAStarNode> openAsList = new List <IntVector2AsAStarNode>(open); // sort the open list ascending by f value openAsList.Sort((IntVector2AsAStarNode a, IntVector2AsAStarNode b) => { return(a.f.CompareTo(b.f)); }); // set the current node to the node with the least f IntVector2AsAStarNode currentNode = openAsList[0]; closed.Add(openAsList[0]); open.Remove(currentNode); if (currentNode.coord.Equals(targetCoord)) // remember to use .Equals() instead of == becuase these are not the same object { bestPath = currentNode.ToPath(); succeeded = true; break; } List <BoardTile> nextBoardTiles = new List <BoardTile>(); BoardTile northTile = GetTile(currentNode.coord.x, currentNode.coord.y + 1); if (northTile != null) { nextBoardTiles.Add(northTile); } BoardTile southTile = GetTile(currentNode.coord.x, currentNode.coord.y - 1); if (southTile != null) { nextBoardTiles.Add(southTile); } BoardTile eastTile = GetTile(currentNode.coord.x + 1, currentNode.coord.y); if (eastTile != null) { nextBoardTiles.Add(eastTile); } BoardTile westTile = GetTile(currentNode.coord.x - 1, currentNode.coord.y); if (westTile != null) { nextBoardTiles.Add(westTile); } foreach (BoardTile boardTile in nextBoardTiles) { // check if we've already visited this coord bool isClosed = false; foreach (IntVector2AsAStarNode closedNode in closed) { isClosed |= closedNode.coord.Equals(boardTile.Coord); } if (isClosed) { continue; } // create a new node to add to the open list IntVector2AsAStarNode childNode = new IntVector2AsAStarNode(); childNode.coord = boardTile.Coord; childNode.previous = currentNode; childNode.g = currentNode.g + boardTile.GetMoveCostForUnit(unit); childNode.h = (boardTile.Coord - targetCoord).ManhattanDistance; childNode.f = childNode.g + childNode.h; // we can't look at tiles that are beyond a unit's move range or that are occupied by some other unit if (childNode.g > moveRange || (boardTile.Occupant != null && boardTile.Occupant != unit)) { continue; } // check if we've visited this coord but now we have a better path to it bool foundBetterPath = false; bool haveVisited = false; foreach (IntVector2AsAStarNode openNode in open) { if (!openNode.coord.Equals(childNode.coord)) { continue; } haveVisited = true; if (openNode.f < childNode.f) { continue; } // we've found a better node! openNode.g = childNode.g; openNode.h = childNode.h; openNode.f = childNode.f; openNode.previous = childNode.previous; foundBetterPath = true; } if (!haveVisited || !foundBetterPath) { open.Add(childNode); } } } if (bestPath.Length == 0) { // if we haven't found a best path, then there's no way the unit could move to the target // instead, return the best path to the node closest to the target List <IntVector2AsAStarNode> toCullFromClosed = new List <IntVector2AsAStarNode>(); foreach (IntVector2AsAStarNode closedNode in closed) { if (closedNode.g != moveRange) { toCullFromClosed.Remove(closedNode); } } foreach (IntVector2AsAStarNode toRemove in toCullFromClosed) { closed.Remove(toRemove); } List <IntVector2AsAStarNode> closedAsList = new List <IntVector2AsAStarNode>(closed); closedAsList.Sort((IntVector2AsAStarNode a, IntVector2AsAStarNode b) => { return(a.f.CompareTo(b.f)); }); bestPath = closedAsList[0].ToPath(); } return(succeeded); }
public static IntVector2[] FindPath(IntVector2 start, IntVector2 goal, Actor actor) { IntVector2[] bestPath = new IntVector2[0]; HashSet <IntVector2AsAStarNode> open = new HashSet <IntVector2AsAStarNode>(); HashSet <IntVector2AsAStarNode> closed = new HashSet <IntVector2AsAStarNode>(); open.Add(new IntVector2AsAStarNode { coord = start, previous = null, f = 0, g = 0, h = 0, }); int counter = 0; while (open.Count > 0) { counter++; List <IntVector2AsAStarNode> openAsList = new List <IntVector2AsAStarNode>(open); // sort the open list ascending by f value openAsList.Sort((IntVector2AsAStarNode a, IntVector2AsAStarNode b) => { return(a.f.CompareTo(b.f)); }); if (counter > 9999) { Debug.LogError("infinite loop detected"); break; } // set the current node to the node with the least f IntVector2AsAStarNode currentNode = openAsList[0]; closed.Add(openAsList[0]); open.Remove(currentNode); if (currentNode.coord.Equals(goal)) // remember to use .Equals() instead of == becuase these are not the same object { bestPath = currentNode.ToPath(); break; } List <IntVector2> potentialNextCoords = new List <IntVector2>(); if (actor.CanOccupyPosition((currentNode.coord + IntVector2.Up).ToUnityVector3_XY())) { potentialNextCoords.Add(currentNode.coord + IntVector2.Up); } if (actor.CanOccupyPosition((currentNode.coord + IntVector2.Down).ToUnityVector3_XY())) { potentialNextCoords.Add(currentNode.coord + IntVector2.Down); } if (actor.CanOccupyPosition((currentNode.coord + IntVector2.Left).ToUnityVector3_XY())) { potentialNextCoords.Add(currentNode.coord + IntVector2.Left); } if (actor.CanOccupyPosition((currentNode.coord + IntVector2.Right).ToUnityVector3_XY())) { potentialNextCoords.Add(currentNode.coord + IntVector2.Right); } foreach (IntVector2 coord in potentialNextCoords) { IntVector2AsAStarNode childNode = new IntVector2AsAStarNode(); childNode.coord = coord; childNode.previous = currentNode; childNode.g = currentNode.g + 1; childNode.h = (coord - goal).ManhattanDistance; childNode.f = childNode.g + childNode.h; // check if we've visited this coord but now we have a better path to it bool alreadyVisited = false; foreach (IntVector2AsAStarNode openNode in open) { if (!openNode.coord.Equals(coord)) { continue; } if (openNode.f <= childNode.f) { continue; } openNode.g = childNode.g; openNode.h = childNode.h; openNode.f = childNode.f; openNode.previous = childNode.previous; alreadyVisited = true; } foreach (IntVector2AsAStarNode closedNode in closed) { if (closedNode.coord.Equals(coord)) { alreadyVisited = true; break; } } if (!alreadyVisited) { open.Add(childNode); } } } if (bestPath.Length == 0) { // we couldn't find a path, so return the path that gets us closest List <IntVector2AsAStarNode> closedAsList = new List <IntVector2AsAStarNode>(closed); closedAsList.Sort((IntVector2AsAStarNode a, IntVector2AsAStarNode b) => { return(a.f.CompareTo(b.f)); }); bestPath = closedAsList[0].ToPath(); } return(bestPath); }