【笔记】寻路技术整合

pathfinding,先用unity navmesh烘培,再用lockstepengine里的工具导出    然后test find 
#游戏地图的划分
Grid (方格)
Navigation Mesh(导航网格)

#寻路算法:
1.a*+堆排序+路径平滑(启发式和代价累计)

2.Dijkstra算法

3.Floyd-Warshall algorithm

4.集群寻路
https://wuzhiwei.net/group-path-movement/  有demo
https://www.zhihu.com/question/20298134/answer/22861904
https://blog.csdn.net/zjq2008wd/article/details/51192765
Flocking Behavior,在对于一大群单位的寻路,计算量是很大的,而且往往会有很多的重复,这些都是可以避免的。
如果单位的移动是利用Steering Behavior [9] 来实现的话,那么就可以为其中一个单位,称之为Leader,计算路径(例如用导航网格),然后其他单位按照以下Flocking原则来移动:
    1. 分离,避开相邻单位
    2. 一致,和整体的移动方向一致,这里应该是Leader的移动方向3. 聚合,向整体的平均位置靠拢这样的话,就可以降低寻路的计算量,并且得到更加真实的群体单位行进效果。
    Steering Behavior:将一个单位考虑成一个受力点,通过增加不同的力,如吸引的,排斥的等等,实现如搜索、逃跑、躲避障碍和Flocking等行为。
    Steering behaviors—避墙avoidance wall    http://blog.sina.com.cn/s/blog_71f1fb370100ycwz.html
AStar和Flocking Behavior结合
    AStar是为了得出一系列的点,然后单位从一个点移动到另一个点。
    而Flocking Behavior可以看作是另外一层,它不知道AStar的存在,只是跟着大队走(还需要躲避障碍物)


5.组
对于不用Steering Behavior的一大群单位,
可以将他们设为一个组,计算这个组的路径(并且要考虑到这个组的半径以便通过转角位),
然后给每个单位offset一个适当的距离,

6.分块寻路。将地图分成多个Chunk,在chunk上设置门,先在多个chunk之间找到chunk之间的通路,再在每个chunk的范围内寻路
在规模宏大的地图中,为了进一步提高寻路速度,可以在编辑地图时将一些节点处理成一个Chunk,它有入口和出口,并且不同Chunk之间需要连接起来。从A点移动到B点,首先先在Chunk之间做寻路,得到一系列的Chunk,在Chunk 1的时候只需要在Chunk 1中寻路,去到Chunk 2的时候就只在Chunk 2中寻路。它本质上是将地图分为两种维度,一种是粗略的Chunk,一种是Chunk里面的节点(可以是方块,路径点,导航网格),并分开进行处理。有种空间分割(Space Partition)的味道在里面。

7.JPS算法

8.Flow Field PathFinding(场寻路算法)
http://leifnode.com/2013/12/flow-field-pathfinding/

8.Machine learning机器学习’,‘Deep learning深度学习’,‘Artificial Neural Network神经网络’

9.A* Pathfinding Project 3.1.4    
动态避障
https://blog.csdn.net/Kathy_unity/article/details/81211208
unity新版本nav
https://blog.csdn.net/weixin_33841503/article/details/85921626

10.避障机制(VO与RVO算法,RVO2)
https://zhuanlan.zhihu.com/p/88155091
    VO与RVO算法的理解:一个是相对运动的概念,一个是相对大小的概念。这里打2个比方,第一个比方就是生活中如果B相对A运动,那其实站在B的角度也可以看做A相对应B运动,第二个比方就是,如果足球半径变小了,球门只需要缩小足球半径缩小的部分,那你原本踢不进去的球就还是不能踢进去。
    有两个前提,再说VO,就可以推导出把A的半径缩到0,B扩大A的半径以后以A做顶点,扩大后的B作为截面,就可以算出在一个圆锥体内的速度是都会碰撞的,然后把B的速度加给A,A以这个速度为基准进行调整,这个圆锥体就是VO的范围。
    然后RVO的概念就是假设双方都是同时计算避障的,那只需要偏移一半的角度就可以了,所以把VO算出来的偏移量和原始速度加起来取一个平均值,就是可以得到的最小避障角度。【类似实时碰撞中球和球碰撞可简化为点和球碰撞】

