A星尋路算法介紹

你是否在作一款遊戲的時候想創造一些怪獸或者遊戲主角,讓它們移動到特定的位置,避開牆壁和障礙物呢?html

若是是的話,請看這篇教程,咱們會展現如何使用A星尋路算法來實現它!算法

在網上已經有不少篇關於A星尋路算法的文章,可是大部分都是提供給已經瞭解基本原理的高級開發者的。編程

本篇教程將從最基本的原理講起。咱們會一步步講解A星尋路算法,幷配有不少圖解和例子。數組

無論你使用的是什麼編程語言或者操做平臺,你會發現本篇教程頗有幫助,由於它在非編程語言的層面上解釋了算法的原理。稍後,會有一篇教程,展現如何在Cocos2D iPhone 遊戲中實現A星算法。網絡

如今找下到達一杯咖啡因飲料和美味的零食的最短路徑,開始吧!:]編程語言

 

一隻探路貓

 

讓咱們想象一下,有一款遊戲,遊戲中一隻貓想要找到獲取骨頭的路線。oop

「爲何會有一隻貓想要骨頭?!」你可能會這麼想。在本遊戲中, 這是一隻狡猾的貓,他想撿起骨頭給狗,以防止被咬死!:]this

如今想像一下下圖中的貓想找到到達骨頭的最短路徑:spa

不幸的是,貓不能直接從它當前的位置走到骨頭的位置,由於有面牆擋住了去路,並且它在遊戲中不是一隻幽靈貓!3d

遊戲中的貓一樣懶惰,它老是想找到最短路徑,這樣當他回家看望它的女友時不會太累:-)

可是咱們如何編寫一個算法計算出貓要選擇的那條路徑呢?A星算法拯救了咱們!

 

簡化搜索區域

 

尋路的第一步是簡化成容易控制的搜索區域。

怎麼處理要根據遊戲來決定了。例如,咱們能夠將搜索區域劃分紅像素點,可是這樣的劃分粒度對於咱們這款基於方塊的遊戲來講過高了(不必)。

做爲代替,咱們使用方塊(一個正方形)做爲尋路算法的單元。其餘的形狀類型也是可能的(好比三角形或者六邊形),可是正方形是最簡單而且最適合咱們需求的。

像那樣去劃分,咱們的搜索區域能夠簡單的用一個地圖大小的二維數組去表示。因此若是是25*25方塊大小的地圖,咱們的搜索區域將會是一個有625 個正方形的數組。若是咱們把地圖劃分紅像素點,搜索區域就是一個有640,000個正方形的數組了(一個方塊是32*32像素)!

如今讓咱們基於目前的區域,把區域劃分紅多個方塊來表明搜索空間(在這個簡單的例子中,7*6個方塊 = 42 個方塊):

 

Open和Closed列表

 

既然咱們建立了一個簡單的搜索區域,咱們來討論下A星算法的工做原理吧。

除了懶惰以外,咱們的貓沒有好的記憶力,因此它須要兩個列表:

  1. 一個記錄下全部被考慮來尋找最短路徑的方塊(稱爲open 列表)
  2. 一個記錄下不會再被考慮的方塊(成爲closed列表)

貓首先在closed列表中添加當前位置(咱們把這個開始點稱爲點 「A」)。而後,把全部與它當前位置相鄰的可通行小方塊添加到open列表中。

下圖是貓在某一位置時的情景(綠色表明open列表):

如今貓須要判斷在這些選項中,哪項纔是最短路徑,可是它要如何去選擇呢?

在A星尋路算法中,經過給每個方塊一個和值,該值被稱爲路徑增量。讓咱們看下它的工做原理!

路徑增量

 

咱們將會給每一個方塊一個G+H 和值:

  • G是從開始點A到當前方塊的移動量。因此從開始點A到相鄰小方塊的移動量爲1,該值會隨着離開始點愈來愈遠而增大。
  • H是從當前方塊到目標點(咱們把它稱爲點B,表明骨頭!)的移動量估算值。這個常被稱爲探視,由於咱們不肯定移動量是多少 – 僅僅是一個估算值。

你也許會對「移動量」感興趣。在遊戲中,這個概念很簡單 – 僅僅是方塊的數量。

