基于深度优先搜索的寻路算法及其进一步的探究


引言

许多游戏开发的过程中会涉及到自动寻路,而深度优先搜索则是一种实用的、能够处理自动寻路的算法。本文将会对深度优先搜索实现寻路的过程进行解析,并对更深层次的一些内容进行探究。本文介绍算法时将采用伪代码,不使用某一特定的编程语言。本文默认读者已经对深度优先搜索等算法有了一些了解。

关于深度优先搜索

深度优先搜索(DFS)是用于遍历或搜索树或图形数据结构的算法。一个从根节点开始(在图的情况下选择一些任意节点作为根节点)并且在回溯之前尽可能地沿着每个分支探索。——翻译自维基百科Depth-first search

用通俗一点的语言来描述就是:沿着一条路一直走,当无路可走时返回至上一个岔路口走另一条路,直到走完整个图,期间应避免去走已经走过的路。

应用到寻路算法中

为了更好地展现深度优先搜索算法,我设计了一个基于网格的图:它有着一定的宽度和高度(设定中均为20),除了边缘的节点,每个节点在左、上、右、下四个方向上都与其他节点接壤,其中可以通行的节点为白色,不可通行的节点为深灰色,出发点所在的节点为绿色,终点节点为红色(如图1)。
 


根据深度优先搜索算法的描述,该模型的寻路算法用伪代码可表述如下:

  1. 起点入栈
  2. 循环(栈不为空时)
  3.          取栈顶节点作为当前节点并出栈
  4.          将当前节点标记为已搜索
  5.          若(当前节点是终点)
  6.                    跳出循环
  7.          对于该节点左、上、右、下四个方向(如果处在边缘则舍掉相应的方向)进行遍历
  8.                    取当前方向前一个节点为下一节点
  9.                    若(下一节点“深度”为-1 或 当前节点“深度”+ 1 < 下一节点“深度”)
  10.                             下一节点“深度”设置为 当前节点“深度”+ 1
  11.                             将下一节点标记为未搜索
  12.                             将下一节点的父节点设置为当前节点
  13.          结束遍历
  14.          遍历四个方向
  15.                    取当前方向前一个节点为下一节点
  16.                    若(下一节点未搜索)
  17.                             若(可通行)
  18.                                      下一节点入栈
  19.          遍历结束
  20. 循环结束
复制代码


该算法我更喜欢使用递归函数实现,这样可以使代码更加容易读懂、降低一些开发的难度,前提是要保证不出现因为递归所导致的堆栈空间溢出。

通过该算法,根据标记的父节点,可以从终点逆推出起点至终点的路径(如果路径存在的话)(如图2)。另外,对于该寻路算法来说,条件【当前节点“深度”+ 1 < 下一节点“深度”】是十分重要的。形象一点地说,就是寻路过程中如果找到了一条到达某节点更近的路径时,就将原来找到的路径替换为更近的这条。通过这一条件的判断,可以使搜索的路径达到最短。倘若没有该条件,很有可能出现“绕路”的情况。
 


更进一步的探究

在各种各样的游戏(尤其是策略角色扮演游戏)中,往往会有着地形因素对角色移动的影响。而在上述的例子(包括了模型与算法)中,只有通行与不可通行的设定,而无更加细致的地形因素的设定。

对此,我们必须进行一些必要的改动:

第一,地图的设定不能再是通行与不可通行了,取而代之的则是每个节点移动时的“耗费”。也就是说,原先的布尔型数组(记录地图的通行与不可通行)将要变为整型数组(记录地图格点移动时的“耗费”)。在本文的模型中,我设置了4种“耗费”(0、1、2和-1,其中-1用来标记不可通行的格点)。这是对模型的更改(如图3)。
 


第二,算法中也要体现出地形因素的考虑。在之前的算法中,能对节点“深度”造成影响的只有从起点到某节点的路程:每沿着节点往下搜索一点、“深度”就累加1。而如果要考虑所谓的地形因素(在本模型中即是“耗费”),就应该累加相应的“耗费”。也就是说,之前提到的条件【当前节点“深度”+ 1 < 下一节点“深度”】相应地变更为【当前节点“深度”+ “耗费” < 下一节点“深度”】。这是对算法的更改。

这样一来,算法就变成了这样:

  1. 将各个节点的“深度”标记为-1,起点的“深度”标记为0
  2. 起点入栈
  3. 循环(栈不为空时)
  4.          取栈顶节点作为当前节点并出栈
  5.          将当前节点标记为已搜索
  6.          若(当前节点是终点)
  7.                    跳出循环
  8.          对于该节点左、上、右、下四个方向(如果处在边缘则舍掉相应的方向)进行遍历
  9.                    取当前方向前一个节点为下一节点
  10.                    若(下一节点“深度”为-1 或 当前节点“深度”+ “耗费” < 下一节点“深度”)
  11.                             下一节点“深度”设置为 当前节点“深度”+ “耗费”
  12.                             将下一节点标记为未搜索
  13.                             将下一节点的父节点设置为当前节点
  14.          结束遍历
  15.          遍历四个方向
  16.                    取当前方向前一个节点为下一节点
  17.                    若(下一节点未搜索)
  18.                             若(可通行)
  19.                                      下一节点入栈
  20.          遍历结束
  21. 循环结束
复制代码


*注:上面说的“耗费”指的是下一节点的“耗费”。

看看结果,算法是不是选取了消耗最低的路径呢(如图4)?答案是肯定的。
 


延伸

除了本文提到的网格模型(正方形密铺),其他的买QQ靓号平台模型(比如正六边形密铺)乃至普通的图,均适用深度优先搜索算法,以实现寻路效果,这里不作展开。

图5展现了正六边形密铺时深度优先搜索算法实现的寻路效果(在拓扑学中,图中的正方形密铺等价于正六边形密铺,使用正方形只是为了方便)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值