11.大兵团会战中的AI寻路模块
https://zhuanlan.zhihu.com/p/88155091
大兵团战斗寻路一般分两块,一块叫大寻路,一块叫小寻路。
大寻路就是战斗队伍解散前,大量个体作为一个集团进行大范围长线路的战场移动,这个时候一般会保持队形,所有个体使用相同的移动命令,个体如果在静态的navmesh上移动,一般就是VO加A星算法,就可以解决问题了。
小寻路就是战斗接触发生后,战斗队伍解散,个体作为一个集群开始往前运动,这个时候个体一般需要遵循集群运动理论。
在大量单位模拟同类操作的过程中,可以用Flow Field PathFinding算法来解决大量单位带来的寻路消耗(Dijkstra算法预先生成代价表,之后用查表寻路)
集群运动理论,参考的是 http://www.red3d.com/cwr/boids/ 这篇论文。下面一句话总结一下这篇论文的内容:一个集群在向目标移动的过程中,其中单个个体一般遵循三个运动规律:1.同向性。2.向心性。3.避堵性。下面我们来一个个详细说明这3个概念。
在详细说明这三个概念前,我们还要明确一些前提,在一个大型群体中,单独的个体只能感知到附近区块的属性,不能感知到整个群体的属性。单个个体只关心距离他最近的目标位置,不会去关注其他更远的目标位置。单个个体运动过程中,如果发现前方有障碍,优先避障。
同向性
同一个区块中的个体,会倾向于向同一个方向前进,如果单个个体的方向和群体的平均方向不一致,单个个体会向群体方向去修正自己当前的方向。
向心性
单个个体总是趋向于移动到当前区块最中心的位置(当前集群的平均位置)去。
避堵性
单个个体总是趋向于移动以降低拥挤度,这条和上一条其实是个逆向的过程。其实很好理解,如果没有这条,那所有的个体就挤到一堆去了,这条保证了群体移动的时候的稀疏性。
计算的时候一般就是首先用一个数据结构把当前所有个体分到一个个区块中去,然后计算每个区块中的一些需要的值缓存起来,然后再根据这些缓存的值更新当前每个个体的速度,位置,朝向等信息。每个AI逻辑帧算一次即可。

寻路后的路径平滑,采用漏斗算法

12.jps各个版本的效率对比
游戏场景使得起点和终点差距200个格子,寻路104次。结果:
A*寻路总时间约2.6074x1011纳秒(一秒为109纳秒);
基础版JPS寻路总时间1.7037x1010纳秒;
利用位运算优化的JPS(下文称JPS-Bit)寻路总时间3.2364x109纳秒;
利用位运算和剪枝优化的JPS(下文称JPS-BitPrune)寻路总时间2.3703x109纳秒;
利用位运算和预处理的JPS(下文称JPS-BitPre)寻路总时间2.0043x109纳秒;
利用位运算、剪枝和预处理三个优化的JPS(下文称JPS-BitPrunePre)寻路总时间9.5434x108纳秒。
上述结果表明,寻路200个格子的路径,JPS的五个版本,
平均消耗时间分别为1.7毫秒、0.32毫秒、0.23毫秒、0.02毫秒、0.095毫秒,寻路速度分别为A*算法的15倍、81倍、110倍、130倍、273倍

13.比较以及需要注意的点
jps比a*
    增加了跳点的概念,减少了进入open和close列表的点的数量
    动态找跳点和强迫邻居节点
jps需要优化掉递归
jps+
    预处理标记地图上的跳点
    在JPS的基础上加上预处理,不能处理动态地图,
    需要记录每个格子在八个方向上到最近的跳点和障碍物的距离,障碍物之前的一个格子记为0,跳点之前的一个格子记为1
    需要记录是从哪个方向来的
JPSPlusGoalBounding
    大致上是利用AABB和位处理进行优化

终点在不可行走区域则结束寻路或者找最新的可行走的点
用位运算来优化JPS中节点拓展的效率

