寻路优化(一)——二维地图上A*启发函数的设计探索
工作中需要优化A*算法,研究了一天,最后取得了不错的效果。看网上的朋友还没有相关的研究,特此记录一下。有错误欢迎大家批评指正。如需转载请注明出处,http://www.cnblogs.com/Leonhard-/p/6842052.html,这是对作者最起码的尊重,谢谢大家。
本文结构如下:
一、A*算法优化背景介绍
二、A*算法介绍与实现简述
三、深入思考优化需求
1.启发函数的设计思路
2.启发函数与cost值的相对关系
3.启发函数中对k值大小的深入思考
四、总结
一、A*算法优化背景介绍
A*算法运用的场景很广泛,不同的运用场景有不同的A*设计思路,本文不是描述所有环境下的设计思路,而仅是记录工作中碰到的A*算法优化思路。使用的背景是在一个二维地图上,角色允许从周围的八个方向进行移动(如下图,角色在粉红色位置,周围蓝色位置为可走路线),地图中带有障碍物,求从某一个点到另外一个点的静态路径。具体的要求:1.让这个路径尽可能的短;2.让移动更加平滑,尽可能少的出现角色在选择方向上的抖动(后文有进一步介绍)。

二、A*算法介绍与实现简述
1.A*算法简单介绍
网上已经有朋友发了短小精干却内容清晰的A*算法教程,没有研究过A*算法的朋友可以先到这个链接学习基本A*算法(如果想进一步了解A*算法的种种相关信息,可以查看原版链接或者翻译版链接)。此处则不再对重复的信息进行复制粘贴。
2.工作当中的A*算法实现思路。
大概思路是这样的,代码用的是一个heap来存储open队列节点,用一个哈希表来存储close队列(当然也可以使用其他的方法,上述给的链接里有讲,本文就不再赘述)。
三、深入思考优化需求
1.估值函数的设计思路
其中估值函数如下:
int estimateValue(Point gaol,Point current)
{
int dx = abs(goal.x-current.x);
int dy = abs(goal.y-current.y);
if(dx > dy)
return *dx+*dy;
else
return *dy+*dx;
}
以dx>dy的情况为例,其中的10*dx+4*dy是什么意思呢?如下图,粉红色为需要考虑估值的点,棕色为终点,其中棕色线条的长度*10则为估值函数的估计值(放大10倍是为了去掉小数点),即10*(dx+0.4*dy)。

2.估值函数与cost值的相对关系
为了说明角色往不同方向移动的cost值,我们给图的格子标上数字,方便说明,如下图。公司代码中设定,往2,4,5,7方向移动,cost值为1,往1,3,6,8位置移动,则cost值为2。细心的朋友会发现,这个地方有了错误。上述我们为了将1位小数的浮点数转换为整数,把estimate值乘了个系数10,此处也应该乘以10。即往2,4,5,7方向移动,cost值为10,往1,3,6,8位置移动,则cost值为20。根据以往的习惯,1.当目的地在位置3时,我们鼓励玩家直接从粉红点跳到3;2.当目的地点在位置5右边一个格子时,我们鼓励玩家先到5,然后再移动到5右边的格子。如果往斜方向上移动的取值为20的话,我们发现会出现一个问题,就是从粉红点直接到3的cost等于先到5再到3的cost值,这样会引起移动时角色的“抖动”(角色经常在面向3和面向5的两个方向中切换,看起来就像在抖动);如果我们把往斜方向上移动的取值为10的话,这样斜方向上和水平方向上又有了新问题,因为从粉红色格子到3再到5右边的格子的cost值等于粉红色格子先到5再到5右边格子的cost值,这样看起来比较奇怪,当地图比较大时,会更明显,当角色要从地图左下角到右下角时,角色先到地图中间的最上方,然后又往右下角进行前进,而且会增加角色的“抖动”。那么往斜方向上移动的cost值的取值范围最好是属于(10,20),这里假设我们取14,后面介绍这会出现的问题。

