JPSPlusGoalBounding尋路演算法整理

2021-09-28 13:39:31 字數 3232 閱讀 4317

使用場景:2d格仔尋路

優點:比a*快上百倍,對於沒有障礙的直線可以由起點直接跳到終點(高效的原因)

缺點:需要預處理,不支援動態改地形碰撞(無法通過的格仔)

潛規則:斜方向可以走的前提是,斜方向兩個分量上的格仔都要能走(即要走左上角,左邊和上邊都得是非碰撞)

題外話:理解這個演算法前,最好先學會a*,因為jps相當於是優化後的a*

要理解jps,首先得清楚該演算法中跳點(也可以叫跳轉點或者轉折點)的含義。在**中對跳點的定義是這樣的

bool precomputemap::isjumppoint(int r, int c, int rowdir, int coldir)

如何理解這段**呢,其實無外乎兩種情況,首先跳點是乙個可非碰撞格仔,假設我往上走,那麼我正上方那一格是跳點,僅當我當前點和正上方可以走,並且(我右邊是碰撞,右上角是可以走)或者(我左邊是碰撞,左上角可以走),那麼我正上方就是跳點,並且跳點的方向是往上。沒錯,跳點是帶方向的,你可以把剛說的情形畫下來,轉幾個方向,模擬一下從左往右,從右往左,從上往下,就能知道其他幾種情況。如下第一張圖,星星格仔的跳點方向有4個,可以認為它是4個方向的跳點。而第二張圖,它只有上和左兩個方向。

首先需要有乙個資料結構儲存地圖上每個格仔8個方向距離碰撞或跳點的距離。這裡我們只討論兩個方向,乙個直的和乙個斜的,其他方向同理。

假設我們要計算每個格仔距離左邊碰撞或跳點的距離時。首先碰撞是不需要存距離的,所以值直接是0即可。碰撞右邊第乙個非碰撞點值是0,第二個非碰撞點值是-1,第三個非碰撞點值是-2,以此類推用負數記錄離碰撞的距離,至於為什麼第乙個非碰撞點是0不是1,是為了後面的非碰撞點找到這第乙個非碰撞點,而不是直接找到碰撞,所以嚴格意義上是距離碰撞格仔第乙個非碰撞格仔的距離。當遇到左方向的跳點時,跳點右邊第乙個非碰撞格仔值是1,第二個是2,第三個是3,以此類推用正數記錄距離跳點的距離

假設要計算左上角的值,如果我當前點處在左邊或上邊邊界,或者左上角點是碰撞,值為0。意味著左上角是碰撞,我是碰撞後的第乙個非碰撞格仔,假設右下角也是非碰撞格仔,那麼它就是第二個非碰撞格仔,值為-1,第三個是-2,同上面一樣用負數記錄離碰撞的距離。當左邊和上邊是非碰撞時並且左上角是上或左的跳點時,值為1,即左上角為跳點,假設右下角也是非碰撞格仔,那麼它就是第二個非碰撞格仔,值為2,第三個是3,以此類推用正數記錄距離跳點的距離

**見precomputemap.cpp的calculatedistantjumppointmap方法

它是預處理的一部分,用乙個資料結構儲存每個格仔8個方向最遠到哪,目的是當檢索到當前格仔時,如果想從該格仔往某個方向走,可以拿到往這個方向最遠能走到的矩形區域,可以看看目標格仔在不在這裡面。不在的話,這個方向可以直接丟棄。但是,這個預處理特別費時間。記得當時跑200*200的地圖跑了半個小時這個預處理,複雜度乎是8(n^2),n是地圖地圖格仔數量。幸運的是,即使沒有這個處理也不礙事,少了一些剪枝而已,可以看懂整個演算法後自行去掉這個部分

上面兩部分說的都是離線預處理,執行時會先預處理每個格仔8個方向是否碰撞

也就是**jpsplus.cpp部分,searchloop就是典型的bfs搜尋,就是先找一遍8個方向,然後優先佇列(優先順序越高越可能到達終點,啟發式搜尋)遍歷佇列裡每個格仔,直到找到終點或隊列為空的套路。唯一不一樣的就是,搜素規則了。

一開始先引入了cases.h,先說說它的作用,它記錄了2048個函式名(喪心病狂地擼了2048個函式),2048也就是2的11次方,也就是總共有11個位元位可以用,高八位用來記錄當前格仔8個方向是否碰撞,低3位用來記錄當前格仔是上乙個格仔從哪個方向過來。這樣就可以為當前格仔制定最優的方向策略,例如不用走回頭的方向或者是碰撞的方向。

