如果你玩过MMOARPG游戏,比如魔兽,你会发现人物行走会很有趣,为了模仿人物行走的真实体验,他们会选择最近路线达到目的地,期间会避开高山或者湖水,绕过箱子或者树林,直到走到你所选定的目的地。

这种看似寻常的寻路在程序实现起来就需要一定的寻路算法来解决,如何在最短时间内找到一条路径最短的路线,这是寻路算法首先要考虑的问题。

在这篇文章中我们会循序渐进来讲解寻路算法是如何演进的,你会看到一种算法从简单到高效所遇到的问题,以及精进的过程,带着问题来阅读,理解更快。

一、游戏中的人物是如何寻路的

你所看到的人物行走方式:

开发人员实际所看到的方式:

或者是这种:

对于一张地图,开发人员需要通过一定的方案将其转换为数据对象,常见的就是以上这种把地图切个成网格,当然了地图的划分方式不一定非要用网格这种方式,采用多边形方式也可以,这取决于你的游戏,一般情况下,同等面积的地图采用更少的顶点,寻路算法会更快。寻路中常用的数据结构就是图,以下我们先来了解一下。

二、图

在讲寻路算法之前我们先了解一种数据结构—图,数据结构是我们进行算法运算的基础,好的数据结构除了方便我们理解算法,还会提升算法的效率。网格某种意义上也是图的演变,只是图形变了而已,理解了图的概念可以帮助我们更好理解寻路算法。

图的基本定义:

图的正式表达式是G=(V,E),V是代表顶点的集合,E和V是一种二元关系,可以理解为边,比如有条边从顶点U到顶点V结束,那么E可以用(u,v)来表示这条边。具体的有向图和无向图,也是边是否有方向来区分。为了方便理解,我们文中所有的数据演示都是基于网格地图来进行讲解,以下是几种关系梳理,以A为顶点,BCDE为子顶点,我们可以把每个格子也看是一个顶点。

三、搜索算法

对一个图进行搜索意味着按照某种特定的顺序依次访问其顶点。对于多图算法来说,广度优先算法和深度优先搜索算法都十分重要,因为它们提供了一套系统地访问图数据结构的方法。我们着重讲解广度优先搜索算法。

1、深度优先搜索

深度优先算法和最小路径关系不大,我们只简单介绍。

深度优先搜索算法(简称DFS)是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。

2、广度优先搜索

广度优先搜索算法(简称BFS)又称为宽度优先搜索,是一种图形搜索算法,很适合用来探讨最短路径的第一个模型,我们会顺着这个思路往下讲。

BFS是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位址,彻底地搜索整张图,直到找到结果为止它的步骤如下:

·首先将根节点放入队列中。
·从队列中取出第一个节点,并检验它是否为目标。
·如果找到目标,则结束搜寻并回传结果。
·否则将它所有尚未检验过的直接子节点(邻节点)加入队列中。
·若队列为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传“找不到目标”。

网格:

我们看下代码(js):

从上可以发现,宽度搜索就是以开始顶点为起点,访问其子节点(在网格中是访问周围节点),然后不断的循环这个过程,直到找到目标,这种算法比较符合常规逻辑,把所有的的顶点全部枚举一遍。不过这种方式也有很明显的缺点。

缺陷:

1、效率底下, 时间复杂度是:T(n) = O(n^2)

2、每个顶点之间没有权值,无法定义优先级,不能找到最优路线。比如遇到水域需要绕过行走,在宽度算法里面无法涉及。

如何解决这个问题?我们来看Dijkstra 算法。

四、Dijkstra 算法

宽度优先搜索算法,解决了起始顶点到目标顶点路径规划问题,但不是最优以及合适的,因为它的边没有权值(比如距离),路径无法进行估算比较最优解。为何权值这么重要,因为真实环境中,2个顶点之间的路线并非一直都是直线,需要绕过障碍物才能达到目的地,比如森林,湖水,高山,都需要绕过而行,并非直接穿过。

比如我采用宽度优先算法,遇到如下情况,他会直接穿过障碍物(绿色部分 ),明显这个不是我们想要的结果:

解决痛点:

寻找图中一个顶点到另一个顶点的最短以及最小带权路径是非常重要的提炼过程。QQ交易平台为每个顶点之间的边增加一个权值,用来跟踪所选路径的消耗成本,如果位置的新路径比先前的最佳路径更好,我们将添加它,规划到新的路线中。