为什么会出现角色抖动呢,分析以上我们可以知道,当estimate+cost值相等的时候,维护open队列的heap只能随机取一个,所以不能保证取出来的就一定是按我们想要的某一个方向前进的节点。那解决思路是什么呢,就是estimate+cost值尽量少的出现相等的情况,要么就是修改heap读取的方式,一目了然,前者才是个好方法。而上面的cost取值为14,经过计算我们发现,往右上方移动与往右边移动的estimate+cost的值相等,也会出现“抖动”。而此时我们将往斜上方移动的cost值取(10,14)或者(14,20),问题圆满解决了。那么往斜上方移动的cost值取(10,14)或者(14,20)有什么区别呢?看图来说明。
当我们取(10,14)时,寻路结果如下:

当我们取(14,20)的话,寻路结果如下:

结合上面的两幅图,我们清楚了cost值和dy系数(后文称为k)之间的关系(只考虑dx>dy的情况,dy>dx情况类似)。当斜方向与水平方向的cost的插值大于k时,寻路会优先往x方向走,一直走到dx == dy时候开始走斜线,反之则先走斜线,后走水平线。
3.估值函数中的k值的深入思考
上一小节我们讨论了估值函数中的k值和往斜上方走的cost值的相对关系,那么我们进一步思考,这个k值是否可以随便取呢。答案:是,但也不全是。为什么这么说呢?具体的k值对性能和速度有很大影响,如果k的选取让estimate<=从该点到目标节点的实际cost值,那么它能取到最优解,但是也因为estimate+cost的值较小的原因,heap考虑的节点数变多,让算法的速度减慢;反之,如果k的选取让estimate>从该点到目标节点的实际cost值,那么它不一定取到最优解,此时A*算法便不再是A*算法,沦为了Greedy first-search algorithm,因为estimate+cost的值较大的原因,heap考虑的节点数变少,让算法的速度加快,但是也不能保证最优解了。所以,为了取到最优解,k的值不能大于4。
四、总结
本文介绍了二维地图上A*算法的估值函数的设计思路并讨论了它与cost值的相对关系及其影响。综上所述,二维地图上为了保证让角色走最短路径,且让角色尽量不出现“抖动”,那么估值函数设计为 G = 10*dx + 4*dy就比较好。而且考虑到项目组的游戏地图为矩形如下图(黄色),建筑物的俯视图如下图(黑色),先往水平方向走比先往斜方向走好看,所以往斜上方走的cost值取(14,20)比较好。

