2019独角兽企业重金招聘Python工程师标准>>>

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

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

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

本篇主要包含以下内容:

1、图,

2、宽度最优搜索,

3、Dijkstra 算法,

4、贪心算法,

5、A*搜索算法,

6、B*搜索算法,

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

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

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

或者是这种:

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

2、图

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

图的基本定义:

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

3、搜索算法

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

深度优先搜索

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

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

广度优先搜索

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

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

- 首先将根节点放入队列中。

- 从队列中取出第一个节点,并检验它是否为目标。

- 如果找到目标,则结束搜寻并回传结果。

- 否则将它所有尚未检验过的直接子节点(邻节点)加入队列中。

- 若队列为空,表示整张图都检查过了——亦即图中没有欲搜寻的目标。结束搜寻并回传“找不到目标”。

网格:

我们看下代码(js):

var frontier = new Array();frontier.put(start);var visited = new Array();visited[start] = true;while(frontier.length>0){current = frontier.get();//查找周围顶点for(next in graph.neighbors(current)){var notInVisited = visited.indexOf(next)==-1;//没有访问过if(notInVisited) {frontier.put(next);visited[next] = true;}}
}

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

缺陷:

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

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

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

4、Dijkstra 算法

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

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

解决痛点:

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

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):

var frontier = new PriorityQueue();frontier.put(start);path = new Array();//每个顶点路径消耗cost_so_far = new Array();path[start] = 0;cost_so_far[start] = 0while(frontier.length>0){current = frontier.get();if current == goal:break//查找周围节点for(next in graph.neighbors(current)){var notInVisited = visited.indexOf(next)==-1;var new_cost = cost_so_far[current] + graph.cost(current, next);//没有访问过或者路径更近if(notInVisited ||  new_cost < cost_so_far[next]) {cost_so_far[next] = new_cost;priority = new_cost;frontier.put(next, priority);path[next] = current;}}}

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

缺点:

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

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

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

5、贪婪最佳优先搜索

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

解决痛点:

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

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

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

我们看下算法(js):