https://www.cnblogs.com/xiaohutu/p/10504586.html
大规模单位实时游戏寻路的构建(好文)
https://blog.csdn.net/yxriyin/category_6057606.html
寻路集合(好文)
https://github.com/genechiu/NavMesh
自己实现的NavMesh

http://www.luzexi.com/2013/10/06/Unity3D%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1NavMesh%E5%AF%BB%E8%B7%AF.html
Navmesh的重写


寻路和避障论文参考:
http://www.red3d.com/cwr/boids/
http://gamma.cs.unc.edu/RVO/

#算法特点和比较
A*是一种single source single destination的算法,它的时间复杂度是O(|E|), E是地图上所有路径点所形成的路径网络的edge的数目。
A*可以用来计算从一个点到另外一个点的最优路径。
A*适合用来对动态环境进行路径计算,可以实时根据当时的环境立即计算出路径。
Dijkstra’s algorithm和Floyd-Warshall适合对于不变的环境来进行计算
需求:去最近的XXX,AStar就不适合了。这时候会使用动态规划寻路

单步寻路算法
需求:当追一个移动的目标时,由于目标点一直在变,而且距离不远,这时候每次都求出全路径就很不合算,就会采用单步寻路算法,虽然有可能不是最优解
但是能省很多计算量。(䃼充下单步寻路的大致思路)
1 只要能直接朝目标方向前进一步,就前进,并离开绕行状态
2 否则,尝试从左右两边绕过障碍,进入绕行状态
3 一旦进入绕行状态,不改变最初选择的绕行方向
4设定最多绕行多少步,超过则判定寻路失败,可以换用其它方法


#格子内的属性和优化点:
权重
八个方有哪些是通路
提前检测绕开障碍物:找下一个格子时,可以向需要移动的方向延迟一定距离检测是否有障碍物,有障碍则这个方向不通(类似于视野的感觉)
拐角生硬
可以添加主角大小,防住在障碍拐角处,太过靠近障碍物,且贴着障碍按折线走


#寻路难点:
寻路的时间和性能,避免碰撞重叠,3d空间下带高度属性,导出导航数据给服务器
动态地修改游戏世界的阻挡和代价标记(如建筑,特别是navmesh中怎么处理)

#解决方案
避碰和避免重叠的解决方案:
1.简单的办法是不要一次直接寻出所有路径,而是细分时间,规定每个role的移动速度,比如每秒行动的距离,然后每秒遍历所有role进行相应距离的依次寻路,每个role寻路完成之后设定新位置,下一个role寻路时把这个位置当成障碍物,以此类推。下一秒再反向遍历。这样当时间细分足够小,所有role看起来就是同时移动并且互不重叠的。
2.ROV2
  
  
a星优化
最小二叉堆存放openlist和closelist(减少遍历次数)
预计算H表,运行时直接查表(或者第一次计算H完成后记录下来,下次直接取,空间换时间)
拆分地图为Chunk,A点到B点如果在2个不同的Chunk中,可以先寻从A点到B点所在Chunk的路径,之后到达B所在Chunk后,再寻路到B点(降低高峰)
可考虑向行进方向延迟一定距离,看是否有阻挡,有则走另一边(防止进入死胡同)

A*可以普遍利用于图搜索,而不一定是常见的网格,比如寻路可以沿着边线走

#参考链接
http://qiao.github.io/PathFinding.js/visual/
在线寻路演示
  
https://blog.csdn.net/zerokkqq/article/details/76282908
深入理解游戏中寻路算法

https://www.cnblogs.com/MarkWoo/p/5211035.html
CritterAI与Recast Navigation寻路

https://www.cnblogs.com/yaukey/p/recastnavigation_unity_optimize_visible_result.html
关于 RecastNavigation 寻路结果异常的问题。

https://www.cnblogs.com/yaukey/p/3585226.html
关于 Unity NavMesh 数据的导出和使用

https://www.cnblogs.com/yaukey/p/rts_unit_traverse_size_based_path_finding.html
从 NavMesh 网格寻路回归到 Grid 网格寻路。

