JPS(jump point search)寻路算法

JPS(jump point search)寻路算法

JPS(jump point search)跳跃点寻路算法是对AStar寻路算法的一个改进。

AStar 算法在扩展节点时会把所有相邻的节点考虑进去,当地图比较大时,openList 中的节点数量会很多,搜索效率较低。

在两点之间没有障碍物时,那么中间的节点对我们来说是没有用的,不希望添加到 openList
如下
JPS(jump point search)寻路算法_第1张图片

可以看到JPS 算法不是每个节点都紧密连接的,而是跳跃性的或者说只有需要转弯的节点(也叫拐点)才会被考虑,所以叫跳跃点(Jump Point)。

在实现JPS前先了解它的规则
1.强迫邻居:节点X的邻居节点有障碍物,且X的父节点P经过X到达N的距离代价,比不经过X到大N的任一路径的距离代价都小,则称N是X的强迫邻居。
不知道说的什么意思,看下图
JPS(jump point search)寻路算法_第2张图片
2.跳点(Jump Point):什么样的节点可以作为跳点
(1)节点 A 是起点、终点.
(2)节点A 至少有一个强迫邻居.
(3)父节点在斜方向(斜向搜索),节点A的水平或者垂直方向上有满足 (1)、(2) 的节点

(1)、(2)两条不需要解释,下面看下什么是斜向搜索,下面是一个方向为 (1, 1)斜向搜索
在搜索过程中它可以将水平和垂直方向两个分量,分解为一个方向为(1, 0)的水平搜索,一个方向为(0, 1)的垂直搜索
同理斜向有四种方向
左上 (-1, 1) ——>对应的水平 (-1, 0),垂直 ( 0, 1)
右上 ( 1, 1) ——>对应的水平 ( 1, 0),垂直 ( 0, 1)
右下 ( 1, -1) ——>对应的水平 ( 1, 0),垂直 ( 0, -1)
左下 (-1, -1) ——>对应的水平 (-1, 0),垂直 ( 0, -1)
JPS(jump point search)寻路算法_第3张图片
如上所说(3)的情形即为如下
JPS(jump point search)寻路算法_第4张图片

JSP寻路算法原理如下
JPS 算法和AStar算法非常相似,如果不了解AStar算法的可以先看下 AStar再看JPS会事半功倍。

一.定义开放列表 openList,将起点 S 加入到 openList
二.如果开放列表中节点数大于0个,则取出权值最低的节点 A,如果节点A 是终点则搜索成功返回。
先进行直线搜索 ( 上(0, 1)、下(0, -1)、 左(-1, 0)、右(1, 0))四个方向,根据情况判断需要搜索的方向放入dirList

dirList = {};
if(A没有父节点)
{
    dirList = {(0, 1)、下(0, -1)、左(-1, 0)、右(1, 0)};
}
else
{
    父方向分解的水平 horizontalDir、垂直方向verticalDir 
    // 如父节点P到节点A的方向 PA斜向右上(1, 1)则horizontalDir=(1, 0)、verticalDir=(0,1)
    // 如父节点P到节点A的方向 PA向右(1, 0)则horizontalDir=(1, 0)、verticalDir=(0,0)
    if(horizontalDir != (0, 0))
    {
        dirList.Add(horizontalDir);
    }
    if(verticalDir != (0, 0))
    {
        dirList.Add(verticalDir );
    }
}

foreach (方向 dir in dirList)
{
       节点B = A
       while(true)
       {
            B += dir //向前迈进一步
            if(B 是跳点)
            {
                给B赋权值并且将跳点加入 openList 
                break;
            }
            if(B是障碍物、地图边界)
            {
                break;
            }
       }
}

然后搜索斜方向(左上(-1, 1)、右上( 1, 1)、右下 (1, -1)、左下 (-1, -1)) 四个方向,根据情况判断需要搜索的方向放入dirList

dirList = {}

if(A没有父节点)
{
    dirList = {左上(-1, 1)、右上(1, 1)、右下(1, -1)、左下(-1, -1)};
}
else
{
    父节点为 P
    PA=(X,Y)
    if(X != 0 && Y != 0)
    {
        //斜向 PA(X,Y)搜索
        dirList.Add(PA(X,Y));
    }
    
    foreach(Node N in A的所有强制邻居)
    {
         // AN 一定是斜向的
         dirList.Add(AN);
    }
}