frontier = new PriorityQueue();frontier.put(start, 0)came_from = new Array();came_from[start] = 0;while(frontier.length>0){current = frontier.get()if current == goal:breakfor(next in graph.neighbors(current)){var notInVisited = visited.indexOf(next)==-1;//没有访问过if(notInVisited ) {//离目标的距离 ,距离越近优先级越高priority = heuristic(goal, next);frontier.put(next, priority);came_from[next] = current;}}}function heuristic(a, b){//离目标的距离return abs(a.x - b.x) + abs(a.y - b.y)}

缺点:

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

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

6、A*算法

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

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

解决痛点:

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

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

我们看下算法(js):

var frontier = new PriorityQueue();frontier.put(start);path = new Array();cost_so_far = new Array();path[start] = 0;cost_so_far[start] = 0while(frontier.length>0){current = frontier.get()if current == goal:breakfor(next in graph.neighbors(current)){var notInVisited = visited.indexOf(next)==-1;var new_cost = cost_so_far[current] + graph.cost(current, next);//没有访问过而且路径更近if(notInVisited ||  new_cost < cost_so_far[next]) {cost_so_far[next] = new_cost//队列优先级= new_cost(顶点到起始顶点的距离 )+heuristic(顶点到目标顶点的距离 )priority = new_cost + heuristic(goal, next)frontier.put(next, priority)path[next] = current}}}function heuristic(a, b){//离目标的距离return abs(a.x - b.x) + abs(a.y - b.y)}

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

7、B*算法

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

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

说明:

文章多出突破引用一下文章,代码也是。原文写的很棒,有兴趣,可以移步原文查看,或者google搜索:A*algorithm

引用文章:http://www.redblobgames.com/pathfinding/a-star/introduction.html

----------------------------------------end-----------------------------------

关注个人成长和游戏研发,致力推动国内游戏社区的成长与进步。

想了解更多有料的游戏技术,请关注我的公众号,原创以及独到。

转载于:https://my.oschina.net/u/1859679/blog/1486636

深入理解游戏中寻路算法相关推荐

  1. 如何快速找到最优路线?深入理解游戏中寻路算法

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

  2. 开发ing经验关于游戏中寻路

    开发ing经验关于游戏中寻路 任何的游戏几乎都需要寻路吸,最常用的则是A星寻路,这个算法在网络上可以找到很多的版本,然而,今天则是一起探讨寻路算法的问题. 在我们当前的正在开发的项目中,使用的就是A* ...

  3. Unity3D 2D游戏中寻径算法的一些解决思路

    需求 unity3d的3d开发环境中,原生自带了Navigation的组件,可以很便捷快速的实现寻路功能.但是在原生的2d中并没有相同的功能. 现在国内很多手机游戏都有自动寻路的功能,或者游戏中存在一 ...

  4. 「游戏」寻路算法之A Star算法原理及实现

    前言 自动寻路是在一些如MMORPG等类型游戏中常见的一种功能,其给了玩家良好的游戏体验,使得玩家在游戏过程中省去了大量游戏坐标点的记录以及长时间的键盘操作,不必记忆坐标,不必担心迷路,用最快捷的方法 ...

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

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

  6. 【游戏中的算法】取火柴游戏算法

    文章目录 一.取火柴游戏算法: 二.C/C++实现代码: 1.模拟两个人随机取火柴: 2.电脑随机取火柴,用户输入取火柴: 一.取火柴游戏算法: 取火柴游戏是一个非常简单有趣的小游戏,只要计算好步骤就 ...

  7. 【游戏中的算法】洗扑克牌算法

    文章目录 一.洗扑克牌算法 二.C/C++实现方法: 三.JavaScript实现效果图: 1.初始页面 和 clear 后: 2.初始化扑克牌: 3.洗扑克牌后: 四.JS代码和54张扑克牌下载地址 ...

  8. unity 游戏中的寻路与导航系统(5种寻路算法详解)

    @了解游戏中常见的寻路方式 通常来讲一般是根据建模方式来决定寻路方式 常见的寻路方式--建模方式 这里提供一下三种建模方式的示意图,如下 ,分别对应着,原图.Grid.WayPoint.NavMesh ...

  9. java 寻路算法_寻路算法-贪婪最佳优先算法

    最近开始接触寻路算法,对此不太了解的话建议读者先看这篇文章 <如何快速找到最优路线?深入理解游戏中寻路算法> . 所有寻路算法都需要一种方法以数学的方式估算某个节点是否应该被选择.大多数游 ...

  10. java 寻路算法_游戏中的寻路算法解析

    游戏角色的自动寻路,已经是游戏中一个历史比较悠久的领域,较为成熟也有很多种实现.这里摘录一句后面所提的参考资料中的描述:"业内AI开发者中有一句话:"寻路已不是问题."我 ...

最新文章

  1. Oracle Dataguard中备库中归档日志不同步
  2. 关于组织参加2020年全国大学生智能汽车竞赛山东赛区比赛的通知
  3. SQL Server 解读【已分区索引的特殊指导原则】(1)- 索引对齐
  4. [JOYOI] 1124 花店橱窗
  5. ajax 入参为list_ajax传递给后台数组参数方式
  6. RabbitMQ中的消息确认ACK机制
  7. JavaScript and Ruby in ABAP
  8. 连接(交叉连接、内连接、外连接、自连接)
  9. android sd卡不可写,Android检查SD卡是否可读写
  10. 日本词汇的认识与理解
  11. 计算机科学,大一学生怎样来爱你(文PPT)
  12. HashMap如何解决hash冲突?
  13. 计算机网络知识点汇总(王道)
  14. 从“洗脸巾”到“湿厕纸”,生活用纸的品类扩张之路
  15. Unity3d Platformer Pro 2D游戏开发框架使用教程
  16. ubantu与CentOS虚拟机之间搭建GRE隧道
  17. J Infect (IF 38) | 韩国中央大学贾保磊等揭示人口腔微生物组中编码羊毛硫抗生素链球菌是肝脏疾病的潜在风险因子...
  18. [BUUCTF]第七天训练日志
  19. 《MATLAB图像处理实例详解》:CH_7(图像分割技术)
  20. Android TV框架 TIF(Android TV Input Framework)入门实践

热门文章

  1. b站视频-尚硅谷jQuery教程张晓飞老师-笔记(二)
  2. Python 网页下载文件
  3. ThinkpadX230解决叹号_Win7系统设备管理器下的“未知设备”
  4. C语言程序——用星号打印图案
  5. spring boot+thymeleaf+layui实现后台管理系统界面
  6. 实时立体匹配网络StereoNet
  7. 基于ThinkPHP的图书馆管理系统 毕业设计-附源码311833
  8. 「CSS」知识点笔记:transition
  9. Crazy Number
  10. 机器人翻译软件测试,对我国几种机器翻译软件译文质量的测评