-------
navmesh分析
https://blog.csdn.net/Windgs_YF/article/details/87805777
第一部分是Recast
主要功能是将场景网格模型生成用于寻路的网格模型(Navigation Mesh)。

第二部分是Detour
主要功能是利用上一步所生成的Navigation Mesh进行寻路,其包含了多种寻路算法。
运行RecastDemo.exe , 看看它的右边的菜单,有哪些功能,我使用中文进行了标注。

(1)Rasterization:用于设定体素的尺寸。
cellSize:较小的值能够产生更加精确接近原始集合图形的网格,减少在生成导航网格过程中生成多边形产生较大偏离度的问题,但是会消耗更加多的处理时间和内存占用。
cellHeight:较低的值并不会对内存消耗产生有显著影响,较低的值虽然能够使网格贴合原始图形,但是如果是凹凸不平的地形,较低的值可能会造成邻接的网格之间产生断裂,导致本来应该连在一起的网格造成分离。设置较高时,可以看到网格和原始图形间距较大。
(2)Agent:用于虚拟角色的设置。
Height: 角色的身高。
Radius:角色的半径。
Max Climb:描述成生活中爬楼梯时,每个台阶的高度。
Max Slope:描述成生活中爬坡的坡度,以角度值进行设置。
在新版本的unity3d的导航bake界面,画出了一张这样的图,进行了详细的表述。

(3)其余参数如下:
Watershed :分水岭算法,最经典、效果最好,但处理比较慢,一般用于离线处理。
Monotone :最快且可以保证生成的是不重叠、没有洞的 Region,但是生成的 Region 可能会又细又长,效果不好。
Layer:速度、效果都介乎分水岭算法和 Monotone partioning 之间,比较依赖于初始数据。
导航网格NavMesh生成算法比较复杂,建议直接使用这个库可以解决大多数3D场景项目需求。


游戏中的AI主要做的事情:
AI玩游戏:棋牌类游戏的AI,竞技类游戏AI,游戏中的NPC行为控制,甚至是创造一个robot来和人来玩游戏等。
AI生成游戏内容:地图关卡的自动生成,游戏场景剧情的自动生成、推演。
AI对玩家建模:对玩家游戏内行为建模。试图去理解游戏内玩家的行为,情感等信息,从而更好的对玩家进行用户画像,而这些信息也能够进一步的改善游戏的沉浸感。
-----------------------


a星在大量单位寻路时消耗比较大

Navmesh和大规模的单位进行寻路时,单位和单位之间的位置表现总是有一些问题(表现为太过规律,挤在一起,这篇博文介绍了一些可能的表现优化,让单位移动看起来更舒服以及符合常理的移动表现)

问题1

停止一组单位的寻路.n个单位有同一个目的地时,容易出现单位重叠的问题,若用AABB,OBB等碰撞判定去处理性能可能会比较耗

问题1解决方案

可以在单位之间通过消息传递系统进行通知,第一个到达目的点的单位,标记为到达,之后向这个位置移动的单位碰到了这个单位(用数据层的圆形触发器,不用引擎自带的的),则第一个单位会通知第二个单位,使第二个单位停止下来,之后依次的n个单位通知停止

问题2

单位1移动时被一群单位挡住的问题

问题2解决方案

可以给每个单位赋予质量和力,单位间相互碰撞时相互施加力,然后这些力的施加可以在一个组内被传播(消息通知)

效果为大单位能冲散一群小的单位,能提升效果表现,因为只有一些简单的力判定,猜想性能问题不大

问题3

一组单位聚在一个范围内一起移动,但又不能太聚拢,同时需要动态的避开障碍物,避开障碍后表现上这个组又能合并在一起移动

问题3解决方案

计算这个组的平均中心位置

排除掉离散度最高(距离中心最远)的一些单位(动态控制这个离散)

再次计算这个组的平均中心位置

控制离散度高的一些单位向这个组的中心靠拢(靠拢需要动态控制,否则可能会出现挤压内部离散度低的单位的情况)