foreach (方向 dir in dirList)
{
       dir 分解出来一个水平方向 horizontal、一个垂直方向vertical
       // 先水平方向搜索
       节点B = A
       while(true)
       {
            B += horizontal //水平向前迈进一步
            if(B 是跳点)
            {
                给B赋权值并且将跳点加入 openList 
                break;
            }
            if(B是障碍物、地图边界)
            {
                break;
            }
       }
       
       令 B = A
       while(true)
       {
            B += vertical//垂直向前迈进一步
            if(B 是跳点)
            {
                给B赋权值并且将跳点加入 openList 
                break;
            }
            if(B是障碍物、地图边界)
            {
                break;
            }
       }
       
       令 B = A
       while(true)
       {
            B += dir //斜向迈进一步
            if(B 是跳点)
            {
                给B赋权值并且将跳点加入 openList 
                break;
            }
            if(B是障碍物、地图边界)
            {
                break;
            }
       }
}

注意事项:
(1).如果节点A没有父方向P(起点)
则直线方向按照 (上下左右)四个方向, dirList = {上、下、左、右}
斜方向按照(左上、右上、右下、左下)四个方向搜索 dirList = {左上、右上、右下、左下}
(2).如果节点A有父方向P
则 PA=(X,Y)
将PA分解为水平 horizontalDir=(X,0),垂直 verticalDir=(0,Y)

还是先考虑水平和垂直的搜索方向,dirList = {}
如果 horizontalDir=(X,0) != (0, 0) 即 X != 0 则 将 horizontalDir 加入到 dirList
如果 verticalDir=(0,Y) !=(0, 0) 即 Y != 0 则 将 verticalDir 加入到 dirList
直线方向搜索 dirList 中的方向

然后是斜向 dirList = {}
如果 PA=(X,Y),X != 0 且 Y != 0, 则将 PA方向加入到 dirList
如果 A有强迫邻居 {N1, N2, N3…},则将 AN1,AN2,AN3,。。。都加入到 dirList

三.回到步骤二继续搜索

下面以一个例子演示JPS搜索的全过程
讲解在图下面,先看图,在看图下边的讲解
JPS(jump point search)寻路算法_第5张图片