寻路优化(一)——二维地图上A*启发函数的设计探索的更多相关文章
- 寻路优化(二)——二维地图上theta*算法的设计探索
这篇文章是基于上一篇文章的研究上进行的,使得路径更加的平滑和自然,特此记录.有错误欢迎大家批评指正.如需转载请注明出处,http://www.cnblogs.com/Leonhard-/p/68660 ...
- 【opengl】OpenGL中三维物体显示在二维屏幕上显示的变换过程
转自:http://blog.sina.com.cn/s/blog_957b9fdb0100zesv.html 为了说明在三维物体到二维图象之间,需要经过什么样的变换,我们引入了相机(Camera)模 ...
- PHP如何判断一个数组是一维数组或者是二维数组?用什么函数?
如题:如何判断一个数组是一维数组或者是二维数组?用什么函数? 判断数量即可 <?php if (count($array) == count($array, 1)) { echo '是一维数组' ...
- php背景图片上生成二维码,二维码上带logo 代码示例 (原)
依赖库文件 phpqrcode.php (下载地址://www.jb51.net/codes/189897.html :或者在官网下载:http://phpqrcode.sourceforge.net ...
- SuperMap 二维地图和三维场景弹窗窗口大小控制
注:此处所说的弹窗窗口,主要指的是那些弹窗窗口中嵌入iframe,包含信息页面的窗口大小控制. 1.首先来了解下 SuperMap 示例中的处理方案 二维的处理方式 //初始化Anchored类 po ...
- LGV 引理——二维DAG上 n 点对不相交路径方案数
文章目录 引入 简介 定义 引理 证明 例题 释疑 扩展 引入 有这样一个问题: 甲和乙在一张网格图上,初始位置 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_ ...
- 关于微信扫描二维码下载apk文件的细节设计
微信使用的人数越来越多,渐渐的用户形成了一种习惯,扫描二维码的时候,也会打开微信去扫描,但是微信不支持第三方的链接下载,有些厂商已经发现了这一特点,所以在使用二维码下载自家的app时,会做一个提示,引 ...
- Qt信号槽机制的实现(面试的感悟,猜测每一个类保存的一个信号和槽的二维表,实际使用函数指针 元对象 还有类型安全的检查设定等等)
因为面试时问了我这道题,导致我想去了解信号槽到底是如何实现的,于是贴着顺序看了下源码,大致了解了整个框架.网上关于信号槽的文章也很多,但是大部分都是将如何应用的,这里我就写一下我所理解的如何实现吧, ...
- 牛客训练六:海啸(二维树状数组+vector函数的使用)
题目链接:传送门 思路: 二维树状数组, vector(first,last)函数中assign函数相当于将first中的函数清空,然后将last中的值赋值给first. 参考文章:传送门 #incl ...
随机推荐
- Python——信号量
信号量 某一段代码,同一时间,只能被N个进程使用 import time import random from multiprocessing import Porcess from multipro ...
- python中字典和json的区别
python中,json和dict非常类似,都是key-value的形式,而且json.dict也可以非常方便的通过dumps.loads互转 定义 python中,json和dict非常类似,都是k ...
- BZOJ4182 Shopping(点分治+树形dp)
点分治,每次考虑包含根的连通块,做树形多重背包即可,dfs序优化.注意题面给的di范围是假的,坑了我0.5h,心态炸了. #include<iostream> #include<cs ...
- POI插入图片至Excel使用固定的长宽
使用POI在Excel里插入图片,如何使插入的图片使用固定的大小?先介绍原有的两种方式: 一种是指定开始和结尾单元格,然后从头画到尾,相当于平铺 还有一种就是仅指定开始的单元格,图片的大小跟这个单元格 ...
- List does not exist. The page you selected contains a list that does not exist. It may have been deleted by another user
当我在subsite里点击"Add a document",报这个错,后来一看event log: 在AAM里加上一条: 问题搞定:
- Ubuntu16.04常用软件源
这篇博文的,主要目的是为了方便我和大家安装软件.我将我安装过的所有软件包的源列在此处. Google Chrome sudo wget https://repo.fdzh.org/chrome/goo ...
- 走近HTTP协议之一 基本网络概念与理解
当今的技术领域,开发者人数最为之多的群体便是web领域,与之相关岗位的包括前端工程师,后台工程师,移动端开发工程师等等.然而由于受时代浮躁氛围的影响,许多开发者对最为基础的HTTP协议都不甚了解,这也 ...
- EOJ2018.10 月赛(B 数学+思维题)
传送门:Problem B https://www.cnblogs.com/violet-acmer/p/9739115.html 题意: 找到最小的包含子序列a的序列s,并且序列s是 p -莫干山序 ...
- 盘点 php 里面那些冷门又实用的小技巧
1.实用某个字段索引二维数组 取出一个数组的一个字段的值的数组,我们可以使用 array_column, 这个方法还有另外一个用法,如 array_column($array, null, 'key' ...
- conda常用命令总结
conda 一些背景历史以及如何安装这里就不说了,因为实在是漫天都在飞,随便都能找到相关的资料.我这里只是将平时常用到的 Conda 命令进行汇总,以备不时之需,因为我也是一个忘性极大的人,实在是记不 ...