JPS(Jump Point Search)寻路

参考资料:2018腾讯移动游戏技术评审标准与实践案例
https://blog.csdn.net/yuxikuo_1/article/details/50406651
http://blog.sina.com.cn/s/blog_4a5c75d40102wo5l.html
https://github.com/SkylerAlvarez/JumpPointSearch

文章目录

    • JPS(Jump Point Search)寻路
  • 一、什么是Jump Point Search?
  • 二、寻路流程
    • 一.定义
    • 二.规则
    • 三.举例
  • 三、实现重点代码分析

一、什么是Jump Point Search?

JPS 又名跳点搜索算法(Jump Point Search),是由澳大利亚两位教授于 2011年提出的基于 Grid 格子的寻路算法。JPS算法在保留A算法的框架的同时,进一步优化了 A算法寻找后继节点的操作。

个人理解,A*是寻找所有的当前点的邻居,会有频繁的点集合加入和删除到点集合操作,jps的优点是会根据当前点的方向及当前点周围邻居的特点进行选择某些特殊的点才执行加入和删除到点集合操作。

JPS类比于KMP算法,有个共同点在于把重复多余的计算通过数据的特性省略,kmp中的next数组是对子字符串的特性的记录,在匹配时根据这些特性跳过多余的计算。jps也是根据邻居点的特性跳过其他多余的点。

这个也是多数优化算法的一个方式。

从论文中的图也可以看出这个特点,M.Time 表示操作 openset 和 closedset 的时间,G.Time 表示搜索后继节点的时间。可见 A*大约有 58%的时间在操作 openset 和 closedset,42%时间在搜索后继节点;而 JPS 大约 14%时间在操作 openset 和 closedset,86%时间在搜索后继节点。

二、寻路流程

PS:本文定义无法直接走到斜对角,比如(1,1)点无法直接走到(2,2)点,需要的话也可以,要改点逻辑\color{red}{PS:本文定义无法直接走到斜对角,比如(1,1)点无法直接走到(2,2)点,需要的话也可以,要改点逻辑}PS:本文定义无法直接走到斜对角,比如(1,1)点无法直接走到(2,2)点,需要的话也可以,要改点逻辑

一.定义

1.点:当前点为:current,邻居点为neighbor,上一点parent

2.点集合:寻路过程中需要保存有效点的集合,分为可探索点集合openset,已探索点集合closedset。

3.路径权值:gCost为起点经过其他点到当前点的代价和,hCost为到目标点的代价,fCost为当前点的与起点终点间价值的和即fCost=gCost+hCost。

4.强迫邻居 (forced neighbour):如果点 neighbor 是 current 的邻居,并且点 neighbor 的邻居有阻挡(不可行走的格子),并且从 parent、current、neighbor 的路径长度比其他任何从 parent到neighbor 且不经过 current 的路径短,其中 parent为路径中 current 的前一个点,则 neighbor 为 current 的强迫邻居,current 为 neighbor 的跳点)

5.跳点(jump point):(1)如果点 y 是起点或目标点,则 y 是跳点,(2)如果 y 有邻居且是强迫邻居则 y 是跳点, 从上文强迫邻居的定义来看 neighbor 是强迫邻居,current 是跳点,二者的关系是伴生的,(3)如果 parent到 y 是对角线移动,并且 y 经过水平或垂直方向移动可以到达跳点,则 y 是跳点。

个人理解:附近有障碍才有强迫邻居,跳点及强迫邻居都可以改变当前的方向,有强迫邻居就是跳点。看文字理解不便,可以看图,主要特点是在当前点旁边有障碍,并且障碍上方可到达。就有强迫邻居。就是图中标黑点部分。

二.规则

规则一,JPS 搜索跳点的过程中,如果直线方向(为了和对角线区分,直线方向代表水平方向、垂直方向,下文所说的直线均为水平方向和垂直方向)、对角线方向都可以移动,则首先在直线方向搜索跳点,再在对角线方向搜索跳点。