看上图,S为起点以绿色格子表示,E为终点以红色表示,棕色节点为障碍物不可通过
1.开始将 S加入到openList
2.从openList 中取出权值最小的节点,此时是S节点,S没有父方向,则开始搜索 (左、上、右、下)四个直线方向,左A1、上B1、下D1处遇到地图边界,C1处遇到障碍物。直线搜索结束
JPS(jump point search)寻路算法_第6张图片
3.看上图
开始向S左上(-1, 1) SE1方向的斜向搜索
SE1(-1, 1)可以分解为水平向左 (-1, 0)和垂直向上(0, 1)两个方向
则SE1斜向搜索需要搜索 水平向左 (-1, 0)、垂直向上(0, 1)和斜向左上(-1, 1) 三个方向
E1向左搜索到达边界,向上搜索到达边界,E1不是跳点,斜向迈进一步到达E2
E2向左搜索到达边界,向上搜索到达边界,E2不是跳点,且已到达边界,结束
JPS(jump point search)寻路算法_第7张图片
4.看上图
开始向S右上(1, 1) SF1方向斜向搜索
SF1(1, 1)可以分解为水平向右(1, 0)和垂直向上(0, 1)两个方向
则SF1斜向搜索需要搜索 水平向右(1, 0),垂直向上(0, 1)和 斜向右上(1, 1)三个方向
F1 向右搜索到障碍物,向上搜索到边界, F1不是跳点,斜向迈进一步到达F2
F2 向右搜索到障碍物,向上搜索到边界, F2不是跳点,斜向迈进一步到达F3
F3 向右搜索到障碍物,向上搜索到边界, F3不是跳点,斜向迈进一步到达F4
F4 向右发现节点M1 为 J2 的强迫邻居,则 F4为跳点,将 F4加入到 openList,将F4节点标记为蓝色,便于区分
由于 F4为 SF1发起的斜向搜索,所以是在 SF1斜向搜索的时候发现了跳点 F4,则不再向上搜索了,也不再继续斜向搜索,SF1斜向搜索停止
右上搜索结束
JPS(jump point search)寻路算法_第8张图片
5.看上图
开始向S右下(1, -1) SG1 方向斜向搜索
SG1(1, -1)可以分解为 水平向右 (1, 0)和垂直向下(0, -1)
则SG1斜向搜索需要搜索 水平向右 (1, 0),垂直向下(0, -1)和斜向右下(1, -1)三个方向
G1 向右搜索到障碍物,向下搜索到边界, G1不是跳点,斜向迈进一步到达 G2
G2 向右搜索到障碍物, 向下搜索到边界, G2不是跳点,斜向迈进一步到达 G3
G3 向右发现节点J3 为 J1 的强迫邻居,则 G3 为跳点,将 G3加入到 openList,将G3节点标记为蓝色
由于G3为 SG1 发起的斜向搜索,所以是在 SG1斜向搜索的时候发现了跳点G3,则不再继续向下搜索了,也不在继续斜向搜索,SG1斜向搜索停止
右下搜索结束
JPS(jump point search)寻路算法_第9张图片
6. 看上图
开始向S左下(-1, -1) SH1 方向斜向搜索
SH1(-1, -1)可以分解为 水平向左(-1, 0) 和 垂直向下(0, -1) 两个方向
则SH1斜向搜索需要搜搜 水平向左(-1, 0) ,垂直向下(0, -1)和斜向左下(-1, -1)三个方向
H1 向左搜索到障碍物,向下搜索到边界,H1不是跳点,斜向迈进一步到达 H2
H2 向左搜索到障碍物,向下搜索到边界,H2到达边界了结束
左下搜索结束
此时围绕 S节点的搜索结束,将S节点放入 closedList
JPS(jump point search)寻路算法_第10张图片
7.看上图
取出 openList 中权值最小的节点此时openList ={G3, F4}, 且G3权值最小,则取出G3,将G3标记为绿色
G3的父方向时 S,则 SG3 方向为右下(1, -1)
SG3可以分解出水平向右 (1, 0),垂直向下 (0, -1) 两个方向
则SG3斜向搜索需要搜索 水平向右 (1, 0),垂直向下 (0, -1)和斜向右下(1, -1)三个方向

G3向右搜索发现节点J3是 J1 的强迫邻居,则J1是跳点,将节点J3加入到J1的强迫邻居列表中,将 J1添加到 openList,将 J1标记为蓝色
由于不是斜向搜索发现的跳点,则继续
G3向下搜索到边界,斜向迈进一步到达 G4
G4向右搜索到达边界,向下搜索到达边界,G4不是跳点,则斜向迈进一步到达G5
G5向右搜索到达边界,向下搜索到达边界,G5到达边界,则搜索结束。
将G3加入到 closedList 中
JPS(jump point search)寻路算法_第11张图片
8看上图
取出openList 中权值最小的节点此时 openList ={F4, J1},F4权值最小,将F4取出,将F4标记为绿色
F4的父方向是S,则SF4方向为右上(1, 1)
SF4可以分解为 水平向右(1, 0)和垂直向上(0, 1)两个方向
则SF4斜向搜索需要搜索的方向为 水平向右(1, 0)、垂直向上(0, 1)和斜向右上(1, 1)三个方向

