日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JPS(Jump Point Search)寻路及实现代码分析

發布時間:2023/12/20 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JPS(Jump Point Search)寻路及实现代码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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:1122

一.定義

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)寻路及实现代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。