Unity3D A 星尋路(A*) C# 版本
轉載:http://www.gopedu.com/article/735
因為專案需要做一個 A 星尋路的功能,但是又不想用 Unity3D 中的 A 星尋路外掛,因為感覺外掛感覺不夠靈活,不能符合自己的設計,還好以前就保留了一位前輩的高效 A 星尋路連結,不過作者是用的 ActionScript 編寫的,所以我就整理成了 C# 版本的了。
最終效果圖如下:
測試程式碼如下(TestWorld.cs):
using UnityEngine; 002 using System.Collections.Generic; 003 004 public class TestWorld : MonoBehaviour 005 { 006 public GameObject cubeObject; 007 public GameObject pathObject; 008 009 public Camera mainCamera; 010 public SceneGrid sceneGrid; 011 012 private AStarUtils aStarUtils; 013 014 private AStarNode beginNode; 015 016 private int cols = 20; 017 private int rows = 20; 018 019 private IList<GameObject> pathList; 020 021 void Awake() 022 { 023 this.pathList = new List<GameObject> (); 024 this.aStarUtils = new AStarUtils (this.cols, this.rows); 025 026 // cols 027 for(int i = 0; i < this.cols; i++) 028 { 029 // rows 030 for(int j = 0; j < this.rows; j++) 031 { 032 AStarUnit aStarUnit = new AStarUnit(); 033 034 if(i != 0 && j != 0 && Random.Range(1, 10) <= 3) 035 { 036 aStarUnit.isPassable = false; 037 038 GameObject gameObject = (GameObject)Instantiate(cubeObject); 039 if(gameObject != null) 040 { 041 gameObject.transform.localPosition = new Vector3(i - this.cols * 0.5f + 0.5f, 0f, j - this.cols * 0.5f + 0.5f); 042 } 043 044 }else{ 045 aStarUnit.isPassable = true; 046 } 047 048 this.aStarUtils.GetNode(i,j).AddUnit(aStarUnit); 049 } 050 } 051 } 052 053 private void FindPath(int x, int y) 054 { 055 AStarNode endNode = this.aStarUtils.GetNode(x, y); 056 057 if (this.beginNode == null) 058 { 059 this.beginNode = endNode; 060 return; 061 } 062 063 if (this.pathList != null && this.pathList.Count > 0) 064 { 065 foreach (GameObject xxObject in this.pathList) 066 { 067 Destroy(xxObject); 068 } 069 } 070 071 if(endNode != null && endNode.walkable) 072 { 073 System.DateTime dateTime = System.DateTime.Now; 074 075 IList<AStarNode> pathList = this.aStarUtils.FindPath(this.beginNode, endNode); 076 077 System.DateTime currentTime = System.DateTime.Now; 078 079 System.TimeSpan timeSpan = currentTime.Subtract(dateTime); 080 081 Debug.Log(timeSpan.Seconds + "秒" + timeSpan.Milliseconds + "毫秒"); 082 083 if(pathList != null && pathList.Count > 0) 084 { 085 foreach(AStarNode nodeItem in pathList) 086 { 087 GameObject gameObject = (GameObject)Instantiate(this.pathObject); 088 this.pathList.Add(gameObject); 089 gameObject.transform.localPosition = new Vector3(nodeItem.nodeX - this.cols * 0.5f + 0.5f, 0f, nodeItem.nodeY - this.cols * 0.5f + 0.5f); 090 } 091 } 092 this.beginNode = endNode; 093 } 094 } 095 096 void Update() 097 { 098 if (Input.GetMouseButtonDown (0)) 099 { 100 Ray ray = this.mainCamera.ScreenPointToRay(Input.mousePosition);
101 102 RaycastHit raycastHit = new RaycastHit(); 103 if(Physics.Raycast(ray, out raycastHit)) 104 { 105 if(raycastHit.collider.gameObject.tag == "Plane") 106 { 107 Vector3 pointItem = this.sceneGrid.transform.InverseTransformPoint(raycastHit.point) * 2f; 108 109 pointItem.x = this.cols * 0.5f + Mathf.Ceil(pointItem.x) - 1f; 110 pointItem.z = this.cols * 0.5f + Mathf.Ceil(pointItem.z) - 1f; 111 112 this.FindPath((int)pointItem.x, (int)pointItem.z); 113 } 114 } 115 } 116 } 117 } SceneGrid.cs 1 using UnityEngine; 2 using System.Collections; 3 4 public class SceneGrid : MonoBehaviour 5 { 6 7 } A 星類庫程式碼如下(AStarCallback.cs): 01 using UnityEngine; 02 using System.Collections; 03 04 public class AStarCallback 05 { 06 // 07 public delegate void IsPassableChangeCallback(); 08 09 // 10 public delegate void HeuristicCallback(AStarNode aStarNode); 11 12 public event HeuristicCallback OnHeuristic; 13 14 public event IsPassableChangeCallback OnIsPassableChange; 15 16 public void InvokeHeuristic(AStarNode callAStarNode) 17 { 18 if (this.OnHeuristic != null) 19 { 20 this.OnHeuristic(callAStarNode); 21 } 22 } 23 24 public void InvokeIsPassableChange() 25 { 26 if (this.OnIsPassableChange != null) 27 { 28 this.OnIsPassableChange(); 29 } 30 } 31 } AtarDiagonalHeuristic.cs 01 using UnityEngine; 02 using System.Collections; 03 04 public class AStarDiagonalHeuristic : IAStarHeuristic 05 { 06 public int Heuristic(int x1, int y1, int x2, int y2) 07 { 08 int dx = x1 > x2 ? x1 - x2 : x2 - x1; 09 int dy = y1 > y2 ? y1 - y2 : y2 - y1; 10 11 return dx > dy ? AStarUtils.DIAG_COST * dy + AStarUtils.STRAIGHT_COST * (dx - dy) : AStarUtils.DIAG_COST * dx + AStarUtils.STRAIGHT_COST * (dy - dx); 12 } 13 } AStarLinkNode.cs 01 using UnityEngine; 02 using System.Collections; 03 04 /// <summary> 05 /// 鄰節點 06 /// </summary> 07 public class AStarLinkNode 08 { 09 /// <summary> 10 /// 節點 11 /// </summary> 12 public AStarNode node; 13 14 /// <summary> 15 /// 花費代價 16 /// </summary> 17 public int cost; 18 19 public AStarLinkNode(AStarNode node, int cost) 20 { 21 this.node = node; 22 this.cost = cost; 23 } 24 } AStarManhattanHeuristic.cs 01 using UnityEngine; 02 using System.Collections; 03 04 public class AStarManhattanHeuristic : IAStarHeuristic 05 { 06 public int Heuristic(int x1, int y1, int x2, int y2) 07 { 08 return ( 09 (x1 > x2 ? x1 - x2 : x2 - x1) 10 + 11 (y1 > y2 ? y1 - y2 : y2 - y1) 12 ) * AStarUtils.STRAIGHT_COST; 13 } 14 } AStarNode.cs 001 using UnityEngine; 002 using System.Collections.Generic; 003 004 public class AStarNode 005 { 006 /// <summary> 007 /// 座標 x 008 /// </summary> 009 public int nodeX; 010 011 /// <summary> 012 /// 座標 y 013 /// </summary> 014 public int nodeY; 015 016 /// <summary> 017 /// 父節點 018 /// </summary> 019 public AStarNode parentNode; 020 021 /// <summary> 022 /// 二叉堆節點 023 /// </summary> 024 public BinaryHeapNode binaryHeapNode; 025 026 /// <summary> 027 /// 與此節點相鄰的可通過的鄰節點 028 /// </summary> 029 public IList<AStarLinkNode> links; 030 031 /// <summary> 032 /// 搜尋路徑的檢查編號(確定是否被檢查過) 033 /// </summary> 034 public int searchPathCheckNum; 035 036 /// <summary> 037 /// 可移動範圍的檢查編號(確定是否被檢查過) 038 /// </summary> 039 public int walkableRangeCheckNum; 040 041 /// <summary> 042 /// 是否能被穿越 043 /// </summary> 044 public bool walkable; 045 046 /// <summary> 047 /// 從此節點到目標節點的代價(A星演算法使用) 048 /// </summary> 049 public int f; 050 051 /// <summary> 052 /// 從起點到此節點的代價 053 /// </summary> 054 public int g; 055 056 /// <summary> 057 /// 在此節點上的單位 058 /// </summary> 059 private IList<IAStarUnit> units; 060 061 /// <summary> 062 /// 通過回撥函式 063 /// </summary> 064 private AStarCallback aStarCallback = new AStarCallback (); 065 066 /// <summary> 067 /// 回撥函式引數 068 /// </summary> 069 private AStarNode aStarNodeParam; 070 071 public int unitCount 072 { 073 get { return this.units.Count; } 074 } 075 076 /// <summary> 077 /// 新增穿越代價被修改後的回撥函式 078 /// </summary> 079 /// <param name="callback">Callback.</param> 080 /// <param name="aStarNodeParam">A star node parameter.</param> 081 public void AddHeuristic(AStarCallback.HeuristicCallback callback, AStarNode aStarNodeParam) 082 { 083 this.aStarNodeParam = aStarNodeParam; 084 this.aStarCallback.OnHeuristic += callback; 085 } 086 087 /// <summary> 088 /// 移除穿越代價被修改後的回撥函式 089 /// </summary> 090 /// <param name="callback">Callback.</param> 091 public void RemoveHeuristic(AStarCallback.HeuristicCallback callback) 092 { 093 this.aStarCallback.OnHeuristic -= callback; 094 } 095 096 /// <summary> 097 /// 重新整理穿越代價 098 /// </summary> 099 private void RefreshPassCost() 100 { 101 foreach(IAStarUnit unit in this.units) 102 { 103 if(!unit.isPassable) 104 { 105 if(this.walkable) 106 { 107 this.walkable = false; 108 this.aStarCallback.InvokeHeuristic(this.aStarNodeParam); 109 } 110 return; 111 } 112 } 113 } 114 115 /// <summary> 116 /// 單位的 isPassable 屬性被改變 117 /// </summary> 118 /// <returns><c>true</c> if this instance is passable change; otherwise, <c>false</c>.</returns> 119 /*private void IsPassableChange() 120 { 121 this.RefreshPassCost(); 122 }*/ 123 124 /// <summary> 125 /// 新增單位 126 /// </summary> 127 /// <returns><c>true</c>, if unit was added, <c>false</c> otherwise.</returns> 128 /// <param name="unit">Unit.</param> 129 public bool AddUnit(IAStarUnit unit) 130 { 131 if(this.walkable) 132 { 133 if(this.units.IndexOf(unit) == -1) 134 { 135 //unit.AddIsPassableChange(this.IsPassableChange); 136 this.units.Add(unit); 137 RefreshPassCost(); 138 return true; 139 } 140 } 141 return false; 142 } 143 144 /// <summary> 145 /// 移除單位 146 /// </summary> 147 /// <returns><c>true</c>, if unit was removed, <c>false</c> otherwise.</returns> 148 /// <param name="unit">Unit.</param> 149 public bool RemoveUnit(IAStarUnit unit) 150 { 151 int index = this.units.IndexOf(unit); 152 if(index != -1) 153 { 154 //unit.RemoveIsPassableChange(this.IsPassableChange); 155 this.units.RemoveAt(index); 156 this.RefreshPassCost(); 157 return true; 158 } 159 return false; 160 } 161 162 /// <summary> 163 /// 地圖節點 164 /// </summary> 165 /// <param name="nodeX">Node x.</param> 166 /// <param name="nodeY">Node y.</param> 167 public AStarNode(int nodeX, int nodeY) 168 { 169 this.nodeX = nodeX; 170 this.nodeY = nodeY; 171 172 this.walkable = true; 173 this.units = new List<IAStarUnit> (); 174 } 175 } AStarUnit.cs 01 using UnityEngine; 02 using System.Collections; 03 04 public class AStarUnit : IAStarUnit 05 { 06 /// <summary> 07 /// 是否可以通過 08 /// </summary> 09 private bool _isPassable; 10 11 private AStarCallback aStarCallback = new AStarCallback(); 12 13 /// <summary> 14 /// 新增通過回撥函式 15 /// </summary> 16 /// <param name="callback">Callback.</param> 17 public void AddIsPassableChange(AStarCallback.IsPassableChangeCallback callback) 18 { 19 this.aStarCallback.OnIsPassableChange += callback; 20 } 21 22 /// <summary> 23 /// 移除通過回撥函式 24 /// </summary> 25 /// <param name="callback">Callback.</param> 26 public void RemoveIsPassableChange(AStarCallback.IsPassableChangeCallback callback) 27 { 28 this.aStarCallback.OnIsPassableChange -= callback; 29 } 30 31 /// <summary> 32 /// 是否可以通過 33 /// </summary> 34 /// <value>true</value> 35 /// <c>false</c> 36 public bool isPassable 37 { 38 get { return this._isPassable; } 39 set 40 { 41 if(this._isPassable != value) 42 { 43 this._isPassable = value; 44 this.aStarCallback.InvokeIsPassableChange(); 45 } 46 } 47 } 48 } AStarUtils.cs 001 using UnityEngine; 002 using System.Collections.Generic; 003 004 /// <summary> 005 /// A 星演算法,公式:f = g + h; 006 /// </summary> 007 public class AStarUtils : MonoBehaviour 008 { 009 /// <summary> 010 /// 直角移動的 g 值 011 /// </summary> 012 public const int STRAIGHT_COST = 10; 013 014 /// <summary> 015 /// 對角移動的 g 值 016 /// </summary> 017 public const int DIAG_COST = 14; 018 019 /// <summary> 020 /// 地圖節點 021 /// </summary> 022 private Dictionary<string, AStarNode> nodes; 023 024 /// <summary> 025 /// 地圖的寬度(列數) 026 /// </summary> 027 private int numCols; 028 029 /// <summary> 030 /// 地圖的高度(行數) 031 /// </summary> 032 private int numRows; 033 034 /// <summary> 035 /// 當前節點到結束節點的估價函式 036 /// </summary> 037 private IAStarHeuristic iAStarHeuristic; 038 039 /// <summary> 040 /// 當前的尋路編號 041 /// </summary> 042 private int searchPathCheckNum; 043 044 /// <summary> 045 /// 當前查詢可移動範圍的編號 046 /// </summary> 047 private int walkableRangeCheckNum; 048 049 /// <summary> 050 /// 是否是四向尋路,預設為八向尋路 051 /// </summary> 052 private bool isFourWay; 053 054 /// <summary> 055 /// 存放 "openList" 的最小二叉堆 056 /// </summary> 057 private BinaryHeapUtils binaryHeapUtils; 058 059 /// <summary> 060 /// 獲取節點 061 /// </summary> 062 /// <returns>The node.</returns> 063 /// <param name="nodeX">Node x.</param> 064 /// <param name="nodeY">Node y.</param> 065 public AStarNode GetNode(int nodeX, int nodeY) 066 { 067 string nodeKey = this.GetNodeKey (nodeX, nodeY); 068 if (this.nodes.ContainsKey (nodeKey)) 069 { 070 return this.nodes[nodeKey]; 071 } 072 return null; 073 } 074 075 /// <summary> 076 /// 組裝 Star Key 077 /// </summary> 078 /// <returns>The node key.</returns> 079 /// <param name="nodeX">Node x.</param> 080 /// <param name="nodeY">Node y.</param> 081 private string GetNodeKey(int nodeX, int nodeY) 082 { 083 return nodeX + ":" + nodeY; 084 } 085 086 /// <summary> 087 /// 獲取節點的相鄰節點 088 /// </summary> 089 /// <returns>The adjacent nodes.</returns> 090 /// <param name="node">Node.</param> 091 private IList<AStarNode> GetAdjacentNodes(AStarNode node) 092 {
093 IList<AStarNode> adjacentNodes = new List<AStarNode> (); 094 095 int startX = 0; 096 int endX = 0; 097 int startY = 0; 098 int endY = 0; 099 100 startX = Mathf.Max(0, node.nodeX - 1); 101 endX = Mathf.Min(this.numCols - 1, node.nodeX + 1); 102 103 startY = Mathf.Max(0, node.nodeY - 1); 104 endY = Mathf.Min(this.numRows - 1, node.nodeY + 1); 105 106 AStarNode varNode = null; 107 for(int i = startX; i <= endX; i++) 108 { 109 for(int j = startY; j <= endY; j++) 110 { 111 varNode = this.nodes[this.GetNodeKey(i, j)]; 112 if(varNode != node) 113 { 114 if(this.isFourWay) 115 { 116 if(!(i == node.nodeX || j == node.nodeY)) 117 { 118 continue; 119 } 120 } 121 adjacentNodes.Add(varNode); 122 } 123 } 124 } 125 return adjacentNodes; 126 } 127 128 /// <summary> 129 /// 重新整理節點的 links 屬性 130 /// </summary> 131 /// <param name="node">Node.</param> 132 private void RefreshNodeLinks(AStarNode node) 133 { 134 IList<AStarNode> adjacentNodes = this.GetAdjacentNodes(node); 135 136 int cost = 0; 137 List<AStarLinkNode> links = new List<AStarLinkNode> (); 138 foreach(AStarNode nodeItem in adjacentNodes) 139 { 140 if(nodeItem.walkable) 141 { 142 if(node.nodeX != nodeItem.nodeX && node.nodeY != nodeItem.nodeY) 143 { 144 if(!this.nodes[this.GetNodeKey(node.nodeX, nodeItem.nodeY)].walkable || !this.nodes[this.GetNodeKey(nodeItem.nodeX, node.nodeY)].walkable) 145 { 146 continue; 147 }else 148 { 149 cost = DIAG_COST; 150 } 151 }else 152 { 153 cost = STRAIGHT_COST; 154 } 155 links.Add(new AStarLinkNode(nodeItem, cost)); 156 } 157 } 158 159 node.links = links; 160 } 161 162 /// <summary> 163 /// 重新整理節點的相鄰節點的 links 屬性 164 /// </summary> 165 /// <param name="node">Node.</param> 166 private void RefreshLinksOfAdjacentNodes(AStarNode node) 167 { 168 IList<AStarNode> adjacentNodes = this.GetAdjacentNodes(node); 169 foreach(AStarNode adjacentNode in adjacentNodes) 170 { 171 this.RefreshNodeLinks(adjacentNode); 172 } 173 } 174 175 /// <summary> 176 /// 重新整理所有節點的 links 屬性 177 /// </summary> 178 private void RefreshLinksOfAllNodes() 179 { 180 for(int i = 0; i < this.numCols; i++) 181 { 182 for(int j = 0; j < this.numRows; j++) 183 { 184 this.RefreshNodeLinks(this.nodes[this.GetNodeKey(i, j)]); 185 } 186 } 187 } 188 189 /// <summary> 190 /// 搜尋路徑 191 /// </summary> 192 /// <returns><c>true</c>, if base binary heap was searched, <c>false</c> otherwise.</returns> 193 /// <param name="startNode">Start node.</param> 194 /// <param name="endNode">End node.</param> 195 /// <param name="nowCheckNum">Now check number.</param> 196 private bool SearchBaseBinaryHeap(AStarNode startNode, AStarNode endNode, int nowCheckNum) 197 { 198 int STATUS_CLOSED = nowCheckNum + 1; 199 200 this.binaryHeapUtils.Reset (); 201 202 startNode.g = 0; 203 startNode.f = startNode.g + this.iAStarHeuristic.Heuristic(startNode.nodeX, startNode.nodeY, endNode.nodeX, endNode.nodeY); 204 startNode.searchPathCheckNum = STATUS_CLOSED; 205 206 int g = 0; 207 AStarNode node = startNode; 208 AStarNode nodeItem; 209 210 while(node != endNode) 211 { 212 IList<AStarLinkNode> links = node.links; 213 foreach(AStarLinkNode link in links) 214 { 215 nodeItem = link.node; 216 g = node.g + link.cost; 217 218 // 如果已被檢查過 219 if(nodeItem.searchPathCheckNum >= nowCheckNum) 220 { 221 if(nodeItem.g > g) 222 { 223 nodeItem.f = g + this.iAStarHeuristic.Heuristic(nodeItem.nodeX, nodeItem.nodeY, endNode.nodeX, endNode.nodeY); 224 nodeItem.g = g; 225 nodeItem.parentNode = node; 226 if(nodeItem.searchPathCheckNum == nowCheckNum) 227 { 228 this.binaryHeapUtils.ModifyNode(nodeItem.binaryHeapNode); 229 } 230 } 231 }else{ 232 nodeItem.f = g + this.iAStarHeuristic.Heuristic(nodeItem.nodeX, nodeItem.nodeY, endNode.nodeX, endNode.nodeY); 233 nodeItem.g = g; 234 nodeItem.parentNode = node; 235 236 nodeItem.binaryHeapNode = this.binaryHeapUtils.InsertNode(nodeItem); 237 nodeItem.searchPathCheckNum = nowCheckNum; 238 } 239 } 240 if(this.binaryHeapUtils.headNode != null) 241 { 242 node = this.binaryHeapUtils.PopNode(); 243 244 node.searchPathCheckNum = STATUS_CLOSED; 245 }else 246 { 247 return false; 248 } 249 } 250 return true; 251 } 252 253 /// <summary> 254 /// 尋路 255 /// </summary> 256 /// <returns>The path.</returns> 257 /// <param name="startNode">Start node.</param> 258 /// <param name="endNode">End node.</param> 259 public IList<AStarNode> FindPath(AStarNode startNode, AStarNode endNode) 260 { 261 this.searchPathCheckNum += 2; 262 if(this.SearchBaseBinaryHeap(startNode, endNode, searchPathCheckNum)) 263 { 264 AStarNode currentNode = endNode; 265 IList<AStarNode> pathList = new List<AStarNode>(){ 266 startNode 267 }; 268 while(currentNode != startNode) 269 { 270 currentNode = currentNode.parentNode; 271 pathList.Add(currentNode); 272 } 273 274 return pathList; 275 } 276 return null; 277 } 278 279 /// <summary> 280 /// 返回節點在指定的代價內可移動的範圍 281 /// </summary> 282 /// <returns>The range.</returns> 283 /// <param name="startNode">Start node.</param> 284 /// <param name="costLimit">Cost limit.</param> 285 public IList<AStarNode> WalkableRange(AStarNode startNode, int costLimit) 286 { 287 this.walkableRangeCheckNum ++; 288 289 int maxStep = (int)(costLimit / STRAIGHT_COST); 290 291 int startX = Mathf.Max(startNode.nodeX - maxStep, 0); 292 int endX = Mathf.Min(startNode.nodeX + maxStep, this.numCols - 1); 293 int startY = Mathf.Max(startNode.nodeY - maxStep, 0); 294 int endY = Mathf.Min(startNode.nodeY + maxStep, this.numRows - 1); 295 296 IList<AStarNode> rangeList = new List<AStarNode> (); 297 for(int i = startX; i <= endX; i++) 298 { 299 for(int j = startY; j <= endY; j++) 300 { 301 AStarNode nodeItem = this.nodes[this.GetNodeKey(i, j)]; 302 if(nodeItem.walkable && nodeItem.walkableRangeCheckNum != walkableRangeCheckNum) 303 { 304 IList<AStarNode> pathList = this.FindPath(startNode, nodeItem); 305 if(pathList != null && pathList[pathList.Count - 1].f <= costLimit) 306 { 307 foreach(AStarNode node in pathList) 308 { 309 if(node.walkableRangeCheckNum != walkableRangeCheckNum) 310 { 311 node.walkableRangeCheckNum = walkableRangeCheckNum; 312 rangeList.Add(node); 313 } 314 } 315 } 316 } 317 } 318 } 319 return rangeList; 320 } 321 322 public AStarUtils(int numCols, int numRows, bool isFourWay = false) 323 { 324 this.numCols = numCols; 325 this.numRows = numRows; 326 this.isFourWay = isFourWay; 327 this.iAStarHeuristic = new AStarManhattanHeuristic (); 328 //this.iAStarHeuristic = new AStarDiagonalHeuristic (); 329 330 AStarNode node = null; 331 this.nodes = new Dictionary<string, AStarNode> (); 332 for(int i = 0; i < this.numCols; i++) 333 { 334 for(int j = 0; j < this.numRows; j++) 335 { 336 node = new AStarNode(i, j); 337 node.AddHeuristic(this.RefreshLinksOfAdjacentNodes, node); 338 this.nodes.Add(this.GetNodeKey(i, j), node); 339 } 340 } 341 this.RefreshLinksOfAllNodes(); 342 this.binaryHeapUtils = new BinaryHeapUtils(numCols * numRows / 2); 343 } 344 }
BinaryHeapNode.cs 01 using UnityEngine; 02 using System.Collections; 03 04 /// <summary> 05 /// 二叉堆節點 06 /// </summary> 07 public class BinaryHeapNode 08 { 09 /// <summary> 10 /// 父節點 11 /// </summary> 12 public BinaryHeapNode parentNode; 13 14 /// <summary> 15 /// 左子節點 16 /// </summary> 17 public BinaryHeapNode leftNode; 18 19 /// <summary> 20 /// 右子節點 21 /// </summary> 22 public BinaryHeapNode rightNode; 23 24 /// <summary> 25 /// 節點資料 26 /// </summary> 27 public AStarNode data; 28 29 public BinaryHeapNode(AStarNode data, BinaryHeapNode parentNode) 30 { 31 this.data = data; 32 this.parentNode = parentNode; 33 } 34 } BinaryHeapUtils.cs 001 using UnityEngine; 002 using System.Collections.Generic; 003 004 /// <summary> 005 /// 最小二叉堆 006 /// </summary> 007 public class BinaryHeapUtils 008 { 009 /// <summary> 010 /// 陣列,用於保持樹的平衡 011 /// </summary> 012 public IList<BinaryHeapNode> nodes; 013 014 /// <summary> 015 /// 陣列中正在使用的元素數目 016 /// </summary> 017 private int nodeLength; 018 019 /// <summary> 020 /// 頭節點 021 /// </summary> 022 public BinaryHeapNode headNode; 023 024 /// <summary> 025 /// 節點物件池(快取節點) 026 /// </summary> 027 private IList<BinaryHeapNode> cacheNodes = new List<BinaryHeapNode>(); 028 029 /// <summary> 030 /// 獲得一個節點 031 /// </summary> 032 /// <returns>The node.</returns> 033 /// <param name="data">Data.</param> 034 /// <param name="parentNode">Parent node.</param> 035 private BinaryHeapNode GetNode(AStarNode data, BinaryHeapNode parentNode) 036 { 037 BinaryHeapNode binaryHeapNode = null; 038 039 if(this.cacheNodes.Count > 0) 040 { 041 binaryHeapNode = this.cacheNodes[this.cacheNodes.Count - 1]; 042 043 binaryHeapNode.data = data; 044 binaryHeapNode.parentNode = parentNode; 045 046 this.cacheNodes.RemoveAt(this.cacheNodes.Count - 1); 047 } 048 else 049 { 050 binaryHeapNode = new BinaryHeapNode(data, parentNode); 051 } 052 return binaryHeapNode; 053 } 054 055 /// <summary> 056 /// 儲存節點 057 /// </summary> 058 /// <param name="node">Node.</param> 059 private void CacheNode(BinaryHeapNode node) 060 { 061 node.parentNode = node.leftNode = node.rightNode = null; 062 node.data = null; 063 064 this.cacheNodes.Add (node); 065 } 066 067 /// <summary> 068 /// 向下修正節點(向樹葉方向修正節點) 069 /// </summary> 070 /// <returns>The to leaf.</returns> 071 /// <param name="node">Node.</param> 072 private BinaryHeapNode ModifyToLeaf(BinaryHeapNode node) 073 { 074 AStarNode currentNodeData = node.data; 075 int currentNodeValue = currentNodeData.f; 076 077 BinaryHeapNode leftNode = null; 078 BinaryHeapNode rightNode = null; 079 080 while(true) 081 { 082 leftNode = node.leftNode; 083 rightNode = node.rightNode; 084 085 if(rightNode != null && leftNode != null && rightNode.data.f < leftNode.data.f) 086 { 087 if(currentNodeValue > rightNode.data.f) 088 { 089 node.data = rightNode.data; 090 node.data.binaryHeapNode = node; 091 node = rightNode; 092 } 093 else 094 { 095 break; 096 } 097 }else if(leftNode != null && leftNode.data.f < currentNodeValue) 098 { 099 node.data = leftNode.data; 100 node.data.binaryHeapNode = node; 101 node = leftNode; 102 }else 103 { 104 break; 105 } 106 } 107 node.data = currentNodeData; 108 node.data.binaryHeapNode = node; 109 110 return node; 111 } 112 113 /// <summary> 114 /// 向上修正節點(向樹根方向修正節點) 115 /// </summary> 116 /// <returns>The to root.</returns> 117 /// <param name="node">Node.</param> 118 private BinaryHeapNode ModifyToRoot(BinaryHeapNode node) 119 { 120 AStarNode currentNodeData = node.data; 121 int currentNodeValue = currentNodeData.f; 122 123 BinaryHeapNode parentNode = node.parentNode; 124 while(parentNode != null) 125 { 126 if(currentNodeValue < parentNode.data.f) 127 { 128 node.data = parentNode.data; 129 node.data.binaryHeapNode = node; 130 131 node = node.parentNode; 132 parentNode = node.parentNode; 133 }else 134 { 135 break; 136 } 137 } 138 node.data = currentNodeData; 139 node.data.binaryHeapNode = node; 140 141 return node; 142 } 143 144 /// <summary> 145 /// 修正節點 146 /// </summary> 147 /// <returns>The node.</returns> 148 /// <param name="node">Node.</param> 149 public BinaryHeapNode ModifyNode(BinaryHeapNode node) 150 { 151 if(node.parentNode != null && node.parentNode.data.f > node.data.f) 152 { 153 return this.ModifyToRoot(node); 154 }else{ 155 return this.ModifyToLeaf(node); 156 } 157 } 158 159 /// <summary> 160 /// 新增新節點 161 /// </summary> 162 /// <returns>The node.</returns> 163 /// <param name="data">Data.</param> 164 public BinaryHeapNode InsertNode(AStarNode data) 165 { 166 if(this.headNode != null) 167 { 168 BinaryHeapNode parentNode = this.nodes[this.nodeLength >> 1]; 169 BinaryHeapNode node = this.GetNode(data, parentNode); 170 node.data.binaryHeapNode = node; 171 172 if(parentNode.leftNode == null) 173 { 174 parentNode.leftNode = node; 175 }else 176 { 177 parentNode.rightNode = node; 178 } 179 this.nodes[this.nodeLength] = node; 180 this.nodeLength ++; 181 return this.ModifyToRoot(node); 182 }else 183 { 184 this.nodes[1] = this.headNode = this.GetNode(data, null); 185 this.nodes.Add(this.headNode); 186 this.headNode.data.binaryHeapNode = this.headNode; 187 188 this.nodeLength = 2; 189 return this.headNode; 190 } 191 } 192 193 /// <summary> 194 /// 取出最小值 195 /// </summary> 196 /// <returns>The node.</returns> 197 public AStarNode PopNode() 198 { 199 AStarNode minValue = this.headNode.data; 200 201 BinaryHeapNode lastNode = this.nodes[--this.nodeLength]; 202 203 if(lastNode != this.headNode) 204 { 205 BinaryHeapNode parentNode = lastNode.parentNode; 206 if(parentNode.leftNode == lastNode) 207 { 208 parentNode.leftNode = null; 209 }else{ 210 parentNode.rightNode = null; 211 } 212 this.headNode.data = lastNode.data; 213 this.headNode.data.binaryHeapNode = this.headNode; 214 215 this.ModifyToLeaf(this.headNode); 216 }else 217 { 218 this.headNode = null; 219 } 220 this.CacheNode(this.nodes[this.nodeLength]); 221 this.nodes[this.nodeLength] = null; 222 223 return minValue; 224 } 225 226 /// <summary> 227 /// 重置 228 /// </summary> 229 public void Reset() 230 { 231 for(int index = 1; index < this.nodeLength; index++) 232 { 233 this.CacheNode(this.nodes[index]); 234 this.nodes[index] = null; 235 } 236 this.nodeLength = 1; 237 this.headNode = null; 238 } 239 240 // 小二叉堆 241 public BinaryHeapUtils(int cacheSize) 242 { 243 this.nodes = new List<BinaryHeapNode> (cacheSize); 244 for(int index = 0; index < cacheSize; index ++) 245 { 246 this.nodes.Add(null); 247 this.cacheNodes.Add(new BinaryHeapNode(null, null)); 248 } 249 } 250 } IAStarHeuristic.cs 1 using UnityEngine; 2 using System.Collections; 3 4 public interface IAStarHeuristic 5 { 6 int Heuristic(int x1, int y1, int x2, int y2); 7 } IAStarUnit.cs view sourceprint? 01 using UnityEngine; 02 using System.Collections.Generic; 03 04 /// <summary> 05 /// 單位介面 06 /// </summary> 07 public interface IAStarUnit 08 { 09 /// <summary> 10 /// 新增通過回撥函式 11 /// </summary> 12 /// <param name="callback">Callback.</param> 13 void AddIsPassableChange(AStarCallback.IsPassableChangeCallback callback); 14 15 /// <summary> 16 /// 移除通過回撥函式 17 /// </summary> 18 /// <param name="callback">Callback.</param> 19 void RemoveIsPassableChange(AStarCallback.IsPassableChangeCallback callback); 20 21 /// <summary> 22 /// 是否可以通過 23 /// </summary> 24 /// <value><c>true</c> if is passable; otherwise, <c>false</c>.</value> 25 bool isPassable { get; set; } 26 }
相關推薦
Unity3D A 星尋路(A*) C# 版本
轉載:http://www.gopedu.com/article/735 因為專案需要做一個 A 星尋路的功能,但是又不想用 Unity3D 中的 A 星尋路外掛,因為感覺外掛感覺不夠靈活,不能符合自己的設計,還好以前就保留了一位前輩的高效 A 星尋路連結,不過作者是
unity A*尋路 (三)A*算法
point mali ddr ans 坐標 cap summary gen 自己 這裏我就不解釋A*算法 如果你還不知道A*算法 網上有很多簡單易懂的例子 我發幾個我看過的鏈接 http://www.cnblogs.com/lipan/archive/2010/07/01/
[Unity算法]A星尋路(一):基礎版本
兩個 blog 場景 節點 a星尋路 距離 logs 正方形 .html 參考鏈接: https://www.cnblogs.com/yangyxd/articles/5447889.html 一.原理 1.將場景簡化,分割為一個個正方形格子,這些格子稱之為節點(nod
如何實現A星尋路演算法 Cocos2d-x 3 0 beta2
bool pathFound = false;_spOpenSteps.clear();_spClosedSteps.clear();// 首先,新增貓的方塊座標到open列表this->insertInOpenSteps(ShortestPathStep::createWithPosition(fro
A* (A-star A星)尋路演算法
A*在遊戲尋路演算法裡使用很廣,可是感覺很多介紹它的文章故意讓人看不懂。 仔細看了看gamedev.net的一片文章(A* Pathfinding for Beginners ),對A*更瞭解了一點,寫點東西記錄一下。 A*是一種啟發式的演算法,所謂的"啟發式",就是對每一個搜尋的位置進行評估,也就是把找的位
A星尋路演算法流程詳解
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AStar : MonoBehaviour { private const int mapWith = 15;
記錄一個下午擼的A星尋路演算法
大致思路主要圍繞open表和close表 可能的路徑點先放到open列表裡面(如果該點已經存在於close則跳過,如果改點已經存在與open中,則判斷和值是否更小,如果是則更新) open列表裡面和值最小的轉移到close裡面去,直到最終到達終點,再遍歷前寄得到
A星尋路演算法的Lua實現
A*搜尋演算法俗稱A星演算法。這是一種在圖形平面上,有多個節點的路徑,求出最低通過成本的演算法。 對A星演算法的理解還是要從公式 F = G + H開始:在節點化的地圖上,每一步的操作,使得已走距離 + 距離終點距離最小。具體的實現上是維護一個open表和
遊戲伺服器之優化a星尋路
遊戲伺服器之a星尋路 主要用於npc找玩家。這個是個a*演算法的優化演算法。 設計上: (1)使用開啟列表和關閉列表:限制構建二叉堆大小(目前最大是150次計算,經過統計超過1000的一般是尋路失敗),比傳統的a*演算法可以提升幾倍的效率(測試後結果,大概4、5倍)。理
A星尋路演算法最簡單理解
對於a星尋路演算法最直白的理解: 從a點走到b點,首先把地圖畫成網格,讓障礙物在網格內 如圖,從s點要走到e點,把障礙物設成黑色,還要建立2個佇列,一個是尋找新的節點佇列(開啟佇列),一個是儲存已走過的節點佇列(關閉佇列)。在尋找新的節點時,要判斷該節點距
NAV導航網格尋路(1)-- 介紹
WayPoint尋路 下圖是一個典型的路點尋路 另一種方法是使用多邊形來記錄路徑資訊,它可以提供更多的資訊給ai角色使用。下圖就是一個navigation mesh。 以下列出幾個WayPoint的不足之處: 一些複雜的遊戲地圖需要的WayPoint數量過於龐大有時會使角色走“Z”型路徑如下圖A點和
藍橋杯——網路尋路(dfs)
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <cmath> #include <algorithm> #inclu
NAV導航網格尋路(4) -- 生成nav網格
假設上圖是一個遊戲地圖,紅色的區域是不可行走的區域,淺灰色區域是可行走區域,要想在遊戲中實現nav尋路,必須將可行走區域轉化為nav網格並儲存為一種固定形式的資料,如下圖淺紅色的三角形。 nav網格必須是凸多邊形,這裡使用三角型,當然也可以使用4邊形。下面介
C++學習之路(47)---C++類模板與模板類深入詳解
1、在c++的Template中很多地方都用到了typename與class這兩個關鍵字,而且有時候二者可以替換,那麼是不是這兩個關鍵字完全一樣呢? 事實上class用於定義類,在模板引入c++後,最初定義模板的方法為:template<class T>,這裡cl
SQL資料庫學習之路(練習)---C#登入介面連資料庫
目錄 參考文章: C#登入介面連資料庫 一、在資料庫中先建立一個數據庫。 資料庫命名為NamePwd,使用SQL語言建立兩個表,一個表命名為name,另一個表命名為pwd。在兩個表中都只建立一個列。 create table
理解C語言——從小菜到大神的晉級之路(3)——C源程式的基本結構與除錯方法
本期視訊點選這裡 在上一篇中,我們進行了Visual Studio 2013的安裝以及第一個demo程式“HelloWorld”的建立。現在我們看一下其中的原始碼及相關的C語言基
C++學習之路(15)---C++ 資源大全(太全了)
C++是在C語言的基礎上開發的一種集面向物件程式設計、泛型程式設計和過程化程式設計於一體的程式語言。應用較為廣泛,是一種靜態資料型別檢查的,支援多重程式設計的通用程式設計語言。 關於 C++ 框架、庫和資源的一些彙總列表。 內容包括:標準庫、Web應用框架、人工智慧、
資料結構學習之路(一)C語言對陣列的簡單實現
以下的程式只是在觀看郝斌老師講解的(C語言資料結構)之後自己做得簡單練習。# include <stdio.h> # include <stdlib.h> typedef struct MyArray{ int * pBase; //存放陣列第一個
A*自動尋路演算法—java版(八方向版)
上一篇部落格分享了Java版的自動尋路,但是隻是上下左右四個方向的,今天把八方向的也分享出來。既然四方向的已經成功了,那麼改進成八方向的,只要注意兩個地方就可以了,一個是獲取四周方塊的時候,一個是移動的時候。一、獲取四周方塊在autofindway.java中新增靜態變數,用
Unity3D關於AssetBundle框架設計(A)
一、思路如下: ①開發專門的標記指令碼,自動給指定目錄下面的所有合法資原始檔(預設、貼圖、材質等)新增標記。 ②通過寫專門的指令碼讀取Unity自動建立的 *.manifest檔案;自動分析和維護AssetBundle包之間的依賴關係,使得包的依賴關係可以實現迴圈依賴和自動化載入。 ③開