规则二,(1)如果从 parent到current 是直线移动,n 是 current 的邻居,若有从 parent到 n 的路径不经过 current且路径长度小于或等于从 parent经过 x 到 n 的路径,则走到 current 后下一个点不会走到 n;(2)如果从 parent(到 current是对角线移动,n 是 current的邻居,若有从 parent到n 的路径不经过 current且路径长度小于从 parent经过 current 到 n 的路径,则走到 current 后下一个点不会走到 n。

规则三,只有跳点才会加入 openset,因为跳点会改变行走方向,而非跳点不会改变行走方向,最后寻找出来的路径点也只会是跳点集合的子集。

继续看图
简单概括,假如方向是往右上角方向,则判断右上方,上方,右方,上寻找跳点,如果邻居中有强迫邻居,当前点是跳点。

垂直方向则同理。

三.举例


5*5 的网格,黑色代表阻挡区,S 为起点,E 为终点。JPS 要寻找从 S 到 E 的最短路径,首先初始化将 S 加入 openset。从 openset 取出 F 值最小的点 S,并从 openset 删除,加入 closedset,S 的当前方向为空,则沿八个方向寻找跳点,在该图中只122有下、右、右下三个方向可走,但向下遇到边界,向右遇到阻挡,因此都没有找到跳点,然后沿右下方向寻找跳点,在 G 点,根据上文定义二的第(3)条,parent(G)为 S,praent(G)到 S 为对角线移动,并且 G 经过垂直方向移动(向下移动)可以到达跳点 I,因此 G 为跳点 ,将 G 加入 openset。从 openset 取出F 值最小的点 G,并从 openset 删除,加入 closedset,因为 G 当前方向为对角线方向(从 S 到 G 的方向),因此在右、下、右下三个方向寻找跳点,在该图中只有向下可走,因此向下寻找跳点,根据上文定义二的第(2)条找到跳点 I,将I 加入 openset。从 openset 取出 F 值最小的点 I,并从 openset 删除,加入closedset,因为 I 的当前方向为直线方向(从 G 到 I 的方向),在 I 点时 I 的左后方不可走且左方可走,因此沿下、左、左下寻找跳点,但向下、左下都遇到边界,只有向左寻找到跳点 Q(根据上文定义二的第(2)条)),因此将 Q 加入openset。从openset取出F值最小的点Q,并从openset删除,加入closedset,因为 Q 的当前方向为直线方向,Q 的左后方不可走且左方可走,因此沿右、左、左上寻找跳点,但向右、左上都遇到边界,只有向左寻找到跳点 E(根据上文定义二的第(1)条)),因此将 E 加入 openset。从 openset 取出 F 值最小的点E,因为 E 是目标点,因此寻路结束,路径是 S、G、I、Q、E。
出处:2018腾讯移动游戏技术评审标准与实践案例

三、实现重点代码分析