F4向右搜索发现 节点M1 是节点 J2的强迫邻居,则J2是跳点,将节点M1加入到J2的强迫邻居列表中,将 J2加入到 openList,并将 J2标记为蓝色
由于不是斜向搜索发现的跳点,则继续
F4向上搜索到达边界,斜向迈进一步到达F5
F5向右搜索到达边界,F5向上搜索到达边界,F5不是跳点,则斜向迈进一步到达F6
F6向右搜索发现节点O1是K1的强迫邻居,则F6是跳点,则将 F6加入到 openList,将F6标记为蓝色,
由于F6为 SF4 发起的斜向搜索,所以是在 SF4斜向搜索的时候发现了跳点F6,则不再继续向下搜索了,也不在继续斜向搜索,SF4斜向搜索停止
将F4加入到 closedList
JPS(jump point search)寻路算法_第12张图片
9.看上图
取出openList 中权值最小的节点此时 openList={J1, J2,F6},节点 J1的权值最小,将 J1从openList取出,并标记为绿色
J1的父方向时 G3,则G3J1方向为向右(1, 0),
G3J1分解的水平方向(1, 0)和垂直反向(0,0),其中垂直方向(0,0)不满足垂直方向要求舍去
J1向右搜索到达边界

由于G3J1方向为水平方向,没有斜向,则不搜索斜向
但是J1有一个强迫邻居节点J3
则需要搜索斜向右上 J1J3=(1,1),发现节点P1是J3强迫邻居,则J3是跳点,将 P1加入到J3的强迫邻居中,将 J3加入到 openList并且标记为蓝色
将J3加入到 closedList
搜索结束
JPS(jump point search)寻路算法_第13张图片

10看上图
从openList中取出权值最小的节点,此时 openList ={J3, F6, J2}, 节点F6权值最小,则取出F6,并将F6标记为绿色
F6父方向是 F4,则F4K6方向为斜向右上 (1, 1)
F4F6分解为水平向右(1, 0)和垂直向上(0, 1)两个方向
则F4F6斜向搜索需要搜索 水平向右(1, 0),垂直向上(0, 1)和斜向右上(1,1)三个方向
F6向右搜索发现节点O1为 K1 的强迫邻居,K1为跳点,将O1加入到K1的强迫邻居中,将 K1加入到openList,并将K1标记为蓝色
不是斜向搜索到的跳点,继续
F6向上搜索到达边界,斜向迈进一步到达F7
F7向右搜索到达障碍物,向上搜索到达边界,
将F6加入到 closedList
搜索结束
JPS(jump point search)寻路算法_第14张图片

11看上图
从openList中取出权值最小的节点,此时 openList ={K1, J3, J2}, 节点K1权值最小,则取出K1,并将K1标记为绿色
K1父方向是 F6,则F6K1方向为水平向右(1, 0)
F6K1分解为水平向右(1, 0)和垂直(0, 0)两个方向,且垂直方向(0,0)不满足要求
K1向右搜索到边界

F6K1为水平向右,没有斜向,则不搜索斜向
但是 K1有一个强迫邻居O1,则搜索斜向右上 K1O1=(1,1)
K1O1分解为水平向右(1, 0)和垂直向上(0, 1)
O1向右搜索到边界,向上搜索到边界,O1不是跳点,则斜向迈进一步到达 O2
O2向右搜索到边界,向上搜索到边界,O2到达边界,
将K1加入到 closedList
搜索结束
JPS(jump point search)寻路算法_第15张图片

12看上图
从openList中取出权值最小的节点,此时 openList ={ J2,J3}, 节点J2权值最小,则取出J2,并将J2标记为绿色
J2父方向是 F4,则F4J2方向为水平向右 (1, 0)
F4J2分解为水平向右(1, 0)和垂直(0, 0)两个方向,且垂直方向(0,0)不满足要求
J2向右搜索到边界

F4J2为水平向右,没有斜向,不需要斜向搜索
但是J2有一个强迫邻居M1,则想 J2M1斜向右下搜索
M1 向右搜索到达边界,向下搜索发现 J3,J3已经在 openList,更新J3的权值,返回
发现 R1为M1的强迫邻居,M1为跳点,将R1加入到 M1的强迫邻居,M1加入到openList,并将M1标记为蓝色
将J2加入到closedList
搜索结束

JPS(jump point search)寻路算法_第16张图片
13看上图
从openList中取出权值最小的节点,此时 openList ={ J3,M1}, 节点J3权值最小,则取出J3,并将J3标记为绿色
J3父方向是 J1,则J1F3方向为斜向右上 (1, 1)
J1J3分解为水平向右(1, 0)和垂直(0, 1)两个方向
J3向右索索到边界,向上搜索到M1,M1已经在openList中,如果从J3到M1 的权值比 M1当前权值更小,则更新M1的权值,斜向迈进一步到达N1
N1向右搜索到边界,向上搜索到边界,斜向迈进一步到达N2
N2向右搜索到边界,向上搜索到边界,斜向迈进一步到达N3
。。。直到到达边界

