在遊戲中,從一點到另一點的操作有時需要遊戲系統自動完成,在一些帶有rpg元素的遊戲中,敵人在發現玩家位置後會自動向玩家的位置移動。這些移動的路線是如何自動確定的?本文將介紹尋路演算法中的a*演算法,並在unity中用c#指令碼來實現尋路功能。
現在有兩個點:起點a,和終點b,允許向周圍的八個方向移動,如圖所示。需要找到從起點a到終點b效率最高的路徑。
當不存在任何障礙物時,找到兩點之間的最短路徑似乎毫無難度:只需要每一步都選擇離目標點最近的鄰近點即可。
但是當存在障礙物時,路徑上的點與目標點的距離並不是單調製化的,這時如何用乙個普適性的思路來找出最優路徑?
從資料結構的角度來看,網格化的地圖是一種帶有權值的無向圖,而路徑可以被視為由圖中的結點組成的鍊錶。因此,尋路的實質是在有權的圖中,在所有以起點為頭結點,終點為尾結點的鍊錶中,選擇長度最短的鍊錶。
核心思想
a*演算法的核心思想是:使圖中的每乙個結點都處在使其權值最小的那條路徑上。
權值的定義
首先,需要確定權值,或者叫代價(cost)的計算方式。點在路徑上的代價由兩部分組成:從起點到該點的代價和從該點到終點的代價。
1.從起點到該點的代價
從起點起到該點的代價應當為每一步新增的代價的總和。每向正上、下、左、右方向走一步,代價增加10,每斜向走一步,代價增加14;
2.從該點到終點的代價
從該點到終點的代價是用來估量這個點到終點還需要的過程長短,可以用該點到目標點的直線距離*10來表示。
搜尋過程
依照這個想法,由於每走一步只能移動至8鄰域,所以每個點的前置結點必然是其8鄰域中的乙個。只要對其8鄰域的每個點作為前置節點時的最小代價進行比較,選擇代價最小的點作為前置結點,依此迭代至終點,再向上提取前置就可以得到最優路徑。
那對如何確定所有點的搜尋順序呢?因為仍然需要優先考慮朝終點最近的點,所以依然以點的總代價作為搜尋順序的依據,總代價小的優先被搜尋(因為代價最小的點意味著已經處於最優路徑)。
設定乙個待搜尋結點的集合open。從起點開始,每搜尋到乙個點,就以其作為前置結點,計算其所有鄰近點在該路徑上的代價,並與鄰近點已有的前置結點下的代價比較,如果當前點帶來的代價更小,則更新前置結點,並使所有鄰近點放入待搜尋集合open。將該點移除集合open。再在open中選取代價最小的結點,重複以上操作,直至搜尋到終點。
當第一次搜尋到終點時,此時路徑第一次被完成,即為最優路徑。
unity中實現a*演算法的**如下所示。這裡將路徑和待搜尋點集合都用list類來表示;編寫結點類node,存放位置、代價和前置結點,其中起點對應的結點前置節點為null。
//事先初始化的全域性變數
vector2 origin, destination;
//起點和終點
int mapwidth, mapheigth;
//地圖大小
bool[,
] isbarrier;
//判斷是否為路障
list road;
//路徑儲存
void
astar()
current = open[minlocation]
; open.
removeat
(minlocation)
; close.
add(current)
;//如果已經到達終點,路徑搜尋完成第乙個,第乙個構建的路徑即為最優路徑if(
(current.position-destination)
.sqrmagnitude ==0)
int index;
//儲存所有暫時需要儲存的list中元素位置
//遍歷當前點鄰域,使所有點的前置節點都保證其代價更小
for(
int i =-1
; i <=
1; i++
)//判斷該處是否為路障
if(isbarrier[
(int
)current.position.x + i,
(int
)current.position.y + j]
)//已經在close中則跳過
if(close.
exists
(t =
>
((t.position -
newvector2
(current.position.x + i, current.position.y + j)
).sqrmagnitude ==0)
))//open中是否已經存在,如果不存在,新增進open,如果存在,看所在路徑是否需要更新
node temp =
newnode
(new
vector2
(current.position.x + i, current.position.y + j)
, destination, current);if
(!open.
exists
(t =
>
((t.position -
newvector2
(current.position.x + i, current.position.y + j)
).sqrmagnitude ==0)
))else}}
}}while
((current.position - destination)
.sqrmagnitude !=0)
;//將鏈中的結點提取出來
road.
add(current);do
while
(current.parent !=
null);
}//結點類
public
class
node
//帶有父節點(前置節點)的node
public
node
(vector2 location,
vector2 destination,
node parent)
}
為了測試尋路演算法的能力,利用unity編寫了乙個以9*9的方格作為模擬地圖的測試程式,實現了以下功能:
·重置地圖並重新(隨機)設定起點、終點(分別為紅色和綠色);
·隨機設定障礙;
·利用尋路演算法尋路並顯示(藍色)。
看到在這些比較簡單的條件下還是有非常不錯的搜尋效果的。
在尋路演算法中,a*演算法還只是靜態條件下的基礎演算法,在具體的環境和需求下還需要對圖中結點的代價的定義和更新有更複雜的要求。但不論是什麼樣的尋路,最終都是在所有的路徑中尋找最優的代價分布。只要遵循這一思想,尋路的問題就都能找到突破的關鍵。
原創 Flex中A star尋路演算法
a 尋路演算法是最常用的尋路演算法了,下面介紹一些概念,及flex中實現的demo 節點 node 本質上就是方形網格裡的某乙個方格 yujjj注 為什麼不把他們描述為方格?因為在一些時候劃分的節點不一定是方形的,矩形 六角形 或其它任意形狀,本書中只討論方格 由此可以看出,路徑將會由起點節點,終點...
在Unity中的Inspector中顯示指令碼屬性
unity的wiki.內聯 片。using unityengine using system using system.collections attributeusage attributetargets.property public class exposepropertyattribute ...
C 字典Dictionary在unity中使用案例
c 字典在unity中使用案例 1 前言 講起c dictionary,許多人聞之色變,不了解,不清楚,即使知道,了解,也不一定會用,鑑於此,本人特地總結了乙個使用字典的案例。2 什麼是字典。必須包含名空間system.collection.generic dictionary裡面的每乙個元素都是乙...