《Unity3D人工智能编程精粹》笔记
- Unity3D人工智能架構模型
- 游戲AI的架構模型
- FPS/TPS游戲中的AI解析
- 實現AI角色的自主移動——操控行為
- Unity3D操控行為編程的主要基類
- 將AI角色抽象成一個質點——Vehicle類
- 控制AI角色移動——AILocomotion類
- 各種操控行為的基類——Steering類
- 個體AI角色的操控行為
- 靠近and離開
- 抵達
- 追逐and逃避
- 隨機徘徊
- 路徑跟隨
- 避開障礙
- 群體的操控行為
- 檢測附近的AI角色
- 與群中鄰居保持適當距離——分離
- 與群中鄰居朝向一致——隊列
- 成群聚集在一起——聚集
- 個體與群體的操控行為組合
- 操控行為的快速實現——使用Unity3D開源庫UnitySteer
- 操控行為的樂趣
- 尋找最短路徑并避開障礙物——A*尋路
- 實現A*尋路的三種工作方式
- 創建基于單元的導航圖
- 創建可視點導航圖(路點圖)
- 創建導航網格
- A*尋路算法是如何工作的
- 用A*算法實現戰術尋路
- A* Pathfinding Project 插件的使用
- A*尋路的適用性
- AI角色對游戲世界的感知
- AI角色對環境信息的感知方式
- 輪詢方式
- 事件驅動方式
- 常用感知類型的實現
- 所有觸發器的基類——Trigger類
- 所有感知器的基類——Sensor類
- 事件管理器
- 視覺感知
- 聽覺感知
- 觸覺感知
- 記憶感知
- 其他類型的感知——血包、寶物等物品的感知
- AI角色自主決策——有限狀態機
- 有限狀態機的FSM圖
- 用Switch語句實現有限狀態機
- 用FSM框架實現通用的有限狀態機
- FSM框架
- FSMState類——AI狀態的基類
- AdvancedFSM類——管理所有的狀態類
- AI角色的復雜決策——行為樹
Unity3D人工智能架構模型
- 感知:是AI角色與游戲世界的接口,負責在游戲運行過程中不斷感知周圍環境,讀取游戲狀態和數據,為思考和決策收集信息。(for example:是否有敵人接近等。)
- 思考:利用感知的結果選擇行為,在多種可能性之間切換。(such as:戰斗還是逃跑?躲到哪里?)
一般來說,這是決策系統的任務,有時也可能簡單地與感知合二為一。 - 行動:發出命令、更新狀態、尋路、播放聲音動畫,也包括生命值減少等。這是運動系統、動畫系統和物理系統的任務,而動畫和物理系統由游戲引擎提供支持。
游戲AI的架構模型
對AI需求的三種基本能力:
(根據游戲種類和需求進行細化或增刪)
運動層:導航+尋路,決定角色的移動路徑,具體的移動行為需要動畫層的配合(有許多行為可以直接由動畫層處理)。運動層包含的算法能夠把上層做出的決策轉化為運動。
決策層:決定角色在下一時間步該做什么。
戰略層:一組角色的總體行為,團隊協作時用。小組中的每個角色可以有它們自己的決策層和運動算法,但總體上,它們的決策層會受到團隊戰略的影響。
FPS/TPS游戲中的AI解析
FPS(第一人稱視角射擊游戲):以玩家的主觀視角來進行射擊的游戲。
TPS(第三人稱視角射擊游戲):玩家控制的游戲人物在屏幕上是可見的,更加強調動作感。
運動層:確定角色如何在游戲世界中移動。(尋最優路)
決策層:確定角色當前目標、命令、狀態和當前目的地,并與其他層通信,使角色協調地運動到一個指定的目標地點。(決定AI的執行行為)
戰斗控制器:
- 感知部分:
- 視覺子系統:考慮距離、視場角度、當前的可視級別(例如光線、霧和障礙等)。感知受傷害、碰撞等。
- 聽覺子系統
- 策略子系統
- 動畫部分:負責控制角色的骨骼關節。
選擇動畫,選擇動畫參數,播放角色運動序列。
實現AI角色的自主移動——操控行為
操控行為:操作控制角色,讓它們能以模擬真實的方式在游戲世界中移動。
它的工作方式是通過產生一定大小和方向的操控力,使角色以某種方式運動。
基本行為中的每一個行為,都產生相應的操控力,將這些操控力以一定的方式組合起來(將基本行為進行不同組合),就能夠得到更復雜的“行為”,從而實現更為高級的目標。
無論群體中有多少個體,對于每個個體,計算復雜性有限,通過這種簡單計算,可以產生逼真效果。
(兩個相似的鳥群,即使是飛過相同的路線,行為也是不同的)
缺點:可能會出現無法預測的行為,在使用時許多參數需要通過實驗調整。
Unity3D操控行為編程的主要基類
將AI角色抽象成一個質點——Vehicle類
控制AI角色移動——AILocomotion類
是Vehicle的派生類,能真正控制AI角色的移動。(包括計算每次移動的距離,播放動畫等)
各種操控行為的基類——Steering類
是所有操控行為的基類,包含操控行為共有的變量和方法。
(操控AI角色的尋找、逃跑、追逐、躲避、徘徊、分離、隊列、聚集等都可由此派生)
個體AI角色的操控行為
靠近and離開
靠近:指定一個目標位置,根據當前的運動速度向量,返回一個操控AI角色到達該目標位置的“操控力”,使AI角色自動向該位置移動。
離開:與前者正好相反,產生一個操控AI角色離開目標的力。
小三角:AI角色
預期速度:從AI角色的當前位置到目標位置的向量
操控向量:預期速度與AI角色當前速度的差(大小隨當前位置變化而變化)
抵達
AI角色減速并停到目標位置,避免沖過目標。
當角色在停止半徑之外,以最大速度移動。
當角色進入停止半徑之內,逐漸減小預期速度,直到為0。
追逐and逃避
追逐:與靠近行為很相似,只是目標由靜止不動變為可移動角色。
預測獵物的未來位置,然后向著未來位置的方向追去,才能在最短時間內追上獵物。
實現:
- 使用一個簡單的預測期,在每一幀重新計算它的值。
(假設采用一個線性預測期,又假設在預測間隔T時間內角色不會轉向,角色經過時間T之后的未來位置可以用當前速度乘T來確定)
一些情況下,追逐可能會提前結束。
例如,如果逃避者在前面,幾乎面對追逐者,那么追逐者應該直接向逃避者當前位置移動。
二者之間的關系可以通過計算逃避者朝向向量與AI角色朝向向量的點積得到。
逃避:使獵物躲避捕獵者。
(比如鹿被狼追逐,鹿要不斷變換逃跑方向,試圖逃離狼預測的追逐方向)
隨機徘徊
在游戲場景中隨機放置目標,讓角色靠近目標。如果每隔一定時間就改變目標的位置,這樣角色就永遠靠近目標而又不能到達目標(即使到達,目標也會再次移動)。
But角色有時會突然掉頭,因為目標移動到了它的后面。
路徑跟隨
產生一個操控力,使AI角色沿著預先設置的軌跡,構成路徑的一系列路點移動。
最簡單的跟隨路徑方式:
將當前路點設置為路點列表中的第1個路點,用靠近行為產生操控力來靠近這個路點,直到非常接近這個點;
然后尋找下一個路點,設置為當前路點,再次接近它。
重復這樣的過程直到到達路點列表中的最后一個路點,再根據需要決定是回到第一個路點還是停止在這最后一個路點上。
- 封閉路徑:循環,永不結束。需要回到起點重新開始。
- 開放路徑:AI角色需要減速(利用抵達行為)停到最后一個路點上。
在實現路徑跟隨行為時,需要設置一個“路點半徑”參數(會引起路徑形狀的變化)。
即當AI角色距離當前路點多遠時,可以認為它已經到達當前路點,從而繼續向下一個路點前進。
避開障礙
操控AI角色避開路上的障礙物。
當AI角色的行進路線上發現比較近的障礙時,產生一個“排斥力”,使AI角色遠離這個障礙物。
當前方發現多個障礙物時,只產生躲避最近的障礙物的操控力。
區別:
尋路算法:事先根據障礙物的位置,計算出一條靜態可行走路線。
障礙躲避:讓AI角色在環境中移動,根據移動情況動態地躲避障礙,最終形成一條動態路線。(缺點:對于躲避“L”或“T”形狀的障礙物時工作得不太好。因為最終決定AI角色運動狀態的是合力,有時無法完全避開障礙)
- 分析步驟:
- 缺點和改進:
群體的操控行為
組行為:
檢測附近的AI角色
組行為的每種操控行為都決定角色對相鄰的其他角色做出何種反應。
為了實現組行為,首先需要用一個雷達腳本實現檢測位于當前AI角色“鄰域”中的其他AI角色。
一個角色的鄰域由一個距離和一個角度來定義,如圖灰色區域。
這個灰色區域可以認為是AI角色的可視范圍。
(有時為了簡化,不考慮AI角色的可見范圍,只用一個圓來定義)
與群中鄰居保持適當距離——分離
使角色與周圍的其他角色保持一定的距離,避免多個角色相互擠到一起。
圖中黑色箭頭表示在三個鄰居的“排斥”作用下,AI角色所受到的操控力。
- 實現:
搜索指定鄰域內的其他鄰居。
對每個鄰居,計算AI角色到達該鄰居的向量r,將向量r歸一化( r / |r| , |r|表示向量r的長度),得到排斥力方向。
由于排斥力的大小是與距離成反比的,因此還需要除以|r|,得到該鄰居對它的排斥力,即 r / |r|^2。
把來自所有鄰居的排斥力相加,得到分離行為的總操控力。
與群中鄰居朝向一致——隊列
試圖保持AI角色的運動朝向與鄰居一致。
當前運動方向:尖端的灰色線指示的方向
鄰居的平均朝向:尖端的深色線指示的方向
深色線(目標朝向)與灰色線(當前朝向)之間的差值是操控力的方向,即操控向量。
- 實現:
通過迭代所有鄰居,可以求出AI角色朝向向量的平均值以及速度向量的平均值,得到想要的朝向。
然后減去AI角色的當前朝向,就可以得到隊列操控力。
成群聚集在一起——聚集
聚集行為產生一個使AI角色移向鄰居的質心的操控力。
與四個鄰居都有連接線的小點是這四個鄰居位置的平均值。
從黑色小三角指向這個小點的箭頭表示作用于AI角色的操控力。
- 實現:
迭代所有鄰居求出AI角色位置的平均值,然后利用靠近行為,將這個平均值作為目標位置。
個體與群體的操控行為組合
最簡單的組合多個行為方式:直接把各個行為所產生的操控力加到一起,這樣得到的總操控力會反映出這些行為。
由于AI角色有最大操控力maxForce的限制,不能簡單地把所有的操控力加起來,需要以某種方式截斷這個總和,確保得到的值不超過最大力的限制。
- 實現截斷的方法:
-
加權截斷總和:為每個操控力乘上一個權重,將它們加在一起,然后將結果截斷到允許的最大操控力。
缺點:當操作力相互沖突會產生問題。
(例如分離的操控力較大,而避開障礙的操控力較小,那么AI角色可能會撞到障礙物上。解決辦法是增加避開障礙的權重,但如果把這個權重調得過大,當AI角色單獨接近障礙物時,又可能出現奇怪的行為,好像被障礙物大力推開了一樣) -
帶優先級的加權截斷累計
-
帶優先級的抖動
-
……
-
操控行為的快速實現——使用Unity3D開源庫UnitySteer
下載地址:https://github.com/ricardojmendez/UnitySteer
靠近、離開、抵達、追逐、逃避等個體行為可以用其他方法來實現,例如尋路算法。
操控行為的樂趣
- 可以更好地模擬隨機徘徊行為
- 具有較高的效率
- 與A* 尋路相比,操控行為更適合于仿真大的群體的呈現一定個性的行為。
(A* 尋路總是會尋找最優路徑,而且當要尋路的AI角色很多時,效率會受到很大影響,并且結果很規則,可以預期。而操控行為與之相較,代價更小,更為生動)
尋找最短路徑并避開障礙物——A*尋路
實現A*尋路的三種工作方式
A*尋路相關術語:
創建基于單元的導航圖
基于單元的導航圖是將游戲地圖劃分為多個正方形單元或六邊形單元組成的規則網格,網格點或網格單元的中心可以看作是節點。
網格越小越精細越易得好路徑,但相對需要存儲和搜索打量的節點,對內存要求高,很影響效率。
白色部分表示可行走區域,深灰色區域是不可行走區域。
優點:
易于理解和使用
易于動態更新(如動態增加建筑物或其他障礙等),因為結構很規則
適合在塔防游戲、即時戰略游戲或其他頻繁動態更新場景的游戲中使用。
缺點:
創建可視點導航圖(路點圖)
手工放置一些“路徑點”,如果兩點之間無障礙物遮擋(即兩點之間相互能“看到”),那么可以用一條線段把這兩個點連接起來,生成一條“邊”(可能對“邊”施加一些限制),最后,這些“路徑點”和“邊”就組成了可視點導航圖。
(對邊的長度施加了限制)優點:靈活。
分散在各處的路徑點是精心選擇的,能覆蓋絕大部分可行走區域。
還可以將其他一些重要位置的點包含進去,同時增加相應的信息存儲。
利用這些可用信息,可以高效地實現戰術尋路,計算出某個位置的戰略信息。
缺點:
當場景很大時,手工放置路徑點很繁瑣且易出錯。
它只是一些點和線段的集合,無法表示出實際的二維可行走區域,角色只能沿著那些邊運動。當起始點或終點既不是路徑點也不在邊上時,只能先找到距離最近的路徑點,然后再進行尋路,這樣很可能會得到一條Z字形路線,看上去很不自然。甚至發生更壞的情況。且由于只能沿著邊行走,很難保證生成路徑的質量。
組合爆炸問題。(如果設置1000個路徑點,要測試999*1000條路徑)
創建導航網格
將游戲場景中的可行走區域劃分成凸多邊形。(實現中可以限制多邊形種類)
表示出了可行走區域的真實幾何關系,是一個非均勻網格。
(Unity3D自帶的尋路系統就建立在導航網格的基礎上)
在這個三角形網格上進行A*尋路時,每個節點對應于一個三角形,所以相鄰節點即為與這個三角形相鄰的其他三角形。
估計g和h時,導航網格方式也采用了不同的方法。(例如,可以用三角形質心之間的線段長度作為節點之間的路徑代價g,也可以用三角形的邊的中點之間的距離作為g值等)
將可行走區域劃分成四邊形,尋路用到的節點位于四邊形的中央,利用A*尋路算法,可以找到路徑所經過的那些多邊形。
如果直接把這些多邊形的中心連接起來,就會得到從起始點到目標點的一條路徑。
由于形成的這條灰色曲線路徑顯然不是很理想,最好進一步對它進行處理。
可以采用“視線確定”方法,通過向前跳到視線所及的最遠途經點,使路徑變得更加平滑而自然。(每當到達路徑內的一個路徑點時,就檢查列表中的下幾個路徑點,跳過一些多余的視線內的途經點)
優點:
可以進行精確的點到點移動。(在同一個三角形中的任意兩個點都是直接可達的)
非常高效。(由于多邊形面積可以任意大,只需要較少數量的多邊形就可以表示出很大的可行走區域,占用內存較小,搜索路徑速度有很大提高)
由于游戲場景本身就是由多邊形構成的,因此,通過事先設計好的算法,就能夠自動地將可行走區域劃分成多邊形,生成導航網格,而不需要人工干預。
缺點:
生成導航網格需要較長時間,在地形經常發生動態變化(如經常添加、移除建筑物等)的場景中很少使用,多用于靜態場景中。
A*尋路算法是如何工作的
在A* 算法中,使用了兩個狀態表,分別稱為Open表和Closed表。
Open表由待考查的節點組成,而Closed表由已經考查過的節點組成。
(對于一個節點來說,當算法已經檢查過與它相鄰的所有節點,計算出了這些相鄰節點的f,g和h值,并把它們放入Open表以待考查,那么,這個節點就是“已考查過”的節點。)
開始時,Closed表為空,而Open表僅包括起始節點。
每次迭代中,A*算法將Open表中具有最小代價值(即f值最小)的節點取出進行檢查,如果這個節點不是目標節點,那么考慮該節點的所有8個相鄰節點。
對于每個相鄰節點按下列規則處理:
(1)如果它既不在Open表中,也不在Closed表中,則將它加入Open表中;
(2)如果它已經在Open表中,并且新的路徑具有更低的代價值,則更新它的信息;
( 3)如果它已經在Closed表中,那么檢查新的路徑是否具有更低的代價值。如果是,那么將它從Closed表中移出,加入到Open表中,否則忽略。
重復上述步驟,直到到達目標節點。
如果在到達目標之前,Open表就已經變空,便意味著在起始位置和目標位置之間沒有可達的路徑。
計算代價:
f(總移動代價) = g(從起始節點移動到當前節點的移動代價) + h(從當前節點移動到目標節點所需的移動代價)
g: 取其父節點g值,按照相對于父節點的連接方式,增加相應的值。
歐式距離(歐幾里得距離):連接兩個點的線段的長度,使用勾股定理從這些點的笛卡爾坐標計算距離。(如果是對角線連接+1.414,如果是直角連接+1)
h: 由于AI角色還沒有到達目標位置,因此只能對這個代價做一個大致的估算,采用啟發的方法。(具體應用中,可以采用歐幾里得距離,也可以采用曼哈頓距離,或采用其他的啟發方法。)
曼哈頓距離:通常稱為出租車距離或城市街區距離,用來計算實值向量之間的距離。想象一下均勻網格棋盤上的物體,如果它們只能移動直角,曼哈頓距離是指兩個向量之間的距離,在計算距離時不涉及對角線移動。
用A*算法實現戰術尋路
為不同區域賦予不同的代價值。
A* 尋路的目標就是要尋找一條從起點到終點的總代價最小的路徑。
只需增加危險或困難的區域(例如強火力區域或沼澤等)的代價值。
A* 就會盡量避開這些區域,而選擇更安全或容易的路徑。
規則1:如果這條邊的兩個頂點都位于敵人火力攻擊范圍內,那么將這條邊的行走代價加50;
規則2:如果這條邊只有一一個頂點位于敵人火力攻擊范圍內,那么將這條邊的行走代價加25;
規則3:其他邊的代價保持原值不變。
A* Pathfinding Project 插件的使用
免費版下載地址:http://arongranberg.com/astar/download
A*尋路的適用性
A* 尋路很好用,但它不是萬能的。
選擇哪種尋路方法要充分考慮到游戲的要求,而不是希望它永遠好用。
一般說來,我們通常都是在設計中建立AI能夠處理的情景,而不是對AI做出過高的期待,讓它去處理任意復雜的場景。
(1) A*尋路算法在游戲中具有十分廣泛的應用,利用它可以找到一條從起點到終點的最佳路徑,它的效率在同類算法中也很高,對于大多數路徑尋找問題,它是最佳的選擇。
(2)有一些A*尋路不太適用的場合。例如,如果起點和終點之間沒有障礙物,終點直接在視線范圍內,就完全不必采用它。
另外,這個算法雖然高效,但尋路具有較大的工作量,需要多幀才能完成。如果CPU計算能力較弱,或者需要為大地形尋找路徑,那么計算起來就比較困難了。而且,它也有一定的使用限制。
(3)如果游戲設計者正在為一個Android平臺下的手機游戲選擇尋路算法,就更需要做好權衡。與PC相比,手機的內存資源要珍貴得多,如果需要在很大的空間中進行尋路,最好選擇其他算法,并且,估價算法的開銷也可能會成為瓶頸。
因此,在手機游戲中需要針對不同的尋路要求,選擇不同的實現方法,例如采用深度優先算法、廣度優先算法、遺傳算法等。
(4)在戰斗游戲中,往往希望AI角色能快速從一個地方跑動到另一個地方。絕大多數情況下,想要的路徑并不是最短路徑。
試想,如果一個敵人AI角色試圖逃離玩家的槍彈,結果卻是從玩家指揮的角色身邊跑過去。顯然,路過玩家角色身邊是一個很壞的選擇,應該采用更好的路徑搜索策略。
為了在戰場上做出好的決策,就需要獲取高質量的信息。這些信息來自地形分析、路徑搜索、視距和許多其他系統。它們的開銷很大,為了找到可靠的戰斗位置,往往需要評估許多不同的可能性,因此,這些信息的獲取過程對系統的效率會有很大的影響。
在實際設計中,除了創建更高效的低層系統,更快速地提供信息之外,設計者還需要能夠利用更少的信息,做出更好的AI角色,并且在開發過程中,要始終意識到每一部分信息的代價。
AI角色對游戲世界的感知
AI角色感知信息中視線查詢是必不可少的。在Unity3D中,Raycast調用可以實現視線查詢,But速度相對較慢,當場景中有大量物體時進行調用,或調用過于頻繁時,開銷很大。
AI角色對環境信息的感知方式
輪詢方式
主動查找感興趣的信息的方式。
缺點:
當可能感興趣的事件數量增加時,AI角色就要花大量的時間用于查詢,并且查詢返回的大部分信息都是無用信息,而且很難調試。
讓基于輪詢的感知系統更易維護的方式:建立一個輪詢中心,在這里進行所有的查詢。
優點:
適合事件少的。
(如果AI角色想檢測玩家是否接近,采用輪詢直接查詢玩家的當前位置是最好的選擇。)
事件驅動方式
在事件驅動的感知系統中,有一個中心檢測系統(事件管理器),它查找角色感興趣的事件是否發生。當發生事件時,它會通知每個角色,可以看作是某種事件傳遞機制。
優點:
事件管理器記錄每個AI角色所感興趣的事件,并負責檢查、處理和分發事件。由于條件和檢查是集中完成的,因此可以很方便地記錄和顯示相關信息,非常有利于調試。
實現:
利用Unity3D的物理引擎,為AI角色(或它的子物體)添加一個大半徑(這個半徑與AI角色自身尺寸無關,而取決于它的感知范圍)的Collider組件,選中isTrigger,當Unity3D的物理引擎檢測到碰撞時,就會自動調用OnTriggerEnter函數,這樣,只需在OnTriggerEnter()函數中寫出相應的代碼就可以了。
檢測方式:
- 選擇多個專用的事件管理器,每種事件管理器只處理特定類型的信息,(例如碰撞、聲音或開關狀態等),也只有少量的監聽者(對事件感興趣的角色)。
- 選擇通用的事件管理器,能夠處理各種不同類型的信息。
檢測機制:
- 采用獨立的代碼,以固定的頻率檢測事件是否發生。如果發生,則向事件管理器發送一個事件。
(相當于輪詢游戲世界的狀態,然后將查詢結果與感興趣的所有AI角色分享) - 基于“觸發器”。觸發器是我們希望AI角色能做出反應的任何“刺激源”,是它們觸發了AI角色感興趣的事件,因此,可以直接由它們通知事件管理器發生了某些事件。
常用感知類型的實現
所有觸發器的基類——Trigger類
所有感知器的基類——Sensor類
事件管理器
負責管理觸發器的集合。
維護一個當前所有觸發器的列表,當每個觸發器被創建時,都會向這個管理器注冊自身,加入到這個列表中。
負責更新和處理所有的觸發器,并且當觸發器已過期需要被移除時,從列表中刪除它們。
維護一個感知器列表,每個感知器被創建時,向這個管理者注冊,加入到感知器列表中。
視覺感知
視錐體模擬視覺法:可以用不同的圓錐來模擬不同類型的視覺。一個近距離、大錐角的圓錐可以模擬出視覺中的余光,而遠距離的視覺通常用更長、更窄的圓錐體來表示。
LOD(Level-Of-Detail)邏輯:當AI角色距離玩家很遠時,可以減少感知檢測,降低感知的開銷。
聽覺感知
- 球形區域模擬。
- 當聲音被創建時,為它加上一個強度屬性,隨著傳播距離的增加,聲音強度會衰減,而每個AI角色也有自己的聽覺閾值,如果聲音小于這個閾值,AI角色就聽不到這個聲音。
聲音存在會持續一定時間,然后自行消失。
觸覺感知
可以利用Unity3D的物理引擎來處理。通過為一個游戲物體加上碰撞體,并選中Inspector面板中的isTrigger屬性,就可以把它標記為“觸發器”。觸發器不受物理引擎的控制,當觸發器和另一個Collider發生碰撞時(其中至少有一個附加了Rigidbody組件),會發出3個觸發信息,分別是OnTriggerEnter(當碰撞體Collider進入trigger觸發器時調用),OnTriggerExit(當碰撞體Collider停止觸發trigger時調用),OnTriggerStay(當碰撞體Collider接觸trigger觸發器時,這個函數將在每幀被調用)。在這3個函數中編寫相應的代碼,就可以實現觸覺感知了。
Unity3D已經為觸覺感知提供了事件管理器,所以在事件感知器中,不再需要編寫觸覺相關的代碼。
記憶感知
實現一個SenseMemory類。
這個類具有一個記憶列表,列表中保存了每個最近感知到的對象、感知類型、最后感知到該對象的時間以及還能在記憶中保留的時間,當有一段時間沒感知到這個對象,這個時間超出了記憶時長,就會將這個對象從記憶列表中刪除。
其他類型的感知——血包、寶物等物品的感知
有一些游戲對象,在被一個實體觸發后,會保持一定時間的非活動狀態。
(例如,一些角色可以“撿起”的物件,如血包或武器)當它被撿起后,會在一定時間內處于非活動狀態,之后又重新變為活動的,可以再次被撿起。
這種觸發器都可以由TriggerRespawning類派生。
AI角色自主決策——有限狀態機
決策系統的任務是對從游戲世界中收集到的各種信息進行處理(包括內部信息和外部信息),確定AI角色下一步將要執行的行為。
有兩種行為:
- 更改AI角色的外部狀態。(例如撥動開關、進人房間、開槍等)
- 引起內部狀態的變化。(例如改變AI角色的心情狀態、改變總體目標等)
有限狀態機的FSM圖
有限狀態機(Finite State Machine:FSM) 由一組狀態(包括一個初始狀態)、輸入和根據輸入及現有狀態轉換為下一個狀態的轉換函數組成。
對于游戲AI來說,狀態的關鍵意義是:不同的狀態對應不同的行為。
有限狀態機是AI系統中最簡單的,同時也是最為有效和最常用的方法。
對于游戲中的每個對象,都可以在其生命期內區分出一些狀態。
(例如,一個騎士可能正在武裝自己、巡羅、攻擊或在休息。一個農民可能在收集木材、建浩房屋,或在攻擊下保護自己)
當某些條件發生時,狀態機從當前狀態轉換為其他狀態。
在不同的狀態下,游戲對象會對外部激勵做出不同的反應或執行不同的動作。
有限狀態機方法讓我們可以很容易地把游戲對象的行為劃分為小塊,這樣更容易調試和擴展。
用戶編寫的每個程序都是狀態機。
每當寫下一個if語句的時候,就創造出了一段至少擁有兩個狀態的代碼——寫的代碼越多,程序就可能具有越多的狀態。
Switch和if語句數量的爆發會讓事情很快失去控制,程序會出現奇怪的bug,幾乎不可能理解出現這些bug的原因,最后得到不可預知的后果。這樣,該項目將很難理解和擴展。
有限狀態機是AI中最容易的部分,但是也很容易出錯。在設計有限狀態機的時候,一定要認真地考慮清楚其中的每個狀態和轉換部分。
一個有限狀態機必須要有一個初始狀態,還要保存當前狀態,另外還有一些事件作為觸發狀態轉換的觸發器,這些事件可能來自輸入、關卡中的觸發點、消息或輪詢檢測的某些條件。
表示有限狀態機的最直接的方法是FSM圖。
有限狀態機是一個有著有限數目狀態的機器,其中的一個狀態是當前狀態。
有限狀態機可以接受輸入,這會導致一個基于某些狀態轉移函數的、從當前狀態到輸出狀態的狀態變遷,然后輸出狀態就成為了新的當前狀態。
因此,可以根據FSM圖,建立一個狀態轉移矩陣。
依賴于怪物的當前狀態和有限狀態機的輸人,怪物將會改變狀態,執行基于怪物狀態的游戲代碼,將引起怪物的不同行為。
顯然,可以引入更多的狀態和輸人,增加更多的狀態轉移,這就是利用有限狀態機來創建AI角色行為邏輯的方式。
用Switch語句實現有限狀態機
用FSM框架實現通用的有限狀態機
FSM框架
每個“當前狀態”對應著一個狀態類。
這些狀態類具有相同的基類——FSMState類。
每個狀態類都擁有自己的“字典”,其中存儲著當前狀態所擁有的“轉換——新狀態”對,表明在這個狀態(即這個類所代表的狀態)下,如果發生某個“轉換”事件(即上表的“輸入”),有限狀態機將會轉移到何種新狀態。
FSM框架中還有一個重要的類——AdvancedFSM類,它負責管理所有這些狀態類(如PatrolState、ChaseState等)。
FSMState類——AI狀態的基類
FSMState類是所有狀態類的基類,它的每個派生類都代表了FSM中的某個狀態,即,每個FSM狀態都是從它派生出來。
狀態類中具有添加轉換、刪除轉換的方法,用于管理記錄這些轉換。
在FSMState類中,包含一個字典對象,稱為map,用來存儲“轉換-狀態”對。
表明在當前狀態(即這個類所代表的狀態)下,如果發生某個“轉換”,FSM將會轉移到何種狀態。可以通過類中的AddTransition方法和DeleteTransition方法添加或刪除“轉換―狀態”對。
另外,這個類中還包括Reason方法和Act方法。
其中,Reason方法用來確定是否需要轉換到其他狀態,應該發生哪個轉換。Act方法定義了在本狀態的角色行為。
在AdvanceFSM中定義了兩個枚舉,分別表示可能的轉換和狀態,并且分配了相應的編號。
AdvancedFSM類——管理所有的狀態類
這個類FSM類的派生類,負責管理FSMState的派生類,并且隨著當前狀態和輸入,進行狀態更新。
需要注意的是,這個類中不能出現Start()、Update()以及FixedUpdate()函數,否則將覆蓋基類中的相應函數。
AI角色的復雜決策——行為樹
有限狀態機難以模塊化,編寫代碼麻煩且易出錯。
行為樹層次清晰,易于模塊化,并且可以利用通用的編輯器簡化編程,簡潔高效。
行為樹很適合用做AI編輯器,它為設計者提供了豐富的流程控制方法。
只要定義好一些條件和動作,策劃人員就可以通過簡單的拖拽和設置,來實現復雜的游戲AI。
總結
以上是生活随笔為你收集整理的《Unity3D人工智能编程精粹》笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 卡巴斯基发现医疗IT系统存安全漏洞
- 下一篇: java delphi aes加密算法_