本篇实现的A星算法是基于贪婪优先算法实现的,由于贪婪优先算法得到的是次优路径,因此我们增加一个当前节点到起始节点的一个路径开销分量来提升路径的质量,筛选最优路径。具体实现如下:
节点类的编写
节点主要用来存储当前节点在地图中的位置信息,如:行号,列号、父节点、到起始节点的路径开销量g,到目标节点的路径开销量h,总的开销量;并包含计算g和h的方法;具体实现代码如下:
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
| // 节点类 public class ANode : System.IComparable { // 行 public int Row { get; set; } // 列 public int Col { get; set; }
// 父节点 public ANode parent = null; // 相邻节点 public List<ANode> adjacent = new List<ANode>(); // 曼哈顿距离 public int h = 0;
public int g = 0;
public int f = 0;
public void H(ANode endNode) { h = Mathf.Abs(endNode.Row - Row) + Mathf.Abs(endNode.Col - Col);
f = g + h; }
// 清除 public void Clear() { parent = null; h = 0; g = 0; f = 0; }
public int CompareTo(object obj) { ANode node = obj as ANode;
if (f - node.f < 0) return -1; else if (f - node.f == 0) return 0; else return 1; } }
|
地图存储
地图存储与贪婪算法中的思路一样,代码逻辑一样,只是把节点变量换一下即可,这里不在描述,代码大家自行实现。
寻路算法实现
寻路算法与贪婪优先算法的实现相比只是增加了一个与起点的开销分量计算,然后根据f值来判断最优路径,具体实现代码如下:
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| public class AStarAlgorithm { private ANode startNode; private ANode destNode;
private List<ANode> openSet = new List<ANode>(); private List<ANode> closedSet = new List<ANode>();
private AMap map;
// 初始化地图 public AStarAlgorithm(AMap map) { this.map = map; }
// 查找开放式集合中H值最小的节点 private ANode FindLowest() { openSet.Sort(); return openSet[0]; }
// 将节点的相邻节点添加到开放集合中 private void AddAdjacent(ANode node) { for (int i = 0; i < node.adjacent.Count; i++) { if (closedSet.Contains(node.adjacent[i])) continue; int newG = node.g + Mathf.Abs(node.Row - node.adjacent[i].Row) + Mathf.Abs(node.Col - node.adjacent[i].Col); if(newG < node.adjacent[i].g || !openSet.Contains(node.adjacent[i])) { node.adjacent[i].parent = node; node.adjacent[i].g = newG; node.adjacent[i].H(destNode); if (!openSet.Contains(node.adjacent[i])) openSet.Add(node.adjacent[i]); } } }
// 更新地图 public void UpdateMap(AMap map) { this.map = map; }
public void Start(ANode startNode, ANode endNode) { openSet.Clear(); closedSet.Clear();
openSet.Add(startNode); destNode = endNode; this.startNode = startNode;
for (int i = 0; i < map.aNodes.Length; i++) { map.aNodes[i].Clear(); } }
public Stack<ANode> Find() { Stack<ANode> path = new Stack<ANode>();
ANode currNode = openSet[0];
while (currNode != destNode) { if (openSet.Count == 0) break;
currNode = FindLowest(); openSet.Remove(currNode); closedSet.Add(currNode);
AddAdjacent(currNode); }
if (currNode == destNode) { ANode node = destNode; while (node != null) { path.Push(node); node = node.parent; } } else return null;
return path; } }
|
对于节点类的优化可以继承贪婪优先算法的节点类,这样便于维护修改。
Unity实现效果如下
完整工程仓库地址:A星算法
参考
《游戏编程算法与技巧》