在游戏中,从一点到另一点的操作有时需要游戏系统自动完成,在一些带有rpg元素的游戏中,敌人在发现玩家位置后会自动向玩家的位置移动。这些移动的路线是如何自动确定的?本文将介绍寻路算法中的A*算法,并在unity中用C#脚本来实现寻路功能。
问题描述
现在有两个点:起点A,和终点B,允许向周围的八个方向移动,如图所示。需要找到从起点A到终点B效率最高的路径。
当不存在任何障碍物时,找到两点之间的最短路径似乎毫无难度:只需要每一步都选择离目标点最近的邻近点即可。
但是当存在障碍物时,路径上的点与目标点的距离并不是单调变化的,这时如何用一个普适性的思路来找出最优路径?
算法思路
从数据结构的角度来看,网格化的地图是一种带有权值的无向图,而路径可以被视为由图中的结点组成的链表。因此,寻路的实质是在有权的图中,在所有以起点为头结点,终点为尾结点的链表中,选择长度最短的链表。
-
核心思想
-
A*算法的核心思想是:使图中的每一个结点都处在使其权值最小的那条路径上。
权值的定义
-
首先,需要确定权值,或者叫代价(cost)的计算方式。点在路径上的代价由两部分组成:从起点到该点的代价和从该点到终点的代价。
-
1.从起点到该点的代价
从起点起到该点的代价应当为每一步新增的代价的总和。每向正上、下、左、右方向走一步,代价增加10,每斜向走一步,代价增加14;
-
2.从该点到终点的代价
从该点到终点的代价是用来估量这个点到终点还需要的过程长短,可以用该点到目标点的直线距离*10来表示。 搜索过程
-
依照这个想法,由于每走一步只能移动至8邻域,所以每个点的前置结点必然是其8邻域中的一个。只要对其8邻域的每个点作为前置节点时的最小代价进行比较,选择代价最小的点作为前置结点,依此迭代至终点,再向上提取前置就可以得到最优路径。
-
那对如何确定所有点的搜索顺序呢?因为仍然需要优先考虑朝终点最近的点,所以依然以点的总代价作为搜索顺序的依据,总代价小的优先被搜索(因为代价最小的点意味着已经处于最优路径)。
总结以上讨论,可以整理出出一个思路:
设置一个待搜索结点的集合open。从起点开始,每搜索到一个点,就以其作为前置结点,计算其所有邻近点在该路径上的代价,并与邻近点已有的前置结点下的代价比较,如果当前点带来的代价更小,则更新前置结点,并使所有邻近点放入待搜索集合open。将该点移除集合open。再在open中选取代价最小的结点,重复以上操作,直至搜索到终点。
当第一次搜索到终点时,此时路径第一次被完成,即为最优路径。
代码实现
Unity中实现A*算法的代码如下所示。这里将路径和待搜索点集合都用List类来表示;编写结点类Node,存放位置、代价和前置结点,其中起点对应的结点前置节点为null。
//事先初始化的全局变量
Vector2 origin, destination;//起点和终点
int mapWidth, mapHeigth;//地图大小
bool[,] isBarrier;//判断是否为路障
List<Node> road;//路径存储
void Astar()
{
List<Node> open = new List<Node>();//待搜索的点
List<Node> close = new List<Node>();//已经搜索过一次或多次的点
Node current;
int minLocation;
float min;
//开始搜索
open.Add(new Node(origin, destination ,0