烘培阻挡信息,点击地图寻路时,在单位移动前先记录下来这个移动方向可能会碰到的障碍物区域范围(只需要一个大致的范围,太过具体可能会耗费性能,需暴露参数,用于后续调整),然后根据移动策略绕开这个障碍区域

问题4

绕过障碍物

问题4解决方案

如果是每个单位向各个方向发n个射线,那m个单位就是n*m个射线,性能消耗会比较高,以下是2个可能的解决方法

1.预先烘培障碍信息,在障碍物边缘做标记,从标记点向每个单位发射线,如果是在障碍物左右两边进行标记,则每个单位根这个障碍物会有2条射线组成一个有一定夹角的区域,移动时单位可能判定移动方向在这个夹角内,则向偏离这个角度的方向移动,这样就能绕开障碍,同时在寻路时将要碰到的障碍物的大致范围框定出一个射线区域。

复杂度为n个标记*m个单位

2.类似下面原文中的一张图,根据一定的算法,将寻路分为2段,以障碍物前方定点为中心,单位当前位置到障碍物前方一定距离的某一点为第一段,障碍物前方某一点到目的地为第二段。群组单位在寻路时,每个单位都向行进方向发一条射线,判断这条射线是否指向这个夹角内,没有指向夹角或者偏移过大则纠正方向。

复杂度为n个单位*2段距离上检查夹角偏移和纠正方向的消耗

--------------------
A* 寻路的八个变种
变种 1 - 束搜索(Beam Search)

    在 A* 算法的住循环中,OPEN 集存储可能需要搜索的节点,用来以查找路径。

    束搜索是 A* 的变体,它限制了OPEN集的大小。

    如果集合变得太大,则丢弃给出良好路径的最差机会的节点。

    束搜索的一个缺点是你必须保持你的设置排序这样做,这限制了你选择的数据结构的种类。

 

变种 2 - 迭代加深(Iterative Deepening)

    迭代加深是一种在许多AI算法中使用的方法,以近似答案开始,然后使其更准确。

    该名称来自游戏树搜索,您可以在其中查看前面的一些移动(例如,在国际象棋中)。

    您可以通过向前看更多动作来尝试加深树木。

    一旦你的答案没有改变或改善很多,你就会认为你有一个非常好的答案,当你试图让它再次变得更准确时它不会改善。

    在 IDA* 中,“深度”是 f 值的截止值。

    当 f 值太大时,甚至不考虑节点(即,它不会被添加到 OPEN 集)。

    第一次通过你处理很少的节点。

    每次后续传递,都会增加您访问的节点数。

    如果您发现路径有所改善,那么您将继续增加截止值;否则,你可以停下来。

 

    我个人认为在游戏地图上不太需要用 IDA* 查找路径。

    因为 ID 算法倾向于增加计算时间,同时减少内存需求。

    然而,在地图寻路中,“节点”非常小 - 它们只是坐标。

    我没有看到节约存储这些节点有什么巨大优势。

 

变种 3 - 动态加权(Dynamic Weighting)

    通过动态加权,您可以假设——

    在搜索开始时,快速到达目的地所在区域更重要;

    在搜索结束时,得到到达目标的最佳路径更重要。

    有一个与启发式相关的权重(w >= 1)。

    随着你越来越接近目标,你减轻了权重;这降低了启发式的重要性,并增加了路径实际成本的相对重要性。 

 

变种 4 - 带宽搜索(Bandwidth Search)

    有一些人可能觉得有用的带宽搜索有两个属性。

    这种变化假设 h 是高估的,但它并没有高估超过一些数e。

    如果您的搜索就是这种情况,那么您获得的路径的成本不会超过最佳路径的成本超过e。

    再次,您的启发式越好,您的解决方案就越好。

     

    您获得的另一个属性是,如果您可以删除OPEN集中的某些节点。

    每当 h + d 大于路径的真实成本(对于某些 d ),您可以丢弃任何 f 值至少比 OPEN 中最佳节点的 f 值高 e + d 的节点。

    这是一个奇怪的属性。

    你有一个很好的 f 值“带宽”;这个频段以外的所有东西都可以丢弃,因为可以保证它不会走在最佳路径上。

 

    奇怪的是,您可以对这两个属性使用不同的启发式方法,但事情仍然有效。

    您可以使用一个启发式方法来保证您的路径不是太糟糕,另一个用于确定 OPEN 集中的内容。

 

    注意:

    带宽搜索看起来可能有用,但在游戏行业很少使用到。

    搜索 Google 以获得更多信息,尤其是相关论文。

 

