AStar算法()


  把網上的AStar算法的論述自己實現了一遍,一開始只是最基礎的實現。當然,現在AStar算法已經演變出了各種優化的版本,這篇也會基於各種優化不斷的更新。

  如果對算法不熟悉可以看下Stanford的這篇文章,我覺得是講解的十分仔細的了:http://theory.stanford.edu/~amitp/GameProgramming/,也附上國內的翻譯:http://blog.csdn.net/coutamg/article/details/53923717

  講講我對上面這篇文章的理解:

  (1)AStar算法的核心就在於這個公式了f(n) = g(n) + h(n),算法的效果如何也都取決於這個公式。就如文章中說的,g(n)可以看做從start到current所花費的cost,h(n)從current到end的花費。

    很多人會直接將這兩個當做距離來計算,這是在忽略地形等條件影響下最簡單的模型。對於每一步,我們可以確定g(n)的准確值,但是h(n)很難預估正確的值(特別是在復雜且大型的場景中)。文章中也提供了幾種解決方案比如waypoint等。

  (2)算法維護着兩張表openlist和closelist,openlist初始化時將start加入。

    在循環尋找路徑時,將openlist中優先級最高的元素取出,移入closelist中,表示該點已經“探測”過。對該點的周圍N個neighbor進行檢測,符合條件將其加入openlist中,並對openlist進行優先級排序。

  既然是對基礎的簡單理解,就不多說直接貼上代碼:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 
  5 public class AStar  {
  6 
  7     public enum POINT_TYPE
  8     {
  9         normal,
 10         obstacle,
 11     }
 12 
 13     public class POINT:System.IComparable
 14     {
 15         float _gValue, _hValue;
 16         public float gValue
 17         {
 18             get
 19             {
 20                 return _gValue;
 21             }
 22             set
 23             {
 24                 _gValue = value;
 25                 fValue = AStar.GetFValue(pos,gValue,hValue);
 26             }
 27         }
 28 
 29         public float hValue
 30         {
 31             get
 32             {
 33                 return _hValue;
 34             }
 35             set
 36             {
 37                 _hValue = value;
 38                 fValue = AStar.GetFValue(pos, gValue, hValue);
 39             }
 40         }
 41         public float fValue
 42         {
 43             get;
 44             private set;
 45         }
 46         public Vector2 pos, parent;
 47         public POINT_TYPE type;
 48 
 49         public int CompareTo(object obj)
 50         {
 51             POINT pt = obj as POINT;
 52             if (fValue < pt.fValue)
 53                 return -1;
 54             else if (fValue == pt.fValue)
 55                 return 0;
 56             else
 57                 return 1;
 58         }
 59 
 60         
 61     }
 62 
 63     public Dictionary<Vector2, POINT> points = new Dictionary<Vector2, POINT>();
 64     public static Vector2 startPt, endPt;
 65 
 66     public List<POINT> openList = new List<POINT>();
 67     public List<POINT> closeList = new List<POINT>();
 68 
 69     public bool finish = false;
 70 
 71 
 72     public static float GetFValue(Vector2 pt,float gValue,float hValue)
 73     {
 74         Vector2 vec1 = pt - startPt;
 75         Vector2 vec2 = endPt - startPt;
 76         float fac = Vector3.Cross(new Vector3(vec1.x, vec1.y, 0), new Vector3(vec2.x, vec2.y, 0)).normalized.z > 0 ? 0.01f : -0.01f;
 77         return gValue + 2f * hValue + fac;
 78     }
 79 
 80     float GetManhattanDistance(Vector2 pos1, Vector2 pos2)
 81     {
 82         return Mathf.Abs(pos1.x - pos2.x) + Mathf.Abs(pos1.y - pos2.y);
 83     }
 84 
 85     List<POINT> GetNeighbours(Vector2 pt)
 86     {
 87         List<POINT> neighbouts = new List<POINT>();
 88         if (points.ContainsKey(new Vector2(pt.x - 1, pt.y)))
 89             neighbouts.Add(points[new Vector2(pt.x - 1, pt.y)]);
 90         if (points.ContainsKey(new Vector2(pt.x + 1, pt.y)))
 91             neighbouts.Add(points[new Vector2(pt.x + 1, pt.y)]);
 92         if (points.ContainsKey(new Vector2(pt.x, pt.y +1)))
 93             neighbouts.Add(points[new Vector2(pt.x, pt.y +1)]);
 94         if (points.ContainsKey(new Vector2(pt.x, pt.y - 1)))
 95             neighbouts.Add(points[new Vector2(pt.x, pt.y - 1)]);
 96         return neighbouts;
 97     }
 98 
 99     public void Init(List<POINT> pts,Vector2 start,Vector2 end)
100     {
101         foreach (POINT pt in pts)
102         {
103             points.Add(pt.pos, pt);
104         }
105 
106         startPt = start;
107         endPt = end;
108 
109         points[startPt].parent = start;
110         points[startPt].gValue = 0;
111         points[startPt].hValue = Mathf.Abs(startPt.x - endPt.x) + Mathf.Abs(startPt.y - endPt.y);
112 
113         openList.Add(points[startPt]);
114 
115         finish = false;
116     }
117 
118     public void StepNext()
119     {
120         if (finish)
121             return;
122 
123         POINT current = openList[0];
124         openList.Remove(current);
125         closeList.Add(current);
126         if (current.pos == endPt)
127         {
128             finish = true;
129             return;
130         }
131 
132         List<POINT> neighbours = GetNeighbours(current.pos);
133         for (int i = 0; i < neighbours.Count; i++)
134         {
135             if (neighbours[i].type == POINT_TYPE.obstacle || closeList.Contains(neighbours[i]))
136                 continue;
137 
138             bool needSort = false;
139             float gValue =GetManhattanDistance(neighbours[i].pos,startPt);
140             float hValue = GetManhattanDistance(neighbours[i].pos,endPt);
141             float fValue = AStar.GetFValue(neighbours[i].pos,gValue,hValue);
142 
143             if (openList.Contains(neighbours[i]))
144             {
145                 if (neighbours[i].fValue > fValue)
146                 {
147                     neighbours[i].gValue = gValue;
148                     neighbours[i].hValue = hValue;
149                     needSort = true;
150                 }
151             }
152             else
153             {
154                 neighbours[i].gValue = gValue;
155                 neighbours[i].hValue = hValue;
156                 neighbours[i].parent = current.pos;
157                 openList.Add(neighbours[i]);
158                 needSort = true;
159             }
160 
161 
162             if (needSort)
163                 openList.Sort();
164         }
165 
166     }
167 
168 }
View Code

  簡單的標注這幾行:

  49-58 :繼承於IComparable接口類,並且重寫了CompareTo方法。這樣就可以利用List<T>.Sort()來排序了。在CompareTo方法中,當fValue相等時,hValue值小具有更高的priority。關於fValue相同的情況在論文中有闡述。

  71-77:根據g(n),h(n)計算f(n)。上面說的算法的核心公式是f(n) = g(n) + h(n),但是很多時候這樣簡單的相加並不能適應各種復雜的情況。考慮這樣一種情況:

            

(請忽略這張圖中坐標下的數值(f值),並不與代碼相符)

  藍色為當前探測的點,黃色為待探測的點。若用公式f(n) = g(n) + h(n)此時有兩種相等(f和h都相同)的情況,這樣將增大計算的消耗。這里簡單的用了cross函數使相等時總是能選擇某一側作為偏向。

  138-139:g和h的值分別為n點到start和end點的曼哈頓距離(x,y軸上距離相加),這里其實只是近似值,並且g的值其實來說並不准確。上文說道g值對於每一步的計算來說都是可以確定的。在本例中暫且這樣,下一例中改進。

  來看一下算法的效果:

  

  第一張圖從(0,10)到(14,2),第二張圖從(0,12)到(14,2)。綠色為算法最終輸出的路徑,黃色與藍色為檢測的范圍。

  而在實現過程中也法線了g和h對算法的影響。當g>>h時,算法偏向於全方位的檢測,當h>>g時,算法偏向於向end點方向檢測。

 

 

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM