遊戲開發 A 尋路演算法

2022-03-10 19:53:02 字數 4285 閱讀 3982

原文出處:a* pathfinding for beginners

譯者序

很久以前就知道了a*演算法,但是從未認真讀過相關的文章,也沒有看過**,只是腦子裡有個模糊的概念。這次決定從頭開始,研究一下這個被人推崇備至的簡單方法,作為學習人工智慧的開始。

這篇文章非常知名,國內應該有不少人翻譯過它,我沒有查詢,覺得翻譯本身也是對自身英文水平的鍛鍊。經過努力,終於完成了文件,也明白的a*演算法的原理。毫無疑問,作者用形象的描述,簡潔詼諧的語言由淺入深的講述了這一神奇的演算法,相信每個讀過的人都會對此有所認識(如果沒有,那就是偶的翻譯太差了--b)。

以下是翻譯的正文。(由於本人使用ultraedit編輯,所以沒有對原文中的各種鏈結加以處理(除了圖表),也是為了避免未經許可鏈結的嫌疑,有興趣的讀者可以參考原文。

會者不難,a*(唸作a星)演算法對初學者來說的確有些難度。

這篇文章並不試圖對這個話題作權威的陳述。取而代之的是,它只是描述演算法的原理,使你可以在進一步的閱讀中理解其他相關的資料。

最後,這篇文章沒有程式細節。你盡可以用任意的計算機程式語言實現它。如你所願,我在文章的末尾包含了乙個指向例子程式的鏈結。 壓縮包包括c++和blitz basic兩個語言的版本,如果你只是想看看它的執行效果,裡面還包含了可執行檔案。

我們正在提高自己。讓我們從頭開始......

序:搜尋區域

假設有人想從a點移動到一牆之隔的b點,如下圖,綠色的是起點a,紅色是終點b,藍色方塊是中間的牆。

[圖1]

你首先注意到,搜尋區域被我們劃分成了方形網格。像這樣,簡化搜尋區域,是尋路的第一步。這一方法把搜尋區域簡化成了乙個二維陣列。陣列的每乙個元素是網格的乙個方塊,方塊被標記為可通過的和不可通過的。路徑被描述為從a到b我們經過的方塊的集合。一旦路徑被找到,我們的人就從乙個方格的中心走向另乙個,直到到達目的地。

這些中點被稱為「節點」。當你閱讀其他的尋路資料時,你將經常會看到人們討論節點。為什麼不把他們描述為方格呢?因為有可能你的路徑被分割成其他不是方格的結構。他們完全可以是矩形,六角形,或者其他任意形狀。節點能夠被放置在形狀的任意位置-可以在中心,或者沿著邊界,或其他什麼地方。我們使用這種系統,無論如何,因為它是最簡單的。

開始搜尋

正如我們處理上圖網格的方法,一旦搜尋區域被轉化為容易處理的節點,下一步就是去引導一次找到最短路徑的搜尋。在a*尋路演算法中,我們通過從點a開始,檢查相鄰方格的方式,向外擴充套件直到找到目標。

我們做如下操作開始搜尋:

從點a開始,並且把它作為待處理點存入乙個「開啟列表」。開啟列表就像一張購物清單。儘管現在列表裡只有乙個元素,但以後就會多起來。你的路徑可能會通過它包含的方格,也可能不會。基本上,這是乙個待檢查方格的列表。

尋找起點周圍所有可到達或者可通過的方格,跳過有牆,水,或其他無法通過地形的方格。也把他們加入開啟列表。為所有這些方格儲存點a作為「父方格」。當我們想描述路徑的時候,父方格的資料是十分重要的。後面會解釋它的具體用途。

從開啟列表中刪除點a,把它加入到乙個「關閉列表」,列表中儲存所有不需要再次檢查的方格。

在這一點,你應該形成如圖的結構。在圖中,暗綠色方格是你起始方格的中心。它被用淺藍色描邊,以表示它被加入到關閉列表中了。所有的相鄰格現在都在開啟列表中,它們被用淺綠色描邊。每個方格都有乙個灰色指標反指他們的父方格,也就是開始的方格。

[圖2]

接著,我們選擇開啟列表中的臨近方格,大致重複前面的過程,如下。但是,哪個方格是我們要選擇的呢?是那個f值最低的。

路徑評分

選擇路徑中經過哪個方格的關鍵是下面這個等式:

f = g + h
這裡:

[圖7]

a*方法總結

好,現在你已經看完了整個說明,讓我們把每一步的操作寫在一起:

把起始格新增到開啟列表。

重複如下的工作:

a) 尋找開啟列表中f值最低的格仔。我們稱它為當前格。

b) 把它切換到關閉列表。

c) 對相鄰的8格中的每乙個?

d) 停止,當你

儲存路徑。從目標格開始,沿著每一格的父節點移動直到回到起始格。這就是你的路徑。

題外話

離題一下,見諒,值得一提的是,當你在網上或者相關論壇看到關於a*的不同的**,你有時會看到一些被當作a*演算法的**而實際上他們不是。要使用a*,你必須包含上面討論的所有元素--特定的開啟和關閉列表,用f,g和h作路徑評價。有很多其他的尋路演算法,但他們並不是a*,a*被認為是他們當中最好的。bryan stout在這篇文章後面的參考文件中論述了一部分,包括他們的一些優點和缺點。有時候特定的場合其他演算法會更好,但你必須很明確你在作什麼。好了,夠多的了。回到文章。

