Unity3D A 星寻路(A*) C# 版本

本文永久地址: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 次。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值