截取重点实现代码解析:

    //Node包含路径代价,因为openset每次从中取fCost中最小值得点,openset采用堆的结构,每次加入点后自动堆排序public int gCost;public int hCost;public int fCost{get{return gCost + hCost;}}-----------------------------------------------//比较fCost,C#中的基本类型都提供了默认的比较算法,C#可以调用比较算法为基本类型的数组进行排序。若希望对自建类进行比较或排序,那么可以使用IComparable<T>和IComparer<T>接口。openSet需要用到堆类型数据public interface IHeapItem<T> : IComparable<T>{int HeapIndex{get; set;}}//自定义比较函数,public int CompareTo(Node nodeToCompare){int compare = fCost.CompareTo(nodeToCompare.fCost);if (compare == 0){compare = hCost.CompareTo(nodeToCompare.hCost);}return -compare;}//二叉堆排序主要功能private void _SortUp(T item){int parentIndex = (item.HeapIndex - 1) / 2;while (true){T parentItem = _items[parentIndex];if (item.CompareTo(parentItem) > 0)_Swap(item, parentItem);elsebreak;parentIndex = (item.HeapIndex - 1) / 2;}}private void _SortDown(T item){while (true){int childLeftIndex = (item.HeapIndex * 2) + 1;int childRightIndex = (item.HeapIndex * 2) + 2;int swapIndex = 0;if (childLeftIndex < _currentItemCount){swapIndex = childLeftIndex;if (childRightIndex < _currentItemCount)if (_items[childLeftIndex].CompareTo(_items[childRightIndex]) < 0)swapIndex = childRightIndex;if (item.CompareTo(_items[swapIndex]) < 0)_Swap(item, _items[swapIndex]);elsereturn;}elsereturn;}}------------------------------------//获取最短路径openSet.Add(_startNode);openSetContainer.Add(_startNode);while (openSet.Count > 0){currentNode = openSet.RemoveFirst();openSetContainer.Remove(_startNode);............}//先将起点加入openSet,再取出fCost最小值点,即堆第一个值。//获取此点的邻居,//起点则parent点为null,遍历邻居非障碍点加入。if (parentNode == null){for (int x = -1; x <= 1; x++){for (int y = -1; y <= 1; y++){if (x == 0 && y == 0)continue;if (IsWalkable(x + currentNode.x, y + currentNode.y)){neighbours.Add(_grid[x + currentNode.x, y + currentNode.y]);}}}}//非起点邻居点判断int xDirection = Mathf.Clamp(currentNode.x - parentNode.x, -1, 1);int yDirection = Mathf.Clamp(currentNode.y - parentNode.y, -1, 1);//判断是否水平方向if (xDirection != 0 && yDirection != 0){//assumes positive direction for variable namingbool neighbourUp = IsWalkable(currentNode.x, currentNode.y + yDirection);bool neighbourRight = IsWalkable(currentNode.x + xDirection, currentNode.y);bool neighbourLeft = IsWalkable(currentNode.x - xDirection, currentNode.y);bool neighbourDown = IsWalkable(currentNode.x, currentNode.y - yDirection);//   当前方向上可走点判断,if (neighbourUp)neighbours.Add(_grid[currentNode.x, currentNode.y + yDirection]);if (neighbourRight)neighbours.Add(_grid[currentNode.x + xDirection, currentNode.y]);if (neighbourUp || neighbourRight)if (IsWalkable(currentNode.x + xDirection, currentNode.y + yDirection))neighbours.Add(_grid[currentNode.x + xDirection, currentNode.y + yDirection]);//是否有强迫邻居if (!neighbourLeft && neighbourUp)if (IsWalkable(currentNode.x - xDirection, currentNode.y + yDirection))neighbours.Add(_grid[currentNode.x - xDirection, currentNode.y + yDirection]);if (!neighbourDown && neighbourRight)if (IsWalkable(currentNode.x + xDirection, currentNode.y - yDirection))neighbours.Add(_grid[currentNode.x + xDirection, currentNode.y - yDirection]);}else{//y水平方向if (xDirection == 0){if (IsWalkable(currentNode.x, currentNode.y + yDirection)){neighbours.Add(_grid[currentNode.x, currentNode.y + yDirection]);if (!IsWalkable(currentNode.x + 1, currentNode.y))if (IsWalkable(currentNode.x + 1, currentNode.y + yDirection))neighbours.Add(_grid[currentNode.x + 1, currentNode.y + yDirection]);if (!IsWalkable(currentNode.x - 1, currentNode.y))if (IsWalkable(currentNode.x - 1, currentNode.y + yDirection))neighbours.Add(_grid[currentNode.x - 1, currentNode.y + yDirection]);}}else{//x水平方向if (IsWalkable(currentNode.x + xDirection, currentNode.y)){neighbours.Add(_grid[currentNode.x + xDirection, currentNode.y]);if (!IsWalkable(currentNode.x, currentNode.y + 1))neighbours.Add(_grid[currentNode.x + xDirection, currentNode.y + 1]);if (!IsWalkable(currentNode.x, currentNode.y - 1))neighbours.Add(_grid[currentNode.x + xDirection, currentNode.y - 1]);}}}------------------------------------------------------------------------------//根据可走邻居,判断是否满足跳点条件,假设可走到邻居,判断是否可以到达跳点。//如果是斜方向,有强迫邻居,直接返回。if ((!_grid.IsWalkable(currentNode.x - xDirection, currentNode.y) && _grid.IsWalkable(currentNode.x - xDirection, currentNode.y + yDirection)) ||(!_grid.IsWalkable(currentNode.x, currentNode.y - yDirection) && _grid.IsWalkable(currentNode.x + xDirection, currentNode.y - yDirection))){return currentNode;}//递归判断,斜方向,水平垂直方向可走,没有强迫邻居,继续斜方向寻找跳点。有则返回当前点。Node nextHorizontalNode = _grid.GetNodeFromIndex(currentNode.x + xDirection, currentNode.y);Node nextVerticalNode = _grid.GetNodeFromIndex(currentNode.x, currentNode.y + yDirection);if (_Jump(nextHorizontalNode, currentNode, xDirection, 0) != null || _Jump(nextVerticalNode, currentNode, 0, yDirection) != null){if (!_forced){UnityEngine.Debug.Log(currentNode);Node temp = _grid.GetNodeFromIndex(currentNode.x + xDirection, currentNode.y + yDirection);if (temp != null && _grid.showDebug)UnityEngine.Debug.DrawLine(new Vector3(currentNode.x, 1, currentNode.y), new Vector3(temp.x, 1, temp.y), Color.green, Mathf.Infinity);return _Jump(temp, currentNode, xDirection, yDirection);}else{return currentNode;}}//如果水平方向移动,没有强迫邻居,继续查找下一个点if (xDirection != 0){//if ((_grid.IsWalkable(currentNode.x + xDirection, currentNode.y + 1) && !_grid.IsWalkable(currentNode.x, currentNode.y + 1)) ||(_grid.IsWalkable(currentNode.x + xDirection, currentNode.y - 1) && !_grid.IsWalkable(currentNode.x, currentNode.y - 1))){_forced = true;return currentNode;}}else{if ((_grid.IsWalkable(currentNode.x + 1, currentNode.y + yDirection) && !_grid.IsWalkable(currentNode.x + 1, currentNode.y)) ||(_grid.IsWalkable(currentNode.x - 1, currentNode.y + yDirection) && !_grid.IsWalkable(currentNode.x - 1, currentNode.y))){_forced = true;return currentNode;}}Node nextNode = _grid.GetNodeFromIndex(currentNode.x + xDirection, currentNode.y + yDirection);if (nextNode!= null && _grid.showDebug)UnityEngine.Debug.DrawLine(new Vector3(currentNode.x, 1, currentNode.y),new Vector3(nextNode.x, 1, nextNode.y), Color.green, Mathf.Infinity);return _Jump(nextNode, currentNode, xDirection, yDirection);//设置返回跳点的cost值,并加入openset。并更新堆排序int newGCost = currentNode.gCost + _GetDistance(currentNode, node);if (newGCost < node.gCost || !openSetContainer.Contains(node)){node.gCost = newGCost;node.hCost = _GetDistance(node, _targetNode);node.parent = currentNode;if (!openSetContainer.Contains(node)){openSetContainer.Add(node);openSet.Add(node);}else{openSet.UpdateItem(node);}}---------------------------------------------------------//判断距离,因为cpu计算*和+速度比较快,所以在计算gCost和hCost值通过*和+采取近似值计算,格子和边的类比长度为,10,14(边如果是1,斜边为1.41....),根据xy上不同距离,进行映射。private int _GetDistance(Node a, Node b){int distX = Mathf.Abs(a.x - b.x);int distY = Mathf.Abs(a.y - b.y);if (distX > distY)return 14 * distY + 10 * (distX - distY);return 14 * distX + 10 * (distY - distX);}//2018腾讯移动游戏技术评审标准与实践案例中提到的jps算法优化部分在本项目中未更新

