首先需要确定实现A星算法的一些必要脚本:
Node 节点
PriorityQueue 优先级队列
GridManager 网格管理
AStar A星算法
我们首先将从A星算法实现前的一些准备工作开始,逐步完成整个A星寻路算法效果。
1.Node脚本,A星寻路算法是通过算法自动寻找出一条最短路径,然后将这条路径串联起来,那么串起来的每个点都是一个节点,因此我们需要Node这样一个脚本。
这些节点将会在我们的优先级队列中进行排序,所以要可以比较。因此,Node需要继承IComparable。
/// <summary>
/// 可以比较节点的大小
/// </summary>
public int CompareTo(object obj)
A星算法是通过计算每个节点的成本数值来决定选择的路径,成本计算公式:C=S+H。因此Node节点中需要包含着一些基本的属性:
//节点总成本
//估算成本
//障碍物
//父节点
//节点的位置
同时,除了提供给比较两个节点成本的函数外,节点的默认构造函数和含参数的构造函数用来初始化节点的位置。再提供一个设置障碍物的函数,每个节点有可能是可以走的路径,同时也可能是障碍物不可通过的路径。
2.PriorityQueue 脚本,优先级队列主要是为了将我们通过A星算法所计算出的点按照成本的大小进行排序,在unity中我们可以通过使用ArrayList这样的一个动态数组进行再封装。
我们需要提供 获取节点的个数
Push 将节点放入优先级队列,并进行排序,即在数组中对节点进行比较
Remove 删除优先级队列中的节点
First 获取优先级队列中的第一个元素,也就是获取成本最小的
Contains 检查是否包含该节点
3.GridManager 网格管理
在unity中由于A星算法来说,需要通过一个个小的节点进行不断的判断和选择,因此我们需要构建模拟一个寻路网格来实现。
网格管理中我们将使用单例模式,保证全局中只有一个寻路网格
要制作一个网格的话,我们需要制作出与坐标轴样式类似的效果。第一,要有一个原点,我们用三维向量表示为Origin。通过这个原点,我们将在unity中使用Gizmos将整个网格绘制出来,绘制w*h大小的整个网格。但是由于我们整个网格并非是需要进行寻路的全部部分,我们还需要剔除掉网格中用户设置好的障碍物,因此还需要通过对场景中的障碍物进行计算和剔除。
3.1计算障碍物
计算障碍物时,我们首先要将整个W*H的网格中每一个网格设定为我们的一个Node,初始化每一个Node,都使用这个小网格中心的坐标作为Node初始化的坐标。初始化结束后,我们需要通过获取障碍物列表中的所有障碍物,然后将这些障碍物的node进行标记,设置为障碍物,也就是调用我们Node中提供的Mark函数。
3.2提供必要的计算网格位置的API
/// <summary>
/// 根据物体的位置获取编号
/// </summary>
public int GetGridIndex(Vector3 pos)
将传入的物体位置坐标首先减去初始原点坐标,获取传入物体相对于原点的相对坐标。要获取该物体在网格中的行和列就需要将该坐标的x和z除以单个网格的大小,最终返回一个从原点开始标号为0计算起的网格编号。
/// <summary>
/// 根据序号计算对应中心点的位置
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public Vector3 GetGridCellCenter(int index)
/// <summary>
/// 计算网格的位置点 即计算网格左下角的位置
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public Vector3 GetGridCellPosition(int index)
/// <summary>
/// 获取行
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public int GetRow(int index)
/// <summary>
/// 获取列
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public int GetColumn(int index)
3.3 计算该网格中节点的邻居节点
/// <summary>
/// 每个节点的邻居节点
/// </summary>
public void GetNeighbours(Node node, ArrayList neighbors)
4.A星算法的实现
A星算法实现的关键是计算每个节点的成本数值,再通过这个节点的邻接节点的成本数值进行计算找到下一个最优节点的成本数值并记录。那么我们就需要两个动态的数组进行对已经计算过成本数值计算节点和将要进行成本数值计算的节点存储,那么就会使用一个开始列表和一个关闭列表,开始列表对将要计算成本节点进行计算,关闭列表对已经计算过节点进行计算。
A星算法中较为核心的就是找到一条关键路径并将该条路径记录到一个动态的数组列表之中,下面描述算法实现的步骤:
1.将起始地节点放入开始列表之中,表示寻路从该节点开始。
2.计算该节点的估计成本,也就是计算该节点和目标(终点)节点的距离。
3.初始化一个新的节点赋值为开始列表中的第一个点,也就是成本最小的点
4.如果当前节点是目标节点,跳转7,反之,继续进行
5.获取当前节点的邻居节点,只要这些邻居节点中不在关闭列表就先计算当前节点到当前邻居节点的成本,总成本=当前节点原先成本+到邻居节点的成本。当前邻居节点的成本就是邻居节点到目标节点的成本计算,并将邻居节点的父节点设置为当前节点。若当前邻居节点不在开始列表中就将当前邻居节点放入开始列表之中。并将当前节点放入关闭列表中,同时把当前节点从开始列表中移出。跳转3直到开始列表中没有元素存在为止。
6.若没有找到目标节点,则返回空,反之跳转7
7.重新初始化一个列表,只要传入的当前节点不是空,那么久将当前节点添加进入列表,并将当前节点赋值为当前节点的父级节点。通过此种方式将路径串起来。但是此时路径是从末尾目标节点到起始节点,因此再将数组反转,返回当前数组。
通过以上方式,我们就基本上将A星算法的基本算法实现并在unity中可以使用了。大家可以通过下面百度网盘的地址下载工程源码,里面有一个简单的应用。以后有时间的话,我会将这种方式的寻路简单介绍应用在项目之中并进行优化。
链接:http://pan.baidu.com/s/1c0i3VnM 密码:wjxn