實現的註解

現在你已經明白了基本原理,寫你的程式的時候還得考慮一些額外的東西。下面這些材料中的一些引用了我用c++和blitz basic寫的程式,但對其他語言寫的**同樣有效。

。其他單位:如果你恰好看了我的例子**,你會發現它完全忽略了其他單位。我的尋路者事實上可以相互穿越。取決於具體的遊戲,這也許可以,也許不行。如果你打算考慮其他單位,希望他們能互相繞過,我建議在尋路演算法中忽略其他單位,寫一些新的**作碰撞檢測。當碰撞發生,你可以生成一條新路徑或者使用一些標準的移動規則(比如總是向右,等等)直到路上沒有了障礙,然後再生成新路徑。為什麼在最初的路徑計算中不考慮其他單位呢?那是因為其他單位會移動,當你到達他們原來的位置的時候,他們可能已經離開了。這有可能會導致奇怪的結果,乙個單位突然轉向,躲避乙個已經不在那裡的單位,並且會撞到計算完路徑後,衝進它的路徑中的單位。

然而,在尋路演算法中忽略其他物件,意味著你必須編寫單獨的碰撞檢測**。這因遊戲而異,所以我把這個決定權留給你。參考文獻列表中,bryan stout的文章值得研究,裡面有一些可能的解決方案(像魯棒追蹤,等等)。

處理未知區域:你是否玩過這樣的pc遊戲,電腦總是知道哪條路是正確的,即使它還沒有偵察過地圖?對於遊戲,尋路太好會顯得不真實。幸運的是,這是一格可以輕易解決的問題。

答案就是為每個不同的玩家和電腦(每個玩家,而不是每個單位--那樣的話會耗費大量的記憶體)建立乙個獨立的「knownwalkability」陣列,每個陣列包含玩家已經探索過的區域,以及被當作可通過區域的其他區域,直到被證實。用這種方法,單位會在路的死端徘徊並且導致錯誤的選擇直到他們在周圍找到路。一旦地圖被探索了,尋路就像往常那樣進行。

平滑路徑:儘管a*提供了最短,最低代價的路徑,它無法自動提供看起來平滑的路徑。看一下我們的例子最終形成的路徑(在圖7)。最初的一步是起始格的右下方,如果這一步是直接往下的話,路徑不是會更平滑一些嗎?

有幾種方法來解決這個問題。當計算路徑的時候可以對改變方向的格仔施加不利影響,對g值增加額外的數值。也可以換種方法,你可以在路徑計算完之後沿著它跑一遍,找那些用相鄰格替換會讓路徑看起來更平滑的地方。想知道完整的結果,檢視 marco pinter 發表在 gamasutra.com 的 一篇文章:toward more realistic pathfinding

(免費,但是需要註冊)。

非方形搜尋區域:在我們的例子裡,我們使用簡單的2d方形圖。你可以不使用這種方式。你可以使用不規則形狀的區域。想想冒險棋的遊戲,和遊戲中那些國家。你可以設計乙個像那樣的尋路關卡。為此,你可能需要建立乙個國家相鄰關係的**,和從乙個國家移動到另乙個的g值。你也需要估算h值的方法。其他的事情就和例子中完全一樣了。當你需要向開啟列表中新增新元素的時候,不需使用相鄰的格仔,取而代之的是從**中尋找相鄰的國家。

類似的,你可以為一張確定的地形圖建立路徑點系統,路徑點一般是路上,或者地牢通道的轉折點。作為遊戲設計者,你可以預設這些路徑點。兩個路徑點被認為是相鄰的如果他們之間的直線上沒有障礙的話。在冒險棋的例子裡,你可以儲存這些相鄰資訊在某個**裡,當需要在開啟列表中新增元素的時候使用它。然後你就可以記錄關聯的g值(可能使用兩點間的直線距離),h值(可以使用到目標點的直線距離),其他都按原先的做就可以了。

另乙個在非方形區域搜尋rpg地圖的例子,檢視我的文章:

two-tiered a* pathfinding

進一步的閱讀

好,現在你對一些進一步的觀點有了初步認識。這時,我建議你研究我的源**。包裡面包含兩個版本,乙個是用c++寫的,另乙個用blitz basic。順便說一句,兩個版本都注釋詳盡,容易閱讀,這裡是鏈結。

android遊戲尋路演算法

遊戲開發區很多朋友都在談論a 尋路演算法,大家都感到高深莫測,而不敢涉足尋路演算法.希望下面的分析能為大家解開這個誤區.a 演算法確實是最高效 最流行的尋路演算法,是搜尋演算法最深層的延伸.a 演算法是由4個要素組成 a 估價函式 並查集 堆 廣搜.想要寫a 演算法,我們必須門心自問對這4個方面的基...

遊戲演算法實踐 A星尋路

先展示效果圖 如圖中亮綠色點為起點,藍點為終點,白色為牆體不可行走。黃色點區域為被試探到的位置,綠色的路徑為最後的最短路徑。其中這裡移動規則是只能上下左右移動。建立乙個open集合,乙個close集合 把起點加入close集合,並把它設為current節點 重複如下過程 查詢current節點的相鄰...

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

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