J3还有一个强迫邻居P1,斜向左上J3P1=(-1,1)
J3P1 分解为水平向左(-1,0)和垂直向上(0, 1)
P1向左搜索到障碍物,向上搜索到障碍物,斜向迈进一步到达P2
P2向左搜索到 终点E,终点是跳点,则将 P2加入到openList中
将J3加入到closedList

到这里已经触摸到终点E了,接下来再将 P2取出来就能搜索到终点E 了,不再说明了,搜索成功结束

下面是一个Unity实现的搜索展示动画
JPS(jump point search)寻路算法_第17张图片
刚开始加入到openList的节点显示蓝色小球,当从openList中取出来时显示绿色小球

此处是代码实现连接,Unity辅助展示,核心逻辑与引擎无关、与语言无关

下面是一个寻路搜索展示网页的搜索展示动画
JPS(jump point search)寻路算法_第18张图片
网页链接

补充:
代码 JPSTool 判断强制邻居部分,的计算逻辑说明

判断水平、垂直方向(水平向右、水平向左、竖直向上、竖直向下)的强制邻居

CheckHVForceNeighbour

判断斜向(左上、左下、右上、右下)的强制邻居

CheckDiagonalForceNeighbour

先来看水平方向逻辑
JPS(jump point search)寻路算法_第19张图片
先看前置接点P和当前节点X 处于横向水平关系时
从上图可看出从 PX水平方向X 的强制邻居有分别是 N1、N2
组成部分是:
(1)P、X、O1、N1
(2)P、X、O2、N2

已知:P、X 坐标
如何求得 O1、N1、O2、N2 的坐标?
P 指向 X 的方向 dir = (1, 0)
(1.1)公式1:
O1坐标 = X + (Abs(dir.Y) * 1, Abs(dir.X) * 1)
O1坐标 = X + (0, 1)

(1.2)公式2:
N1 坐标 = O1坐标 + dir
N1 坐标 = O1 坐标 + (1, 0)

(2.1)公式3:
O2 坐标 = X + (Abs(dir.Y) * -1, Abs(dir.X) * -1)
O2 坐标 = X + (0, -1)
(2.2)公式4:
N2 坐标 = O2 坐标 + dir
N2 坐标 = O2 坐标 + (1, 0)

同理水平向左、竖直向上、竖直向下 也都是使用 公式1、公式2、公式3、公式4
公式1公式3的区别在于
公式1 乘以 1 -> (Abs(dir.Y) * 1, Abs(dir.X) * 1)
公式3 乘以 -1 -> (Abs(dir.Y) * -1, Abs(dir.X) * -1)

公式2 和 公式4 相同

再来看斜向逻辑
JPS(jump point search)寻路算法_第20张图片
先看前置接点P和当前节点X 处于斜向关系
从上图可看出从 PX斜向左上X 的强制邻居有分别是 N1、N2
组成部分是:
(1)P、X、O1、N1
(2)P、X、O2、N2

已知:P、X 坐标
如何求得 O1、N1、O2、N2 的坐标?
P 指向 X 的方向 dir = (-1, 1)
(1.1)公式1:
O1坐标 = X + (dir.X, 0)
O1坐标 = X + (-1, 0)

(1.2)公式2:
N1 坐标 = O1 坐标 + (dir.X, 0)
N1 坐标 = O1 坐标 + (-1, 0)

(2.1)公式3:
O2 坐标 = X + (0, dir.Y)
O2 坐标 = X + (0, 1)

(1.2)公式4:
N2 坐标 = O2 坐标 + (0, dir.Y)
N2 坐标 = O2 坐标 + (0, 1)

同理水平向左、竖直向上、竖直向下 也都是使用 公式1、公式2、公式3、公式4

如有错误之处请指出,感谢

你可能感兴趣的:(寻路,JPS寻路算法,JumpPointSearch,寻路算法,JumpPoint)