然而,在遊戲中你能夠對這個值作調整。例如:

  • 若是你容許對角線移動,你能夠針對對角線移動把移動量調得大一點。
  • 若是你有不一樣的地形,你能夠將相應的移動量調整得大一點 – 例如針對一塊沼澤,水,或者貓女海報:-)

這就是大概的意思 – 如今讓咱們詳細分析下如何計算出G和H值。

關於G值

 

G是從開始點A到達當前方塊的移動量(在本遊戲中是指方塊的數目)。

爲了計算出G的值,咱們須要從它的前繼(上一個方塊)獲取,而後加1。因此,每一個方塊的G值表明了從點A到該方塊所造成路徑的總移動量。

例如,下圖展現了兩條到達不一樣骨頭的路徑,每一個方塊都標有它的G值:

關於H值

H值是從當前方塊到終點的移動量估算值(在本遊戲中是指方塊的數目)。

移動量估算值離真實值越接近,最終的路徑會更加精確。若是估算值中止做用,極可能生成出來的路徑不會是最短的(可是它多是接近的)。這個題目相對複雜,因此咱們不會再本教程中講解,可是我在教程的末尾提供了一個網絡連接,對它作了很好的解釋。

爲了讓它更簡單,咱們將使用「曼哈頓距離方法」(也叫「曼哈頓長」或者「城市街區距離」),它只是計算出距離點B,剩下的水平和垂直的方塊數量,略去了障礙物或者不一樣陸地類型的數量。

例如,下圖展現了使用「城市街區距離」,從不一樣的開始點到終點,去估算H的值(黑色字):

A星算法

 

既然你知道如何計算每一個方塊的和值(咱們將它稱爲F,等於G+H),  咱們來看下A星算法的原理。

貓會重複如下步驟來找到最短路徑:

  1. 將方塊添加到open列表中,該列表有最小的和值。且將這個方塊稱爲S吧。
  2. 將S從open列表移除,而後添加S到closed列表中。
  3. 對於與S相鄰的每一塊可通行的方塊T:
    1. 若是T在closed列表中:無論它。
    2. 若是T不在open列表中:添加它而後計算出它的和值。
    3. 若是T已經在open列表中:當咱們使用當前生成的路徑到達那裏時,檢查F 和值是否更小。若是是,更新它的和值和它的前繼。

若是你對它的工做原理還有點疑惑,不用擔憂 – 咱們會用例子一步步介紹它的原理!:]

貓的路徑

讓咱們看下咱們的懶貓到達骨頭的行程例子。

在下圖中,我根據如下內容,列出了公式F = G + H 中的每項值:

  • F(方塊的和值):左上角
  • G(從A點到方塊的移動量):左下角
  • H(從方塊到B點的估算移動量): 右下角

同時,箭頭指示了到達相應方塊的移動方向。

最後,在每一步中,紅色方塊表示closed列表,綠色方塊表示open列表。

好的,咱們開始吧!

第一步

第一步,貓會肯定相對於開始位置(點A)的相鄰方塊,計算出他們的F和值,而後把他們添加到open列表中:

你會看到每一個方塊都列出了H值(有兩個是6,一個是4)。我建議根據「城市街區距離」去計算方塊的相關值,確保你理解了它的原理。

同時注意F值(在左上角)是G(左下角)值和H(右下腳)值的和。
第二步

在第二步中,貓選擇了F和值最小的方塊,把它添加到closed列表中,而後檢索它的相鄰方塊的相關數值。

如今你將看到擁有最小增量的是F值爲4的方塊。貓嘗試添加全部相鄰的方塊到open列表中(而後計算他們的和值),除了貓自身的方塊不能添加之外(由於它已經被添加到了closed列表中)或者它是牆壁方塊(由於它不能通行)。

注意被添加到open列表的兩個新方塊,他們的G值都增長了1,由於他們如今離開始點有2個方塊遠了。你也許須要再計算下「城市街區距離」以確保你理解了每一個新方塊的H值。
第三步

再次,咱們選擇了有最小F和值(5)的方塊,繼續重複以前的步驟:

如今,只有一個可能的方塊被添加到open列表中了,由於已經有一個相鄰的方塊在close列表中,其餘兩個是牆壁方塊。

