關於尋路演算法的一些思考(6):預先計算好的路徑的所用空間

stonesun發表於2015-04-23

有時候,影響計算尋路路徑的不是時間,而是計算路徑所需的上百個單元格所佔的空間。尋路是需要記憶體來執行尋路演算法,還需要額外記憶體來儲存尋到的路徑。執行尋路演算法(A*,開集或閉集)所需的臨時空間經常會比儲存這些尋到的路徑所需的空間更大。通過在同一時間內只進行一條路徑計算來限制遊戲中的計算量,可以將你需要的臨時空間降到最少。另外,對開集閉集的資料結構的選擇也會對減少你所需的臨時記憶體產生很大的影響。在本章中將會轉而關注通過生成的路徑減少使用的空間。

位置vs方向

一條路徑可以是一堆位置或者一堆方向,位置需要更多的空間,但是它的優勢在於它很容易決定一條路徑上的一個任意的位置點或者方向而不用遍歷這條路徑。當儲存方向的時候,只需要這個方向就可以很容易的決定;而位置只能通過遵循某個方向經由整條路徑才能決定,在傳統的柵格化地圖中,位置可能使用兩個16位的整數來儲存,這樣的話,每一步儲存都需要32位元組。因為它有更少的方向所以需要的空間就更少。如果一個單元格只能在四個方向上移動,每一步只需要2位元組;如果單元格可以在六個或者八個方向上移動,每一步就會需要3位元組。這些儲存在儲存位置上的路徑點在路徑中是非常重要的。Hannu Kankaanpaa建議你可以通過儲存絕對的方向(比如”向北”)而不是儲存這些相對方向(比如”右轉60度”)來進一步的減少儲存所需的空間。一些相對方向可能讓一些單元格難以理解。比如說如果你的單元格在想北方移動,那它下一步就不大可能向南移動。在一個六方向的遊戲中,你只有五個有意義的方向。在一些地圖中你可能只有3個方向(直走,左轉60度,右轉60度)有意義。但是在一些其他的地圖中右轉120度可能才是一個有效的移動(比如通過Z字形路線爬一座陡峭的山)。

路徑壓縮

一旦一個路徑被找到,它將會通過某種方式被壓縮。我們可以使用一個通用的壓縮演算法,但是我們不會在這篇文章中討論這個演算法。一個針對具體路徑的壓縮演算法可以用來縮短基於位置的路徑或者基於方向的路徑。在做決定之前,考慮你遊戲中的具體的典型路徑來決定哪一種壓縮演算法最適合你所尋到的路徑。同時 也要考慮在你遊戲中實施(或者除錯)的可行性,程式碼的體積還有這個壓縮演算法是否真的很重要。如果你有 個300單元格的限制,在同一時間只有50個單元格在移動,並且路徑很短(只有100步),那麼所需要的記憶體可能最多隻有50k,那麼你就不需要再考慮使用路徑的壓縮演算法了。

位置點儲存

在一張地圖中如果障礙是尋路的主要影響因子而不是地形,那麼就可以將路徑分成很多條線段,如果是這種情況的話,那麼一條路徑只需要包括這些線段集合的各個終點位置(有時也被稱為路徑點)。運動就是由檢查這條路徑的下一個終點位置並沿著直線向終點移動組成。

方向儲存

當方向被儲存的時候,它可能是在一排中多次出現的方向,你可以利用那種常見的模式使用較少的空間去儲存那條路徑。

一種最好的儲存路徑方式是同時儲存這個方向和指明單元格將在這個方向上移動多少次的數字。不同於位置儲存的優化,當這個方向在這一排中並沒有多次使用的時候這種優化可能會變得很糟。當然,對於很多直線路徑的位置儲存這種方式很有效,由於線可以不與的行走方向之一對齊,這種情況並不適用方向儲存的壓縮。當有多種可選方向時,你可以選擇清除“一直直走”作為一個可行的方向。Hannu Kankaanpaa指出在一個八方向圖中,你可以清除直走,後退還有135度左,右轉(假設你的地圖允許這樣),然後你可以僅僅使用2位元組去儲存每個方向。

另一種儲存路徑的方法是使用可變長度的編碼。這是指使用一個單位元組去儲存大部分的一般性步驟比如說:直走。使用數字1去標記轉向,在跟上一個數字1使用一些位元組去表示轉向,在一個4向圖中,你只可以左轉或者右轉,所以你可能需要使用10來表示左轉11來表示右轉。

可變長度編碼更為通用,可能比遊程編碼工作起來更好,但是對於長直型的路徑就不如混合編碼了。這個(北向,六步直走,左轉,直走三步,右轉,直走5步,左轉,直走六步)的序列被使用長編碼的[(North,6),(WEST,3),(NORTH,5),(WEST,2)]所代替。如果每個方向佔用2位元組,美短距離佔用8位元組,這條路徑需要40位元組去儲存。如果使用可變長度編碼,你需要使用1個位元組去儲存各個步驟2個位元組去儲存每次轉向-[NORTH 0 0 0 0 0 0 10 0 0 0 11 0 0 0 0 0 10 0 0]總共需要24位元組。如果初始化的方向和每次轉向代表一步,你可以每次轉向省下一位元組,這樣你只需要20位元組就可以儲存這條路徑。但是使用可變長度編碼在遇到較長的路徑可能需要使用更多的空間。如果使用遊程編碼這個序列(north,直走200步)是[(NORTH,200)]只需要10個位元組,同樣的序列如果使用可變長度編碼就變成[NORTH 0 0…],總共需要202位元組。

計算路徑點

路徑點是指一條路徑上的所有點。尋路完成後處理步驟時,可以摺疊多個步驟到一個單一的路徑點鐘,通常儲存的是路徑改變方向的點,或者像城市的主要位置點,而不是儲存一路走來的每一步。然後使用演算法在路徑點之間沿著路徑運動。

限制路徑長度

考慮到地圖條件或者指令可能會發生變化,儲存一條長路徑可能意義並不大,因為餘下的路徑點可能根本就不會被使用到。每個單元格可以在路徑開始的時候儲存一些合適的步驟數,然後在路徑快要走完時在重新計算心的路徑。這種方法可以控制每個單元格的資料量。

總結

路徑在遊戲中可能佔用很多空間,尤其是當路徑很長,並且這個路徑上有很多遊戲單元的時候。路徑壓縮、路徑點還有信標(beacon)都會在一定程度上減少在一小塊資料裡儲存很多行路步驟的空間。在一條直線路徑上加入需要儲存路徑點的話,只需要儲存末尾點就可以了,信標是依靠在地圖上特意標明的地方之間事先計算好的路徑上使用。如果路徑仍然需要佔用很大的空間,就要限制路徑的長度了,在經典的實時路徑計算中是這樣做的:為了節省空間,訊息可以被忽略並且延遲計算。

相關文章