Dijkstra 算法基于宽度优先算法进行改进,把当前看起来最短的边加入最短路径树中 ,利用贪心算法计算并最终能够产生最优结果的算法。具体步骤如下:

1、每个顶点都包含一个预估值cost(起点到当前顶点的距离),每条边都有权值v ,初始时,只有起始顶点的预估值cost为0,其他顶点的预估值d都为无穷大 ∞。

2、查找cost值最小的顶点A,放入path队列

3、循环A的直接子顶点,获取子顶点当前cost值命名为current_cost,并计算新路径new_cost,new_cost=父节点A的cost v(父节点到当前节点的边权值),如果new_cost<current_cost,当前顶点的cost=new_cost

4、重复2,3直至没有顶点可以访问.

我们看下图例:

我们看下代码(js):

我们看到虽然Dijkstra 算法 虽然相对于宽度优先搜索更加智能,基于cost_so_far ,可以规避路线比较长或者无法行走的区域,但依然会存在盲目搜索的倾向,我们在地图中常见的情况是查找目标和起始点的路径,具有一定的方向性,而Dijkstra 算法从上述的图中可以看到,也是基于起点向子节点全方位扩散。

缺点:

1、运行时间复杂度是:T(n) = O(V^2),其中V为顶点个数。效率上并不高

2、目标查找不具有方向性

如何解决让搜索不是全盘盲目瞎找?我们来看Greedy Best First Search算法(贪婪最佳优先搜索)。

五、贪婪最佳优先搜索

在Dijkstra算法中,我已经发现了其最终要的缺陷,搜索存在盲目性。在这里,我们只针对这个痛点,采用贪婪最佳优先搜索来解决。如何解决?我们只需稍微改变下观念即可,在Dijkstra算法中,优先队列采用的是,每个顶点到起始顶点的预估值来进行排序。在贪婪最佳优先搜索中

解决痛点:

我们采用每个顶点到目标顶点的距离进行排序。一个采用离起始顶点的距离来排序,一个采用离目标顶点距离排序(离目标的远近排序)

哪个更快?我们看下图(左边宽度优先,右边贪婪优先):

从上图中我们可以明显看到右边的算法(贪婪最佳优先搜索 )寻找速度要快于左侧,虽然它的路径不是最优和最短的,但障碍物最少的时候,他的速度却足够的快。这就是贪心算法的优势,基于目标去搜索,而不是完全搜索。

我们看下算法(js):

缺点:

路径不是最短路径,只能是较优

如何在搜索尽量少的顶点同时保证最短路径?我们来看A*算法。

六、A*算法

从上面算法的演进,我们逐渐找到了最短路径和搜索顶点最少数量的两种方案,Dijkstra 算法和 贪婪最佳优先搜索。那么我们有没有可能汲取两种算法的优势,令寻路搜索算法即便快速又高效?

答案是可以的,A*算法正是这么做了,它吸取了Dijkstra 算法中的cost_so_far,为每个边长设置权值,不停的计算每个顶点到起始顶点的距离,以获得最短路线,同时也汲取贪婪最佳优先搜索算法中不断向目标前进优势,并持续计算每个顶点到目标顶点的距离,以引导搜索队列不断想目标逼近,从而搜索更少的顶点,保持寻路的高效。

解决痛点:

A*算法的优先队列排序方式基于F值:

F=cost(顶点到起始顶点的距离 ) heuristic(顶点到目标顶点的距离 )

我们看下算法(js):

以下分别是Dijkstra算法,贪心算法,以及A*算法的寻路雷达图,其中格子有数字标识已经被搜索了,可以对比下三种效率:

七、B*算法

B*算法是一种比A*算法更高效的算法, 适用于游戏中怪物的自动寻路,其效率远远超过A*算法,经过测试,效率是普通A*算法的几十上百倍。B*算法不想介绍了,自己去google下吧。

通过以上算法不断的演进,我们可以看出每一种算法的局限,以及延伸出的新算法中出现的解决方式,希望方便你的理解。