变种 5 - 双向搜索(Bidirectional Search)

    您可以不从开始到结束搜索,而是并行开始两次搜索,一次从头到尾,一次从完成到开始。

    当他们见面时,你应该有一条好路。

 

 

    在某些情况下,这是一个好主意。

    双向搜索背后的想法是搜索结果在地图上扇出的“树”。

    一棵大树比两棵小树要糟糕得多,所以最好有两棵小树。

 

    从面对面的变化将两个搜索链接在一起。

    不是选择最佳前向搜索节点【 g(start, x) + h(x, goal) 】或最佳后向搜索节点【 g(y, goal) + h(start, y) 】

    该算法选择具有最佳【g(start, x) + h(x, y) + g(y, goal)】的节点对。

 

 

    重定向方法(retargeting approach)放弃了向前和向后方向的同时搜索。

    相反,它会在短时间内执行前向搜索,选择最佳前向候选者 N;

    然后在终点执行后向搜索,搜索的目标不是起点,而是最佳前向候选者 N。

    过了一会儿,它选择了一个最好的后向候选人 M ,并执行从 N 到 M 的前向搜索。

    这个过程一直持续循环,直到两个候选人是同一个点。

 

    Holte,Felner,Sharon,Sturtevant 在 2016 年的论文《具有近似最优节点扩展的前端双向启发式搜索》 是最近的结果,具有接近最优的 A* 双向变体。

    Pijls 和 Post 在 2009 年的论文 《另一种用于最短路径的双向算法》 提出了一种比平衡双向搜索运行得更快的不平衡双向 A* 。

 

变种 6 - 动态 A*(Dynamic A*)和 终身规划 A*(Lifelong Planning A*)

    A* 的变体允许在计算初始路径之后改变世界。

    D* 适用于没有完整信息的情况。

    如果你没有所有的信息,A* 会犯错误;D* 的贡献在于它可以在不花费太多时间的情况下纠正这些错误。

    LPA* 旨在用于成本变化时使用。

    使用A*,路径可能会因地图的更改而失效;LPA *可以重复使用先前 A* 的计算来生成新路径。

 

    但是,D* 和 LPA* 都需要很大的空间 —— 需要保存在最佳路径周围的很多节点的内部信息(OPEN / CLOSED集,路径树,g值)

    然后当地图改变时,D* 或 LPA* 会告诉您是否需要调整路径以考虑地图更改。

 

    对于具有大量移动单位的游戏,您通常不希望保留所有这些信息(因为太大),因此 D* 和 LPA* 不适用。

    它们是专为机器人设计的,如果只有一个机器人 —— 您不需要将内存重用于其他机器人的路径。

    如果您的游戏只有一个或少数单位,您可能需要试试 D* 或 LPA* 。

 

 

    LPA* 概述

    LPA* 小程序

 

变种 7 - 跳点搜索(Jump Point Search)

    许多加速 A* 的技术实际上都是关于减少节点数量。

    在具有统一成本的方形网格中,一次一个地查看所有单独的网格空间是相当浪费的。

    一种方法是构建关键点(例如角点)的图形并将其用于寻路。

    但是,如果您不希望预先计算航点图,那就可以了解一下跳转点搜索,这是 A* 的变体,可以在方格网格上向前跳跃。

    当考虑当前节点的子节点可能包含在 OPEN 集中时,跳转点搜索会向前跳到从当前节点可见的远端节点。

    每个步骤都更昂贵,但它们更少,减少了 OPEN 集中的节点数量。

    有关详细信息,请参阅此博客。

    此文章提供了一个很好的可视化程序用来解释。

 

    另请参阅矩形对称缩减,它可以分析地图并将跳转嵌入到图形本身中。

    这两种技术都是为方形网格开发的。

    这个网站是为六边形网格扩展的算法。

 

 

