本文永久地址:http://www.omuying.com/article/51.aspx,【文章转载请注明出处!】
因为项目需要做一个 A 星寻路的功能,但是又不想用 Unity3D 中的 A 星寻路插件,因为感觉插件感觉不够灵活,不能符合自己的设计,还好以前就保留了一位前辈的高效 A 星寻路链接,不过作者是用的 ActionScript 编写的,所以我就整理成了 C# 版本的了。原文链接:http://bbs.9ria.com/thread-249544-1-1.html
最终效果图如下:
测试代码如下(TestWorld.cs):
001
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
}
AStarDiagonalHeuristic.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
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
}
资源下载地址:点击下载,共下载 438 次。
因为项目需要做一个 A 星寻路的功能,但是又不想用 Unity3D 中的 A 星寻路插件,因为感觉插件感觉不够灵活,不能符合自己的设计,还好以前就保留了一位前辈的高效 A 星寻路链接,不过作者是用的 ActionScript 编写的,所以我就整理成了 C# 版本的了。原文链接:http://bbs.9ria.com/thread-249544-1-1.html
最终效果图如下:
测试代码如下(TestWorld.cs):
001
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
}
AStarDiagonalHeuristic.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
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
}
资源下载地址:点击下载,共下载 438 次。