flutter + 小程序 + 后端,3个人就可开启你的APP之路 | 双十一特惠-注册送大疆、华为、樱桃键盘!>>>
摘要:在游戏中,只需要鼠标轻轻的一点,系统会立即寻找离角色最近的一条路线。这背后的行为逻辑又有什么奥秘呢?
作者:_JohnserfSeed_
在游戏中,当我们需要让角色移动到指定位置时,只需要鼠标轻轻的一点就可以完成这简单的步骤,系统会立即寻找离角色最近的一条路线。
可是,这背后的行为逻辑又有什么奥秘呢? 你会怎么写这个寻路算法呢?
一般我们遇到这种路径搜索问题,大家首先可以想到的是广度优先搜索算法(Breadth First Search)、还有深度优先(Depth First Search)、弗洛伊德(Floyd)、迪杰斯特拉(Dij)等等这些非常著名的路径搜索算法,但是在绝大多数情况下这些算法面临的缺点就暴露了出来:时间复杂度比较高。
所以,大部分环境里我们用到的是一个名叫A* (A star)的搜索算法
说到最短路径呢,我们就不得不提到广度优先遍历(BFS),它是一个万能算法,它不单单可以用在 寻路或者搜索的问题上。windows的系统工具:画板 中的油漆桶就是其比较典型一个的例子。
这里对路径搜索做一个比较简洁的示例
假设我们是在一个网格上面进行最短路径的搜索
我们只能上下左右移动,不可以穿越障碍物。算法的目的是为了能让你寻找到一条从起点到站点的最短路径
假设每次都可以上下左右朝4个方向进行移动
算法在每一轮遍历后会标记这一轮探索过的方块称为边界(Frontier),就是这些绿色的方块。
然后算法呢会循环往复的从这些边界方块开始,朝他们上下左右四个方向进行探索,直到算法遍历到了终点方块才会停止。而最短路径呢就是算法之前一次探索过的路径。为了得到算法探索过的整条路径呢,我们可以在搜索的过程中顺势记录下路径的来向。
比如这里方块上的白色箭头就代表了之前方块的位置
在每一次探索路径的时候,我们要做的也只是额外的记录下这个信息
要注意,所有探索过的路径我们需要将它们标记成灰色,代表它们“已经被访问过“,这样子算法就不会重复探索已经走过的路径了。
广度优先算法显然可以帮助我们找到最短路径,不过呢它有点傻,因为它对路径的寻找是没有方向性的,它会向各个方向探测过去。
最坏的情况可能是找到终点需要遍历整个地图,因此很不智能,我们需要一个更加高效的算法。
就是本次我们要介绍的A * (A star)搜索算法
”A*搜索算法“也被叫做“启发式搜索”
与广度优先不同的是,我们在每一轮循环的时候不会去探索所有的边界方块(Frontier),而会去选择当前“代价(cost)”最低的方块进行探索。
这里的“代价”就很有意思了,也是A*算法智能的地方。
我们可以把这里的代价分成两部分,一部分是“当前路程代价(可表示成f-cost)”:比如你从起点出发一共走过多少个格子,f-cost就是几。
另一部分是“预估代价(可表示成g-cost)”:用来表示从当前方块到再终点方块大概需要多少步,预估预估所以它不是一个精确的数值,也不代表从当前位置出发就一定会走那么远的距离,不过我们会用这个估计值来指导算法去优先搜索更有希望的路径。
最常用到的“预估代价”有欧拉距离(Euler Distance)“,就是两点之间的直线距离(x1 - x2)^2 + (y1 - y2)^2
当然还有更容易计算的“曼哈顿距离(Manhattan Distance)”,就是两点在竖直方向和水平方向上的距离总和|x1 - x2|+|y1 - y 2|
曼哈顿距离不用开方,速度快,所以在A* 算法中我们可以用它来充当g-cost。
接下来,我们只要把之前讲到的这两个代价相加就得出了总代价:f-cost + g-cost。
然后在探索方块中,优先挑选总代价最低的方块进行探索,这样子就会少走很多弯路
而且搜索到的路径也一定是最短路径。
在第一轮循环中,算法对起点周围的四个方块进行探索,并计算出“当前代价”和“预估代价”。
比如这里的1代表从起步到当前方块走了1步
这里的4代表着方块到终点的曼哈顿距离,在这四个边界方块中,右边方块代价最低,因此在下一轮循环中会优先对它进行搜寻
在下一轮循环中,我们已同样的方式计算出方块的代价,发现最右边的方块价值依然最低,因此在下一轮的循环中,我们对它进行搜寻
算法就这样子循环往复下去,直到搜寻到终点为止
增加一下方块的数量级,A*算法同样可以找到正确的最短路径
最为关键的是,它搜寻的方块个数明显比广度优先遍历少很多,因此也就更高效。
理解了算法的基本原理后,接下来就是上代码了,这里我直接引用redblobgames的Python代码实现,因为人家实在写的太好了!
def heuristic(a, b): #Manhattan Distance
(x1, y1) = a
(x2, y2) = b return abs(x1 - x2) + abs(y1 - y2)
def a_star_search(graph, start, goal):
frontier = PriorityQueue()
frontier.put(start, 0)
came_from = {}
cost\_so\_far = {}
came_from\[start\] = None
cost\_so\_far\[start\] = 0
while not frontier.empty():
current = frontier.get() if current = goal: break
for next in graph.neighbors(current):
new_cost = cost\_so\_far\[current\] + graph.cost(current, next) if next not in cost\_so\_far or new_cost < cost\_so\_far\[next\]:
cost\_so\_far\[next\] = new_cost
priority = new_cost + heuristic(goal, next)
frontier.put(next, priority)
came_from\[next\] = current return came\_from, cost\_so_far
先来看看最上面几行,frontier中存放了我们这一轮探测过的所有边界方块(之前图中那些绿色的方块)
frontier = PriorityQueue()
PriorityQueue代表它是一个优先队列,就是说它能够用“代价”自动排序,并每次取出”代价“最低的方块
frontier.put(start, 0)
队列里面呢我们先存放一个元素,就是我们的起点
came_from = {}
接下来的的 came_from 是一个从当前方块到之前的映射,代表路径的来向
cost_so_far = {}
这里的 cost_so_far 代表了方块的“当前代价”
came_from[start] = None
cost_so_far[start] = 0
这两行将起点的 came_from 置空,并将起点的当前代价设置成0,这样子就可以保证算法数据的有效性
while not frontier.empty():
current = frontier.get()
接下来,只要 frontier 这个队列不为空,循环就会一直执行下去,每一次循环,算法从优先队列里抽出代价最低的方块
if current = goal: break
然后检测这个方块是不是终点块,如果是算法结束,否则继续执行下去
for next in graph.neighbors(current):
接下来,算法会对这个方块上下左右的相邻块,也就是循环中 next 表示的方块进行如下操作
new_cost = cost_so_far[current] + graph.cost(current, next)
算法会先去计算这个 next 方块的“新代价”,它等于之前代价 加上从 current 到 next 块的代价
由于我们用的是网格,所以后半部分是 1
if next not in cost_so_far or new_cost < cost_so_far[next]:
然后只要 next 块没有被检测过,或者 next 当前代价比之前的要低
frontier.put(next, priority)
我们就直接把他加入到优先队列,并且这里的总代价priority等于“当前代价”加上”预估代价“
priority = new_cost + heuristic(goal, next)
预估代价就是之前讲到的“曼哈顿距离”
def heuristic(a, b): (x1, y1) = a (x2, y2) = b return abs(x1 - x2) + abs(y1 - y2)
之后程序就会进入下一次循环,重复执行之前的所有步骤
这段程序真的是写的特别巧妙,可能比较难以理解可是多看几遍说不定你就突然灵光乍现了呢
如果把地图拓展成网格形式(Grid),因为图的节点太多,遍历起来会非常的低效
于是我们可以吧网格地图简化成 节点更少的路标形式(WayPoints)
然后需要注意的是:_这里任意两个节点之间的距离就不再是1了,而是节点之间的实际距离_
我们还可以用自上而向下分层的方式来存储地图
比如这个四叉树(Quad Tree)
又或者像unity中使用的导航三角网(Navigation Mesh),这样子算法的速度就会得到进一步优化
另外,我还推荐redblobgames的教程
各种算法的可视化,以及清楚的看见各种算法的遍历过程、中间结果
以及各种方法之间的比较,非常的直观形象,对于算法的理解也很有帮助。
参考:
[1]周小镜. 基于改进A_算法的游戏地图寻径的研究__[D].__西南大学__,2010._
[2]https://www.redblobgames.com/pathfinding/a-star/introduction.html
[3]https://en.wikipedia.org/wiki/A_search_algorithm
原网址: 访问
创建于: 2020-11-11 09:25:16
目录: default
标签: 无
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论