变种 8 - Theta*

    网格用于寻路,是因为地图是在网格上制作的,而不是因为我们实际上想要在网格上移动。

    如果给出关键点(例如角点)而不是网格的图形,A* 将运行得更快并产生更好的路径。

    但是,如果您不想预先计算角点图形,则可以使用在方形网格上运行的 A* 变体 Theta* 来查找不严格遵循网格的路径。

    在构建父指针时,如果该节点与祖先节点有一条视线,互相可见,那么 Theta* 将该节点直接指向祖先节点,并跳过他俩中间的节点。

    与路径平滑不同,路径平滑在 A* 找到路径之后拉直路径;而 Theta* 可以在 A* 过程中分析这些路径的,是 A* 的一部分。

    这可以得到比将网格路径后处理成任意角度路径更短的路径。

    这篇文章是对该算法的合理介绍;这里是 Lazy Theta*。

 

    Theta *的想法很可能也适用于导航网格。

 

 Block A*,它声称通过使用分层方法比 Theta* 快得多。

--------------------
https://zhuanlan.zhihu.com/p/85994520
无权重规则网格的JPS寻路相关

https://www.xiaoxiongbaby.com/blog/385/
A*、JPS、JPS+ 笔记整理

https://www.cnblogs.com/KillerAery/p/10878367.html
空间划分的数据结构(四叉树/八叉树/BVH树/BSP树/k-d树)

https://www.cnblogs.com/KillerAery/p/10283768.html#b-%E5%AF%BB%E8%B7%AF%E7%AE%97%E6%B3%95
游戏AI之路径规划
https://www.cnblogs.com/KillerAery/p/12242445.html
JPS/JPS+ 寻路算法
https://www.cnblogs.com/KillerAery/category/1229106.html
游戏AI之模糊逻辑

https://www.cnblogs.com/KillerAery/p/10631310.html
https://www.cnblogs.com/KillerAery/category/1227020.html
unity ml-agents机器学习