尋路一開始先對起始格仔8個方向搜一遍,之後的格仔就用上述的方式剪枝

然後往各個方向的移動策略,這裡只套路直走和斜著走兩種方式的其中乙個方向,其他的以此類推。假設是直著往下走,會直接跳到下方的跳點或者下方離牆最近的點(用到離線預處理的資料),將這個點加到佇列,假設終點在這條路徑上,則直接跳到終點,將終點加入佇列。假設是往右下方走,如果終點不在右下方,則僅當右下角還有跳點才將右下角跳點加入佇列。如果終點在右下方,則判斷能不能斜著跳到和終點同行或同列的位置,如果這條路線上有其他跳點,則認為不能需要將跳點加入佇列,如果能的話,將跳到的位置加入佇列(這樣下次就能直著到達終點)。

加入佇列時,需要記錄上乙個源點(即從哪邊過來),需要根據到終點的距離用啟發函式算出乙個值用於優先佇列排序的優先順序,並且計算這條路徑開銷,上乙個源點的路徑開銷累加上這兩個點之間距離,就是起始點到這個格仔的開銷了,下一次如果這個格仔又被加入佇列,如果比較下來路徑開銷更優的話,則用新的路徑

尋路邏輯部分到這裡就說完了,然後講一些當時事後一些工作。

雖然這個支援8方向,但斜著的限制太大,改一改能支援不用兩個分量都是非碰撞,乙個非碰撞或者直接斜著走都行。這麼一改的話,那2048個函式就得改一改策略,總結下規律之後,自己寫個生成器生成就好了,似乎他也提供了生成器。

平滑處理,這個演算法最終生成的路徑,其實大部分是一堆跳點,然後它又把這堆跳點補了中間點。但事實上那些跳點捨棄掉一些點後,依然是可行解並且更加平滑,不會出現折來折去的情況。所以採取的策略是這段路線兩端一起平滑,先拿第乙個點也就是起點作為固定點,然後在第二個點和終點間二分,直到找到乙個即跟固定點間無碰撞並且盡可能靠後的點,將兩點間的其他點捨棄,這樣這個找到的點就成為第二個點了。然後拿終點作為固定點,在第二個點和倒數第二個點中找乙個即靠前又跟終點是通路的點,捨棄掉找到的點與終點間的其他點,作為倒數第二個點。接著第二個點作為固定點,在第三個點與倒數第二個點中找。。以此類推,最終平滑的效果還不錯,不過比較麻煩的是找出兩點間經過的格仔。

後面又支援了執行時動態維護,可以動態加減碰撞,改動量和腦洞略大,作為公司知識財產,不予細說。只能說需要執行時動態改跳點距離,一邊尋路一邊維護和計算真實的跳點距離,而不是加完碰撞,立馬都計算出正確跳點距離。 然後還有連通性問題要考慮,因為離線處理,可以人為保證整個地圖是連通的,也就不用急著去考慮連通性。當時連通性直接用的最粗暴的並查集去搞的,並查集的複雜度等價於地圖大小,好在需求上增減碰撞不是特別頻繁,所以還能接受

迷宮尋路(A星尋路演算法)

題目 假設我們有乙個7 5大小的迷宮,如下圖所示,綠色格仔表示起點,紅色的格仔表示終點,中間的3個深灰色格仔表示障礙物。請找到一條從起點到終點最短的路徑。解題思路 需要引入兩個集合和乙個公式,如下 具體步驟 把起點放入openlist 檢查openlist中是否有值,如果沒有則無法到達終點,結束尋路...

A 尋路演算法

問題 由於遊戲中尋路出了個小問題 玩家尋路到乙個死角後在那邊不停的來回跑,就是無法越過障礙物,就研究了下a 尋路演算法以解決這個問題 研究了幾天,自己寫了個demo這裡給出總結 原理 a 演算法給出的是權值最優的路徑而不是最短路徑 權值有f g h來表示 啟發式函式如下 f p g p h p h值...

A 尋路演算法

a 演算法是靜態環境下求最短路徑的不二之選,由於是啟發式搜尋,比dijkstra 深搜廣搜要快的多啦。a 也算是我第一次接觸的移動機械人演算法,csdn上的科普文章也不少,但我作為乙個機械的小白,實現出來還是小有成就感滴。今天抽空和大家分享一下原始碼,開發環境win7 64 opengl vs201...