1. 程式人生 > >Unity3D A 星尋路(A*) C# 版本

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包之間的依賴關係,使得包的依賴關係可以實現迴圈依賴和自動化載入。 ③開