Created
December 8, 2023 17:48
-
-
Save AlexiyOrlov/e4ffbd7fa7d16e70bf622aa0c6a1bec8 to your computer and use it in GitHub Desktop.
A* pathfinding for floating point coordinates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class FloatCell | |
{ | |
public bool walkable = true; | |
public float x, y; | |
public int hCost, gCost, fCost; | |
public FloatCell previous; | |
public FloatCell(float x,float y) | |
{ | |
this.x = x; | |
this.y = y; | |
} | |
public void CalculateFCost() | |
{ | |
fCost = hCost + gCost; | |
} | |
public override string ToString() | |
{ | |
return "X "+x+", Y "+y; | |
} | |
} | |
public class FloatNavigationGrid | |
{ | |
public int width, height; | |
public FloatCell[,] cells; | |
public FloatNavigationGrid(int width, int height) | |
{ | |
this.width = width; | |
this.height = height; | |
cells=new FloatCell[width, width]; | |
} | |
public FloatCell GetCell(float x,float y) | |
{ | |
int ix = Mathf.RoundToInt(x / LevelGenerator.CELL_SIZE); | |
int iy = Mathf.RoundToInt(y / LevelGenerator.CELL_SIZE); | |
return cells[ix, iy]; | |
} | |
List<FloatCell> GetNeighbors(FloatCell of) | |
{ | |
List<FloatCell> neighbors = new List<FloatCell>(); | |
if (of.x - LevelGenerator.CELL_SIZE >= 0) | |
{ | |
FloatCell left = GetCell(of.x - LevelGenerator.CELL_SIZE, of.y); | |
if (left.walkable) | |
{ | |
neighbors.Add(left); | |
} | |
if (of.y - LevelGenerator.CELL_SIZE >= 0) | |
{ | |
FloatCell lowerLeft = GetCell(of.x - LevelGenerator.CELL_SIZE, of.y - LevelGenerator.CELL_SIZE); | |
if (lowerLeft.walkable) | |
{ | |
neighbors.Add(lowerLeft); | |
} | |
} | |
if (of.y + LevelGenerator.CELL_SIZE < height*LevelGenerator.CELL_SIZE) | |
{ | |
Debug.Log((of.y+LevelGenerator.CELL_SIZE)+" lesser than "+height*LevelGenerator.CELL_SIZE); | |
FloatCell upperLeft = GetCell(of.x - LevelGenerator.CELL_SIZE, of.y + LevelGenerator.CELL_SIZE); | |
if (upperLeft.walkable) | |
{ | |
neighbors.Add(upperLeft); | |
} | |
} | |
} | |
if (of.x + LevelGenerator.CELL_SIZE < width*LevelGenerator.CELL_SIZE) | |
{ | |
FloatCell right = GetCell(of.x + LevelGenerator.CELL_SIZE, of.y); | |
if (right.walkable) | |
{ | |
neighbors.Add(right); | |
} | |
if (of.y - LevelGenerator.CELL_SIZE >= 0) | |
{ | |
FloatCell lowerRight = GetCell(of.x + LevelGenerator.CELL_SIZE, of.y - LevelGenerator.CELL_SIZE); | |
if (lowerRight.walkable) | |
{ | |
neighbors.Add(lowerRight); | |
} | |
} | |
if (of.y + LevelGenerator.CELL_SIZE < height*LevelGenerator.CELL_SIZE) | |
{ | |
FloatCell upperRight = GetCell(of.x + LevelGenerator.CELL_SIZE, of.y + LevelGenerator.CELL_SIZE); | |
if (upperRight.walkable) | |
{ | |
neighbors.Add(upperRight); | |
} | |
} | |
} | |
if (of.y - LevelGenerator.CELL_SIZE >= 0) | |
{ | |
FloatCell lower = GetCell(of.x, of.y - LevelGenerator.CELL_SIZE); | |
if (lower.walkable) | |
{ | |
neighbors.Add(lower); | |
} | |
} | |
if (of.y + LevelGenerator.CELL_SIZE < height*LevelGenerator.CELL_SIZE) | |
{ | |
FloatCell upper = GetCell(of.x, of.y + LevelGenerator.CELL_SIZE); | |
if (upper.walkable) | |
{ | |
neighbors.Add(upper); | |
} | |
} | |
return neighbors; | |
} | |
int CalculateDistanceCost(FloatCell a, FloatCell b) | |
{ | |
float distanceX = Mathf.Abs(a.x - b.x); | |
float distanceY = Mathf.Abs(a.y - b.y); | |
float remaining = Mathf.Abs(distanceX - distanceY); | |
return Mathf.RoundToInt(14 * Mathf.Min(distanceX, distanceY)/LevelGenerator.CELL_SIZE + 10* remaining/LevelGenerator.CELL_SIZE); | |
} | |
FloatCell GetLowestFCostNode(List<FloatCell> cellList) | |
{ | |
FloatCell cheapest = cellList[0]; | |
for (int i = 1; i < cellList.Count; i++) | |
{ | |
if (cellList[i].fCost < cheapest.fCost) | |
{ | |
cheapest = cellList[i]; | |
} | |
} | |
return cheapest; | |
} | |
List<FloatCell> CalculatePath(FloatCell end) | |
{ | |
List<FloatCell> path = new(){end}; | |
FloatCell current = end; | |
while (current.previous != null) | |
{ | |
path.Add(current.previous); | |
current = current.previous; | |
} | |
path.Reverse(); | |
return path; | |
} | |
public List<FloatCell> FindPath(float startX, float startY, float endX, float endY) | |
{ | |
FloatCell start = cells[Mathf.RoundToInt(startX /LevelGenerator.CELL_SIZE), Mathf.RoundToInt(startY / LevelGenerator.CELL_SIZE)]; | |
FloatCell end = cells[Mathf.RoundToInt(endX / LevelGenerator.CELL_SIZE), Mathf.RoundToInt(endY / LevelGenerator.CELL_SIZE)]; | |
List<FloatCell> openList = new List<FloatCell>() { start }; | |
HashSet<FloatCell> closedList = new HashSet<FloatCell>(); | |
for (int x = 0; x < width; x++) | |
{ | |
for (int y = 0; y < height; y++) | |
{ | |
FloatCell cell = cells[x, y]; | |
cell.gCost = int.MaxValue; | |
cell.CalculateFCost(); | |
cell.previous = null; | |
} | |
} | |
start.gCost = 0; | |
start.hCost = CalculateDistanceCost(start, end); | |
start.CalculateFCost(); | |
while (openList.Count > 0) | |
{ | |
FloatCell currentCell = GetLowestFCostNode(openList); | |
if (currentCell == end) | |
{ | |
return CalculatePath(end); | |
} | |
openList.Remove(currentCell); | |
closedList.Add(currentCell); | |
foreach (FloatCell neighbor in GetNeighbors(currentCell)) | |
{ | |
if (closedList.Contains(neighbor)) | |
continue; | |
int tentativeCost = currentCell.gCost + CalculateDistanceCost(currentCell, neighbor); | |
if (tentativeCost < neighbor.gCost) | |
{ | |
neighbor.previous = currentCell; | |
neighbor.gCost = tentativeCost; | |
neighbor.hCost = CalculateDistanceCost(neighbor, end); | |
neighbor.CalculateFCost(); | |
if (!openList.Contains(neighbor)) | |
{ | |
openList.Add(neighbor); | |
} | |
} | |
} | |
} | |
return null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment