In my Unity-based tactical RPG, I implemented the A* algorithm for efficient pathfinding. This algorithm finds the shortest path from one tile to another by evaluating nodes based on cost and heuristic values. Starting with the open and closed lists, it continuously updates the path cost for neighboring nodes, adding them to the open list if they present a better route. The algorithm calculates distances using the Manhattan method and retraces the path from the destination to the start node. This algorithm has been useful for developing the enemies AI, as it allows enemies with tracking behavior to pursue player units from anywhere on the grid.
public List FindShortestPath(int startX, int startZ, int endX, int endZ) {
PathTile startTile = pathTiles[startX, startZ];
PathTile endTile = pathTiles[endX, endZ];
List openList = new List();
List closedList = new List();
openList.Add(startTile);
while (openList.Count > 0) {
PathTile currentTile = openList[0];
for (int i = 0; i < openList.Count; i++) {
if (currentTile.fValue > openList[i].fValue) {
currentTile = openList[i];
}
if (currentTile.fValue == openList[i].fValue && currentTile.hValue > openList[i].hValue) {
currentTile = openList[i];
}
}
openList.Remove(currentTile);
closedList.Add(currentTile);
if (currentTile == endTile) {
return RetracePath(startTile, endTile);
}
List neighborTiles = new List();
for (int x = -1; x < 2; x++) {
for (int z = -1; z < 2; z++) {
if ((x == 0 && z == 0) || (x != 0 && z != 0)) {continue;}
if (!gridTraverse.IsValid(currentTile.x + x, currentTile.z + z)) { continue; } //?
neighborTiles.Add(pathTiles[currentTile.x + x, currentTile.z + z]);
}
}
for (int i = 0; i < neighborTiles.Count; i++) {
if (closedList.Contains(neighborTiles[i])) { continue; }
if (!gridTraverse.GetGridTile(neighborTiles[i].x, neighborTiles[i].z).GetPassable()) { continue; }
float moveCost = currentTile.gValue + CalculateDistance(currentTile , neighborTiles[i]);
if (!openList.Contains(neighborTiles[i]) || moveCost < neighborTiles[i].gValue) {
neighborTiles[i].gValue = moveCost;
neighborTiles[i].hValue = CalculateDistance(neighborTiles[i], endTile);
neighborTiles[i].parentTile = currentTile;
if(!openList.Contains(neighborTiles[i])) {
openList.Add(neighborTiles[i]);
}
}
}
}
return null;
}
private int CalculateDistance(PathTile currentTile, PathTile targetTile) {
int distX = Mathf.Abs(currentTile.x - targetTile.x);
int distZ = Mathf.Abs(currentTile.z - targetTile.z);
if (distX > distZ) { return 14 * distZ + 10 * (distX - distZ); }
return 14 * distX + 10 * (distZ - distX);
}
private List RetracePath(PathTile startTile, PathTile endTile) {
List path = new List();
PathTile currentTile = endTile;
while (currentTile != startTile) {
path.Add(currentTile);
currentTile = currentTile.parentTile;
}
path.Reverse();
return path;
}