第四步

如今咱們遇到了一個有趣的狀況。正如你以前看到的,有4個方塊的F和值都爲7 – 咱們要怎麼作呢?!

有幾種解決方法可使用,可是最簡單(快速)的方法是一直跟着最近被添加到open列表中的方塊。如今繼續沿着最近被添加的方塊前進。

此次有兩個可經過的相鄰方塊了,咱們仍是像以前那樣計算他們的和值。
第五步

接着咱們選擇了最小和值(7)的方塊,繼續重複以前的步驟:

咱們愈來愈接近終點了!

第六步

你如今訓練有素了!我打賭你可以猜出下一步是下面這樣子了:

咱們差很少到終點了,可是此次你看到有兩條到達骨頭的最短路徑提供給咱們選擇:

在咱們的例子中,有兩條最短路徑:

  • 1-2-3-4-5-6
  • 1-2-3-4-5-7

It doesn’t really matter which of these we choose, it comes down to the actual implementation in code.

選擇哪一條其實不要緊,如今到了真正用代碼實現的時候了。

第七步

讓咱們從其中一塊方塊,再重複一遍步驟吧:

啊哈,骨頭在open列表中了!
第八步

如今目標方塊在open列表中了,算法會把它添加到closed列表中:

而後,算法要作的全部事情就是返回,計算出最終的路徑!

一隻有遠見的貓

在上面的例子中,咱們看到當貓在尋找最短路徑時,它常常選擇更好的方塊(那個在它的將來最短路徑上的方塊)- 好像它是一隻有遠見的貓!

可是若是貓是盲目的,而且老是選擇第一個添加到它的列表上的方塊,會發生什麼事情?

下圖展現了全部在尋找過程當中會被使用到的方塊。你會看到貓在嘗試更多的方塊,可是它仍然找到了最短路徑(不是以前的那條,而是另外一條等價的):

圖中的紅色方塊不表明最短路徑,它們只是表明在某個時候被選擇爲「S」的方塊。

我建議你看着上面的圖,而且嘗試過一遍步驟。此次不管你看到哪一個相鄰的方塊,都選擇「最壞」的方式去走。你會發現最後仍是找到了最短路徑!

因此你能夠看到跟隨一個「錯誤的」方塊是沒有問題的,你仍然會在屢次重複嘗試後找到最短路徑。

因此在咱們的實現中,咱們會按照如下的算法添加方塊到open列表中:

  • 相鄰的方塊會返回這些順序: 上面/左邊/下面/右邊。
  • 當全部的方塊都有相同的和值後,方塊會被添加到open列表中(因此第一個被添加的方塊是第一個被貓挑選的)。

下面是從原路返回的示意圖:

最短的路徑是從終點開始,一步步返回到起點構成的(例子:在終點咱們能夠看到箭頭指向右邊,因此該方塊的前繼在它的左邊)。

總的來講,咱們能夠用下面的僞代碼,合成貓的尋找過程。這是Objective-C寫的,可是你能夠用任何的語言去實現它:

[openList add:originalSquare]; // start by adding the original position to the open list
do {
	currentSquare = [openList squareWithLowestFScore]; // Get the square with the lowest F score
 
	[closedList add:currentSquare]; // add the current square to the closed list
	[openList remove:currentSquare]; // remove it to the open list
 
	if ([closedList contains:destinationSquare]) { // if we added the destination to the closed list, we've found a path
		// PATH FOUND
		break; // break the loop
	}
 
	adjacentSquares = [currentSquare walkableAdjacentSquares]; // Retrieve all its walkable adjacent squares
 
	foreach (aSquare in adjacentSquares) {
 
		if ([closedList contains:aSquare]) { // if this adjacent square is already in the closed list ignore it
			continue; // Go to the next adjacent square
		}
 
		if (![openList contains:aSquare]) { // if its not in the open list
 
			// compute its score, set the parent
			[openList add:aSquare]; // and add it to the open list
 
		} else { // if its already in the open list
 
			// test if using the current G score make the aSquare F score lower, if yes update the parent because it means its a better path
 
		}
	}
 
} while(![openList isEmpty]); // Continue until there is no more available square in the open list (which means there is no path)
相關文章
相關標籤/搜索