Unity学习记录-A星寻路
准备
路径排序
在A星寻路算法中,计算出组成路径的方格的关键是下面的等式:
F=G+H
其中:
- G是从起点A移动到该方格的移动代价
- H是从该方格移动到终点B的估算成本。H的值是乐观的,也就是说,H在计算时不会考虑障碍物、河流等地形因素。在这里,H的计算可以用Manhattan方法:横向移动的步数+纵向移动的步数
- F是方格的代价总和,F越低的方格越优先试探

数据结构准备
准备如下数据结构:
- List sortList:存储待试探的节点
- Dictionary cameFrom: 存储每个节点及其父节点(从哪里来的)
1 2
| List<Vector2Int> sortList = new List<Vector2Int>(); Dictionary<Vector2Int, Vector2Int> cameFrom = new Dictionary<Vector2Int, Vector2Int>();
|
开始搜索
我们可以从七点开始,检查其相邻的方格,然后向四周扩展,直到找到目标
- 将起点加入sortList
- 检查起点的相邻四个节点是否可以行走(非超出边界、河流、障碍物等),计算相邻合法节点的G、H、F,存入sortList中
- 从sortList中取出代价F最小的节点,对此节点的相邻节点进行试探,如果有多个代价最小的节点,则取H较小的
- 重复步骤3,直到找到终点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| sortList.Add(start); cameFrom[start] = new Vector2Int(-1, -1); bool hasRoute = false;
while (sortList.Count > 0) { sortList.Sort((Vector2Int a, Vector2Int b) => { if ((manhattan(start, a) + manhattan(end, a)) - (manhattan(start, b) + manhattan(end, b)) != 0) return (manhattan(start, a) + manhattan(end, a)) - (manhattan(start, b) + manhattan(end, b)); else return manhattan(end, a) - manhattan(end, b); });
Vector2Int current = sortList[0]; sortList.RemoveAt(0); if (current == end) { hasRoute = true; break; } foreach (Vector2Int offset in offsets) { Vector2Int newPos = current + offset; if (newPos.x < 0 || newPos.y < 0 || newPos.x >= map.Count || newPos.y >= map[0].Count) { continue; } if (cameFrom.ContainsKey(newPos)) { continue; } if (map[newPos.x][newPos.y].Find("Wallnut")) { continue; } sortList.Add(newPos); cameFrom[newPos] = current; } }
|

绘制路径
找到路径后,进行路径的绘制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| if (hasRoute) { Stack<Vector2Int> trace = new Stack<Vector2Int>(); Vector2Int pos = end; while (cameFrom.ContainsKey(pos)) { trace.Push(pos); pos = cameFrom[pos]; } while (trace.Count > 0) { Vector2Int p = trace.Pop(); mapComp.RenderRoute(p); } }
|
整体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| public IEnumerator IAStar(List<List<Transform>> map, Vector2Int start, Vector2Int end) { List<Vector2Int> sortList = new List<Vector2Int>(); Dictionary<Vector2Int, Vector2Int> cameFrom = new Dictionary<Vector2Int, Vector2Int>(); sortList.Add(start); cameFrom[start] = new Vector2Int(-1, -1); bool hasRoute = false; while (sortList.Count > 0) { sortList.Sort((Vector2Int a, Vector2Int b) => { if ((manhattan(start, a) + manhattan(end, a)) - (manhattan(start, b) + manhattan(end, b)) != 0) return (manhattan(start, a) + manhattan(end, a)) - (manhattan(start, b) + manhattan(end, b)); else return manhattan(end, a) - manhattan(end, b); });
Vector2Int current = sortList[0]; sortList.RemoveAt(0); if (current == end) { hasRoute = true; break; } foreach (Vector2Int offset in offsets) { Vector2Int newPos = current + offset; if (newPos.x < 0 || newPos.y < 0 || newPos.x >= map.Count || newPos.y >= map[0].Count) { continue; } if (cameFrom.ContainsKey(newPos)) { continue; } if (map[newPos.x][newPos.y].Find("Wallnut")) { continue; } sortList.Add(newPos); mapComp.RenderBox2(newPos); cameFrom[newPos] = current; } } if (hasRoute) { Stack<Vector2Int> trace = new Stack<Vector2Int>(); Vector2Int pos = end; while (cameFrom.ContainsKey(pos)) { trace.Push(pos); pos = cameFrom[pos]; } while (trace.Count > 0) { Vector2Int p = trace.Pop(); mapComp.RenderRoute(p); } } }
|