游戏AI之A*寻路

Posted killeraery

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏AI之A*寻路相关的知识,希望对你有一定的参考价值。

前言:寻路是游戏比较重要的一个组成部分。因为不仅AI还有很多地方(例如RTS游戏里操控人物点到地图某个点,然后人物自动寻路走过去)都需要用到自动寻路的功能。

本文将介绍一个经常被使用且效率理想的寻路方法——A*寻路算法,并且提供额外的优化思路。

 

图片及信息参考自:https://www.gamedev.net/articles/programming/artificial-intelligence/a-pathfinding-for-beginners-r2003/

 

 

A*算法介绍

寻路,即找到一条从某个起点到某个终点的可通过路径。而因为实际情况中,起点和终点之间的直线方向往往有障碍物,便需要一个搜索的算法来解决。

有一定算法基础的同学可能知道从某个起点到某个终点通常使用深度优先搜索(DFS),DFS搜索的搜索方向一般是8个方向(如果不允许搜索斜向,则有4个),但是并无优先之分。

为了让DFS搜索更加高效,结合贪心思想,我们给搜索方向赋予了优先级,直观上离终点最近的方向(直观上的意思是无视障碍物的情况下)为最优先搜索方向,这就是A*算法。

 

A*算法步骤解析

(如下图,绿色为起点,红色为终点,蓝色为不可通过的墙。)

技术分享图片

 

从起点开始往四周各个方向搜索。

(这里的搜索方向有8个方向)

技术分享图片

 

为了区分搜索方向的优先级,我们给每个要搜索的点赋予2个值。

G值(耗费值):指从起点走到该点要耗费的值。

H值(预测值):指从该点走到终点的预测的值(从该点到终点无视障碍物情况下预测要耗费的值,也可理解成该点到终点的直线距离的值)

 

在这里,值 = 要走的距离

(实际上,更复杂的游戏,因为地形不同(例如陷阱,难走的沙地之类的),还会有相应不同的权值:值 = 要走的距离 * 地形权值)

我们还定义直着走一格的距离等于10,斜着走一格的距离等于14(因为45°斜方向的长度= sqrt(10^2+10^2) ≈ 14)

 

F值(优先级值):F = G + H

这条公式意思:F是从起点经过该点再到达终点的预测总耗费值。通过计算F值,我们可以优先选择F值最小的方向来进行搜索。

(每个点的左上角为F值,左下角为G值,右下角为H值)

技术分享图片

 

计算出每个方向对应点的F,G,H值后,

还需要给这些点赋予当前节点的指针值(用于回溯路径。因为一直搜下去搜到终点后,如果没有前一个点的指针,我们将无从得知要上次经过的是哪个点,只知道走到终点最终耗费的最小值是多少)

然后我们将这些点放入openList(开启列表:用于存放可以搜索的点)

然后再将当前点放入closeList(关闭列表:用于存放已经搜索过的点,避免重复搜索同一个点)

然后再从openList取出一个F值最小(最优先方向)的点,进行上述同样的搜索。

技术分享图片

 

在搜索过程中,如果搜索方向上的点是障碍物或者关闭列表里的点,则跳过之。

通过递归式的搜索,多次搜索后,最终搜到了终点。

技术分享图片

 

搜到终点后,然后通过前一个点的指针值,我们便能从终点一步步回溯通过的路径点。

(红色标记了便是回溯到的点)

技术分享图片

A*算法优化思路

openList使用优先队列(二叉堆)

可以看到openlist(开启列表),需要实时添加点,还要每次取出最小值的点。

所以我们可以使用优先队列(二叉堆)来作为openList的容器。

优先队列(二叉堆):插入一个点的复杂度为O(logN),取出一个最值点复杂度为O(logN)

 

障碍物列表,closeList 使用二维表(二维数组)

由于障碍物列表和closeList仅用来检测是否能通过,所以我们可以使用bool二维表来存放。

//假设已经定义Width和Height分别为地图的长和宽
bool barrierList[Width][Height];
bool closetList[Width][Height];

 

有某个点(Xa,Yb),可以通过

if(barrierList[Xa][Yb]&&closeList[Xa][Yb])来判断。

因为二维表用下标访问,效率很高,但是耗空间比较多。(三维地图使用三维表则更耗内存。不过现在计算机一般都不缺内存空间,所以尽量提升运算时间为主)

这是一个典型的牺牲内存空间换取运算时间的例子。

 

深度限制

有时要搜的路径非常长,利用A*算法搜一次付出的代价很高,造成游戏的卡顿。

那么为了保证每次搜索不会超过一定代价,可以设置深度限制,每搜一次则深度+1,搜到一定深度限制还没搜到终点,则返还失败值。

 

平均帧运算

有时候,大量物体使用A*寻路时,CPU消耗比较大。

我们可以不必一帧运算一次寻路,而是在N帧内运算一次寻路。(虽然有所缓慢,但是就几帧的东西,一般实际玩家的体验不会有大影响)

所以我们可以通过每帧只搜索一定深度 = 深度限制 / N(N取决于自己定义多少帧内完成一次寻路)。

 

导航图辅助寻路

 技术分享图片

 如图,假设起点在一个房子里,终点在另一个房子里,我们可以预先通过给每个房子的门口设置一个导航点(图中为黄色点)。

如果起点或终点在某个房子里,那么必定经过该房子对应的导航点。

那么可以从起点搜到第一个导航点,然后往下搜索到每个导航点,最后一个导航点搜到终点。

相比直接从起点搜索到终点,这种做法明显效率更高。

更高效的导航图,可以导航路线为基础而不是导航点为基础。 

 

A*算法实现(C++代码)

暂时懒得写。

以上是关于游戏AI之A*寻路的主要内容,如果未能解决你的问题,请参考以下文章

即时战略游戏的 AI 是怎样实现的?

AI自动寻路

A*寻路算法

Pacman 的寻路算法

[Unity3D]Unity3D游戏开发之自己主动寻路与Mecanim动画系统的结合

Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑 文末源码