JPS(Jump Point Search)寻路及实现代码分析相关推荐

  1. JPS(jump point search)寻路算法

    JPS(jump point search)寻路算法 JPS(jump point search)跳跃点寻路算法是对AStar寻路算法的一个改进. AStar 算法在扩展节点时会把所有相邻的节点考虑进 ...

  2. 最快速的寻路算法 Jump Point Search

    作者:runzhiwang,腾讯 TEG 后台开发工程师 本文介绍一种跳点搜索算法 JPS 以及其四个优化算法,其寻路速度最快可是 A*算法的 273 倍.文中的 JPS-Bit 和 JPS-BitP ...

  3. 基于搜索的路径寻找方法(Dijkstra, A*和Jump Point Search)

    对解空间的定义 先看我们会遇到的一个问题.上图描述了在地图中不同机器人的形状和尺寸.因为形状和尺寸的差异让碰撞检测问题变得非常复杂.很自然我们会想能不能仅用一个点来描述机器人呢?忽略掉形状和尺寸的差异 ...

  4. java斐波那契查找_详解Java Fibonacci Search斐波那契搜索算法代码实现

    一, 斐波那契搜索算法简述 斐波那契搜索(Fibonacci search) ,又称斐波那契查找,是区间中单峰函数的搜索技术. 斐波那契搜索采用分而治之的方法,其中我们按照斐波那契数列对元素进行不均等 ...

  5. 开源代码分析技巧之三——老外如是说

    开源代码分析技巧之三--老外如是说 继续从深入分析开源代码说起,当然源码分析没有太多捷径可走.笔者只是探讨下,如何分析会更好些.特通过Samba技术邮件群组,向老外提问"如何更好的分析Sam ...

  6. Understand: 静态代码分析神器

    一直以来,我以为Source Insight的代码分析已经是业界最强.最专业.今天试用了一下Scitools的Understand,导入代码后直接可以生成图形化分析结果,包括模块间调用,函数调用流程等 ...

  7. ARM裸机篇---启动代码分析

    ARM裸机篇---启动代码分析 先搞清楚启动代码和Bootloader的区别,启动代码是指CPU复位后到进入C语言的main函数之前需要执行的那段汇编代码. 下面的代码先暂且这样吧,没啥注释的,时间关 ...

  8. 模块加载过程代码分析1

    一.概述 模块是作为ELF对象文件存放在文件系统中的,并通过执行insmod程序链接到内核中.对于每个模块,系统都要分配一个包含以下数据结构的内存区. 一个module对象,表示模块名的一个以null ...

  9. crt0.S(_main)代码分析

    crt0,S(_main)代码分析 --- 1. 设置sp寄存器地址 //设置SP栈指针 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG ...

  10. Cppcheck 1 54 C/C++静态代码分析工具

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Cppc ...