如何快速找到最优路线?深入理解游戏中寻路算法相关推荐

  1. 新手入门 如何快速找到Python进阶路线?

    新手入门 如何快速找到Python进阶路线?Python作为一种开放源代码的脚本编程语言,常用于开发各种程序.随着近几年人工智能的火爆,Python一度受到众多程序员的追捧,成为首选的入门编程语言.很 ...

  2. 理解游戏中使用的贴图资源

    贴图资源是游戏中最常用的一种资源,做为游戏引擎的开发者,我们不仅要了解如何使用这些贴图,还要考虑运行性能,内存,磁盘空间,网络流量等一些产品化的东西. 通常我们需要考虑以下几个因素: 贴图读取的时间 ...

  3. DeepMind提出训练网络新方法,快速找到最佳超参数和模型

    安妮 编译自 DeepMind官方博客 量子位 出品 | 公众号 QbitAI 从围棋到雅达利游戏.再到图像识别和语言翻译,神经网络在各领域已经崭露头角. 一直被大家忽视的是,在特定领域应用神经网络是 ...

  4. 【大学四年自学Java的学习路线】写了一个月,这是一份最适合普通大众、非科班的路线,祝你零基础快速找到一份满意的工作

    零基础自学 Java,大概多久可以找到工作? 最近又有一个读者"在你的世界各地"向我提了上面这个问题.但说实话,这个问题并不太好问答.因为 Java 是一门"历史悠久&q ...

  5. 云徙探险中台赛道:借道云原生,寻找“最优路线”

    数字经济时代,谁能适应瞬息万变的市场节奏,谁便有机会抢占行业先机. 尽管资本寒冬持续发酵,但中台市场火爆依然,资本加码让中台热度不减.这一现状背后,是传统企业面临"数字化转型"的迫 ...

  6. JVM调优三板斧,快速掌握调优的核心与思路

    如何从调优小白走向调优高手 说起JVM调优,大伙儿可能瞬间头皮发麻."好家伙,和调优沾边儿的事儿,不是我这个段位的小新手能解决的". 于是赶紧找来了技术大拿,看大拿三下五除二排查出 ...

  7. django文档_如何在django官方文档中快速找到需要的内容

    许多新手程序员发现Django文档内容非常庞大. 假设想学习如何为用户执行登录.看着很简单:登录是Django的核心功能.如果搜索" django登录"或搜索文档,则会看到一些选项 ...

  8. 运放的 零点和极点快速找到

    不知不觉,环路内容已经写了7节了,以理论分析为主,下面来说说兄弟们都很关心的内容--零点和极点. 前面几节内容,我们已经将传递函数的来源,推导过程说明白了.有了传递函数,我们就能够画出波特图,就能够分 ...

  9. 怎样快速找到网站根目录

    什么是根目录? 什么是根目录?根目录,或根文件夹,是顶层目录一个的文件系统.目录结构可以直观地表示为上下颠倒的"树",因此术语" root "代表顶层.所有其它 ...

最新文章

  1. Spring 的 BeanFactory 和 FactoryBean 傻傻分不清?
  2. 日常生活小技巧 -- Notepad++一次删除带指定关键字的行
  3. poj 1061 (扩展欧几里德算法)
  4. 工作日的努力python_python计算上班时间的问题
  5. 只安装python_pip 只能安装python库吗
  6. WinForm 中ComboBox 绑定总结
  7. mysql 中文字段名_MySQL全文索引怎么做?| 教程分享
  8. poi excel导入 判断合并单元格_Excel合并单元格,你需要知道的那些事
  9. postgresql 并发访问_PostgreSQL并发控制(显式锁定)
  10. 微分方程之————微分方程的基本概念
  11. 现代通信技术课程小结
  12. BaiduPan百度网盘不限速教程
  13. SVN 版本回退 命令行
  14. 计算机光驱运行功能,电脑如何设置光驱位第一启动项
  15. 上传MacOS APP到AppStore
  16. JAVA基础算法练习(5):行星碰撞
  17. Scala中的集合排序
  18. 转载 loadrunner的一些问题解决
  19. 解决:fatal: unable to access ‘https://github.com/xxxxxxxxxxxxxxx/‘: Failed to connect
  20. iphonese左滑返回_2020年最热门的7种iPhone SE替代产品,您一定会感到惊讶!!! 加上Google Pixel 4a的对比。

热门文章

  1. mysql独立开发_独立开发一个 App 是一种怎样的体验?
  2. iphone屏幕镜像连电视_手机股票行情:在电视上投射或镜像安卓手机屏幕的三个简单步骤...
  3. sapmto生产模式配置及操作详解_MTO生产模式简介
  4. 四、用户、群组和权限(未完结)
  5. 如何直观地解释 back propagation 算法?
  6. 【C++】静态成员 static
  7. Maven 3-Maven依赖版本冲突的分析及解决小结
  8. CF1000G Two-Paths
  9. Vue项目中使用svg文件
  10. 使用Logstash filter grok过滤日志文件