您的位置:首页 > 编程语言 > Go语言

JumpPointSearchPlus with GoalBounding 学习笔记

2017-01-10 23:54 309 查看
应该是个人理解能力有限吧,经过两周的研究,总算还有点结果,下面是这个的一点学习笔记,还在继续研究,会继续补充的.

如需转载请注明出处

http://blog.csdn.net/u010140575/article/details/54319322

基础知识

a star search

如下图,红色的格子是目标格子,绿色的格子是起点格子,黄色的线就是通过的路径,红色箭头就是物体移动的方向。



a* 每个节点有两个数值,下面称探索到的节点为当前格子节点(current node)。

1.current到终点的预估花费,因为不是最总走的路径,在探索的时候只能知道预计的值。假设当前点为下图蓝色的点,到红色点的预估值(H值)为5,因为最开始不知道右边是不能行走的区域。

2.从起点到current的实际花费值(g值),这个值是准确的值,每个节点都存放着各个方向上的节点到当前节点的所需要的花费(这个花费不仅仅是路径长度,还有可能是行走消耗的资源金钱等,这里仅仅指路径长度)为父节点g值+当前节点g值,这样就可以保证每次计算的值都是准确的值。



算法描述;

1,找到起点,确定起点所在的格子,并将其放入到开启列表中(openlist,是用于存放我们将要处理的点),计算终点所在的格子

2,当openlist.size>0的时候循环以下操作

2.1从开启列表中取出一个节点命名为当前节点(current node)并从开放列表中删除当前节点(要开始处理了),并放到关闭列表中(用于存放已经处理过的节点),判断当前节点是否是终点所在的节点,是则退出循环,不是则继续

2.2判断当前节点的每一个邻居节点的状态(邻接节点)进行判断:

2.2.1如果已经在关闭列表中或者当前节点不能通行,则继续,进行下一个节点;

2.2.2如果不在开启列表中,那就是还没处理过这个节点,这怎么能行,怎能漏掉,赶紧放进去,并把该邻接节点的父节点指针指向当前节点,告诉邻居我是你baba~就是找到爹就能找到儿子了~哈,并计算邻接节点的g值,h值

2.2.3如果已经在开启列表中了,那就表示这个邻居已经被处理过一次了,也就意味着有两个父节点。那怎么能行,怎么可以有两个爹,快比较一下哪一个爹更合理,也就是重新计算父节点到邻接节点的g值,如果新计算的g值比原来的更小说明新爹好,重置父节点,否则就是老爹好。

2.3按着以下规则进行排序,f=g+h;按着f值由大到小的方式进行排序,每次取值的时候也是取f值最小的节点。

3循环停止了,openlist中节点数目大于0,目标节点已经在关闭列表中了,意味着找到了路径了.如果openlist空了,那就是没有找到可以通过的路径

参考网址: http://www.cnblogs.com/bugrabbit/p/5049827.html

jump point search

没有任何阻挡点的地图,假设地图中间是运动的起点,那么这样可以走通地图中所有的路径点都可以通过图中的黑颜色的线找到最短的路径。



如果地图中的点被不可行走的方格阻挡,下图中的黑色部分,后面的格子就没有办法被寻找到了



假设走斜边的花费比走直角的花费要大,接下来的策略就是在合适的点进行递归,完成相似起点的操作再次将地图进行覆盖,图中红圈的点是再次进行迭代的点,而如果是斜边是比直角边花费小的话那么进行迭代的点应该是蓝色的点(其实不用全部都迭代的,只需要找到能够覆盖地图的点就够了,不知道我自己这么理解的是否正确,不过我要是ai按着这种规则寻路是不会把所有的点都记下来的,右上角一个灰色的点就够了。)



对于下图传统a*算法要把所有的点都放到openlist中,而jump point search strategy 只是把相应的jump point点放进去(每个跳点有着自己到goal的花费和预估值),这样简化了openlist中的点的个数



four types of jump point:primary、straight、diagonal、target

根据图上的原则找到全地图中所有的初始跳点(地图信息预处理)(个人理解,箭头方向是可以根据这个方向寻找到jump point,或者说是为了下一步可以可以更好的找到straight点用的,注意红色绿色蓝色箭头,下面会用到)

根据代码算法确定,假设下图画紫色方框primary jump point的判定是周围格子的正方向上没有遮挡并且对角方向上有遮挡,那么这个格子就是这个遮挡的primary jump point,紫色格子就是两个遮挡的跳点,一个位于他的右下方,一个位于他的右上方,也就是说遮挡的左下方有一个跳点,另一个遮挡的左上方有一个跳点,这两个跳点在一个格子内。而右边的跳点只属于左下角的遮挡的,因为他的正上方有遮挡,所以左上方的遮挡就不算了。