寻路的优化点
A*的最小堆
    优化:从openList里获取最小代价的节点的速度,从堆顶获取节点
    
    启发函数f(n)=g(n)+h(n) 
    g(n) 为从起点走到该节点的总共耗费值, h(n)为 从该节点走到终点的预测值
    h(n)=0,意味着 f(n)=g(n),此时A星算法则退化成了Dijkstra算法,效率十分低。
    h(n)<cost(n),搜索效率略低,h(n)越小,意味着搜索节点越多,效率上越低。
    h(n)=cost(n),是A*算法最高效的情形。
    h(n)>cost(n),不可以保证一致性,h(n) 越小,寻得的路越接近最优解。

    加权优化
        估值函数的设计遵守以下规则
        当节点离终点较远时,权重应该大一些;当节点逐渐靠近终点时,权重随之变小。
        尽可能让启发函数值不要太大(小于等于实际路径代价是最好的,可有一致性)。
        当节点到达终点时,启发函数值 f(n) 等于实际值。
        2种方案
            1是乘以系数:f(n)=g(n)+k∗h(n),离终点节点远时预测值往往很大,乘了权重后启发函数值会变得更大,从而会开启离终点较近节点
            2是进行指数衰减:f(n)=g(n)+exp[h(n)]∗h(n),跟1类似
            k值的选择,需要测试

    距离的选择
        欧几里得距离
            二维:dis=sqrt(  (x1-x2)^2 + (y1-y2)^2  )
            三维:dis=sqrt( (x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2 )
        曼哈顿距离:两个点在标准坐标系上的绝对轴距总和
            dis=abs(x1-x2)+(y1-y2)
        切比雪夫距离:各坐标数值差的最大值
            dis=max(abs(x1-x2),abs(y1-y2))

    平均帧运算
        大量ai寻路时,可以分帧计算,例如限制1帧走10米,那么如果目标点距离当前位置30米,则分30/10=3帧计算完路径,第一帧计算完时,单位就可以开始运动了,相当于数据和表现分离

    多个单位寻路,单位1挡住了单位2的路怎么办
    
    路径平滑
        路径点和路径点之间没有障碍,则直连,将中间的点丢弃掉
        Navmesh的话可以用漏斗算法
        排除不需要的点后,进行样条曲线

    双向搜索(Bidirectional Search)
        同时开始 从开始节点向目标节点 和 从目标节点向开始节点的搜索,当搜索位置相遇时,代表找到了路径
        优化:双向搜索需要搜索的节点,也就是open列表比单向搜索少
        缺点:某些情况下比单向搜索慢,可用GDC 2018提到了一个名为 NBS的结构进行辅助,使得双向搜索最差情况下比最好的单向搜索情况慢一点点

    路径拼接
        需要设置检测频率m步,和检测距离k步
        例如每m步检测K步范围内是否有障碍,若有障碍则该K步为阻塞路段
        重新计算从p[N]到p[N+k]步的路径,之后将得到的路径代替旧的p[N]到p[N+k]步路径
        此时拼接出来的路径因为绕过了障碍物的原因往往不是最佳的,还需要做路径平滑
        或者看游戏的情况,如果绕路的代价很大,可以考虑原地等待或者重新计算完整的路径
        (例如机关类游戏,ai走到机关前被机关挡住了,这个机关在n秒后会打开,如果路径拼接后的路径很长,可以做成原地等待n秒,之后继续运动)

    避碰
        ROCA/RVO

JSP
    优化:根据轴向或斜向去靠近跳点,减少了openList里的节点数量,也就减少了检测的时间
JSP+(适合静态的场景)
    优化:相比较JSP,JSP+预先计算了每个节点的轴向和斜线方向的最大移动距离(也就是能到达的跳点的距离)

路径点填充优化
    地图很大时手动设置路径点很麻烦,可以通过洪水填充法(Floodfill)自动创建路径点
    以任意一点为起始点,往周围八个方向扩展点(不能通行的位置则不扩展)
    已经扩展的点不需要再次扩展,而扩展出来新的点继续扩展
    直到所有的点都没法再扩展了,此时能得到一张导航图
    可以调整扩展的距离,从而得到合适的节点和边的数量。同时也可以调整扩展方向,再加入一定随即噪音作为距离和方向的参数,可以实现随机扩展的效果,从而得到更加自然的路径

路径查询表    
    对每个顶点使用Dijkstra算法(求出最短路径),求出该顶点到各顶点的路径,再通过对路径回溯得到前一个经过的点
    也就从A点要到D点的路径如果是A,B,C,D的话,那么在表横轴A竖轴D处会记录C

路径成本查询表
    记录每个顶点到其他顶点的最小路径成本

多层次路径点(例如划分为2级,2代表最高级)
    地图特别大时,进行超远距离的导航时可以用这个方法
    划分多层,先找当前位置最近的第3级的路点,之后找到这个第3级路点到目的点是否垮了区
    没有跨区的话,则找当前位置到最近的第2级的路点,之后在第2级的路点中寻路
    跨区了的话,则先通过第3级的路点走到目的点所在区,之后再在第2级进行寻路


障碍物是凸多边形时的碰撞优化
    优化:
        原本应该是几何体和凸多边形的碰撞检测,可以优化为点和几何体的碰撞检测
        具体是:
            扩展障碍物的碰撞体,让角色碰撞器和凸多边形的每个顶点紧挨着,之后连接所有的角色碰撞器,
            得到的n条边就是新的障碍物的边,之后检测角色碰撞体的中心点和这个扩展后的几何体的碰撞(实际上就是让碰撞模型紧挨着障碍几何体走一圈)

    注意:
        各个需要寻路的碰撞模型最好统一形状,这样我们只需要记录一张(或少量)扩展过的障碍图。
        用于扩展障碍物碰撞体的碰撞模型不可以是圆形,因为这样扩展出的障碍几何体将是圆曲的,很难计算。解决方案是用正方形近似替代圆形来生成扩展障碍几何体,然后碰撞模型还是圆形的

Goal Bounding

D*
    适合动态障碍场景,例如移动的车辆堵住了路

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页