最新文章

  1. Msdn 杂志 asp.net ajax 文章汇集
  2. 【Java基础】异常
  3. MySQL探秘(七):InnoDB行锁算法
  4. Linux项目自动部署
  5. SAP BSP应用有状态和无状态行为差异比较
  6. P1407-[国家集训队]稳定婚姻【tarjan,强连通分量】
  7. php 将颜色透明度,css中如何使颜色透明度
  8. Confluence 6 上传文件
  9. Android基于mAppWidget实现手绘地图(九)–如何处理地图对象的touch事件
  10. LNMP的安装(命令)
  11. SVM——支持向量回归(SVR)
  12. mysql 分页 conut优化_mysql count函数与分页功能极限优化
  13. 移动接入身份认证技术
  14. 天天打排位,你知道王者荣耀的皮肤怎么测试吗?
  15. vue收藏/取消收藏,点赞、取消点赞一个道理,切换图标
  16. css 超出添加滚动条并隐藏滚动条
  17. php实现视频中合成文字,几个视频合并 把照片也加进去 然后配上文字?
  18. 基于WF4.0的公文管理系统
  19. 华为推送的文档就是垃圾垃圾垃圾!
  20. RHCA考试基础(三)

热门文章

  1. USB驱动程序(四)——键盘驱动(控制传输)
  2. 【算法】Leetcode438. 找到字符串中所有字母异位词(每日一题)
  3. JavaSE银行账户管理系统
  4. 淘宝在微信推广,在微信直接打开淘宝的信息,微信进淘宝
  5. C++ 独木舟旅游活动
  6. 计算机常用英语词汇一览表,计算机常用英语词汇总表
  7. NetLimiter(网络限速-对付流氓上传)
  8. Crazy bubbles
  9. JavaScript数组map方法
  10. HoudahGeo 6 for Mac(地理位置信息软件)