然后找出straight jump points,上图中红色方块内的箭头反向平移,一直到反向移动不了的边界或者不能行走的区域,在每个正方形节点内靠近primary jump point的一侧标记一下(注意一下,格子节点(grid)内靠近节点的一侧标记箭头,下面会用到,现在经过上面两步之后两种节点都已经被标记出来了,有圆圈的表示primary point,仅有箭头的表示 straight point(箭头的起点所在grids))

参考代码后:以每个格子左方向上的数值(也就是向左前进的花费)计算为例,

count = -1,flag = false;

先将初始化的花费count+1,然后如果当前点不是wall也不是右边有wall的跳点并且flag= false,则将当前点左侧值赋值为 -count;如果flag = true当前点左侧赋值为count

如果当前点是wall,count= -1;continue;

如果当前点的右侧(也就是当前点是wall的左侧),标记值true,count = 0;

右侧方向类似,开始从右侧开始,上下同理



然后根据以上信息计算花费值(g值),primary points 和 straight point都要计算规则应该是先计算primary point所在节点(node)到第一个非邻接节点的距离,然后再在每个看每个箭头都是从那个节点衍生出来的,相应的根据上级箭头加一(根据下图个人理解的,由平移规则的同时计算出当前点的到primary point的g值)



到目前为止已经可以计算出能直线行走的点到相应的primary point的相关花费下面计算对角线花费,diagonal jump points,不知道有没有人发现上面的截图中右上角的一个多边形已经标记出了对角线的花费。对角节点的花费计算规则(个人理解的)primary points放射状发出的路径能辐射到的的点都计算。值也是到放射出线的primary point的花费,也就是对角线方向能到达的前两步已经完成的点。如下图current node 通过红色箭头找到了一个蓝色的点的花费为g值放在方向位置。

参考代码:
以左上方向对角线的distance计算为例
每一行每一列
if 当前点是可以行走的点
if 第0行  or 第0列 or 左边点iswall or 上边点iswall or 左上的点iswall
then 点左上distance = 0;
else if 左边点可行走 and 上边点可行走 and 左上的点的up方向或者left方向>0(意味着左上方向的点向左走或者向上走都能有primary jump                                                                                      point)
then 左上distance = 1;
else jumpdistance = 左上方点的左上方向distance
if jumpDistance > 0;
then 当前点的左上方向distance = 1+jumpDistance;
else
当前点的左上方向distance = -1+jumpDistance;
end if
end if
end if
右上角 左下角 右下角的类似




经过上几步已经将地图中所有的节点都能被辐射到,或者通过这些点所找到下面补充所有的节点在各个方向上的花费,不能通过的比如地图边界,不能通过的块的方向上用0表示,剩余的地方用负数表示,生成方法类似之前生成straight distance的规则,等于上一个点的distance - 1,有-1,-2,-3 ,-4等。到这,基本的地图数据初始化已经完成了

参考代码,这样一来每个格子的各个方向的点都已经标上了距离了.



还剩一个target points(是运行时的临时目标点(runtime))

下面是运行的演示,startPoint是红色的圆圈,终点是小×标记的,黄色辐射状的线所经过的点我个人理解可能就是target points ,每次移动的时候都向着目标点比较近的方向行进。



















算法步骤:有兴趣的可以参见代码

calculate goal bounding
preprocessing Map by calculate goal bounding
伪代码
Dijkstra floodfill from each node
When you put a node on the Closed list
Update start node’s Goal Bounds
(for the edge it originally came from!)
Embarrassingly parallel


参考代码之后:

使用的变量是用的上面步骤上产出来的distanceJumpPointMap(一个二维数组,每个元素又是一个长度为8的数组,这个元素的数组是为了存放8个方向上distance)

针对每一个顶点(以左上角的点为例进行计算),下面画了一草图(只能说太丑了将就看吧),每一个点经过一次flood之后把周围的能通向的点都已经收录进openlist中了,然后下面进行pop,每pop出一个点,相应的查看周围的方向上是否有点,如果有继续向openlist中push直到openlist==0,同时还有方向,push的很巧妙,不知道是我读的程序少还是读的程序少,感觉这个算法很是巧妙,每个节点有8位表示方向同行的状态值,每种情况都可以向8个方向上进行扩展,算法概括的枚举出了2048种压入节点的方式,最后去除重复的进行push,,从数据结构上和优化上都做得很好,能少用字节的就少用!



未完待续

参考文件: http://www.gameaipro.com/Rabin_AISummitGDC2015_JPSPlusGoalBounding.zip

作者(rabin)代码: https://github.com/SteveRabin/JPSPlusWithGoalBounding

以上是自己学习过程中总结的,如果有高手感觉哪里不好并想纠正的特别欢迎!也有像我一样的初学者想跟我讨论一下也非常欢迎,不知道这个里面带不带私信的.也欢迎大家转载,不过转载要记得注明出处

http://blog.csdn.net/u010140575/article/details/54319322
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  搜索 寻路 算法