游戏AI之决策结构—行为树
目錄
有限狀態機
行為樹
?
- 控制節點
- 條件節點
- 行為節點
- 裝飾節點
- 總結
- 額外
游戲AI的決策部分是比較重要的部分,游戲程序的老前輩們留下了兩種經過考驗的用于AI決策的結構:
?
- 有限狀態機
- 行為樹
在以前,游戲AI的實現基本都是有限狀態機,
隨著游戲的進步,游戲AI的復雜性要求越來越高,傳統的有限狀態機實現很難維護越來越復雜的AI需求。
現代游戲AI都比較偏向采用行為樹作為決策結構。
有限狀態機
有限狀態機的一般實現是將每個狀態寫成類,再用一個載體(也就是所謂的狀態機)管理這些狀態的切換。
關于狀態機設計模式的具體介紹,可參考我的另一篇博文:https://www.cnblogs.com/KillerAery/p/9680303.html
有限狀態機的缺陷:
?
- 各個狀態類之間互相依賴很嚴重,耦合度很高。
- 結構不靈活,可擴展性不高,難以腳本化/可視化。
行為樹
?
?
(一個武裝小隊隊員的AI行為樹示例)
可以看到,行為樹由一個個節點組成
?
- 結構:樹狀結構
- 運行流程:從根節點開始自頂向下往下走,每經過一個節點就執行節點對應的功能。
我們規定,每個節點都提供自己的執行函數,返還執行失敗/成功結果。
然后根據不同節點的執行結果,往下的路徑隨之改變,最終總會走到末尾的葉節點,執行其對應的行為。
?
主流的行為樹實現,將節點主要分為四種類型:
?
- 控制節點(非葉節點)
- 條件節點(葉節點)
- 行為節點(葉節點)
- 裝飾節點(非葉節點)
控制節點
控制節點是用于控制如何執行子節點。
由于非葉節點的特性,其需要提供容納子節點的容器和添加子節點的函數。
所以先寫好非葉節點的類:
?
下面列出一些控制節點的介紹:
選擇節點(Selector)
按順序執行多個子節點,手游賬號買號平臺若成功執行一個子節點,則不繼續執行下一個子節點。
舉例:實現要不攻擊,要不防御,要不逃跑。
用一個選擇節點,按順序添加<攻擊節點>和<防御節點>和<逃跑節點>作為子節點。
?
順序節點(Sequence)
按順序執行多個子節點,若遇到一個子節點不能執行,則不繼續執行下一個子節點。
?
舉例:實現先開門再移動到房子里。
用一個順序節點,按順序添加<開門節點>和<移動節點>作為子節點。
000
并行節點(Parallel)
同時執行多個節點。
?
舉例:一邊說話和一邊走路。
用一個并行節點,添加<說話節點>和<走路節點>作為子節點。
?
常用的控制節點一般是<并行節點><選擇節點><并行節點>。當然還有其他更多控制節點種類(不常用):
?
- 隨機選擇節點(隨機執行一個子節點)。例如偶爾閑逛,偶爾停下來發呆。
- 隨機順序節點(隨機順序執行若干個子節點)
- 次數限制節點(只允許執行若干次)
- 權值選擇節點(執行權值最高的子節點)
- 等等..
可能到這里,有想到還有個問題:為什么控制節點也需要提供(執行成功/執行失敗)兩種執行結果。
答:這樣做就可以做到決策的復合——控制節點不僅可以控制行為節點,也能控制控制節點。
條件節點
前提條件
執行節點不會總是一帆風順的,有成功也總會有失敗的結果。
這就是引入前提條件的作用——滿足前提條件,才能成功執行行為,返還<執行成功>結果。否則不能執行行為,返還<執行失敗>結果。
?
但是每個節點的前提總會不同,或有些沒有前提(換句話說總是能滿足前提)。
一個可行的做法是:讓行為節點含有bool函數對象(或函數接口)。這樣對于不同的邏輯條件,就可以寫成不同的bool函數,綁定給相應的行為節點。
?
但是一種更復雜也更成熟的做法則是把前提條件抽象分離成新的節點類型,稱之為條件節點。
將其作為葉節點混入行為樹,輔助控制節點決策。它相當模塊化,更加方便適用。
?
這里的se節點,能夠讓其所有子節點依次運行,若運行到其中一個子節點失敗則不繼續往下運行。
這樣可以實現出不滿足條件則失敗的效果。
只是由于邏輯條件的種類繁多,其編寫各種條件節點類需要花費一定時間,不過我們可以使用別人現成寫好的庫,
亦或者仍然基于bool函數對象,相當于上文做法的裝飾。
?
行為節點
行為節點是代表行為的葉節點,其執行函數一般位該節點代表的行為。
行為節點的類型是比較多的,畢竟一個智能體的行為是多種多樣的,而且都得根據自己的智能體模型定制行為節點類型。
這里列舉一些行為:站立,射擊,移動,跟隨,遠離,保持距離....
行為狀態
一些行為是可以瞬間執行完的(例如轉身?),
而另外一些動作則是執行持續一段時間才能完成的(例如攻擊從啟動攻擊行為到攻擊結算要1秒左右的時間)
為了不讓每幀重復啟動執行一個持續行為,
我們給所有行為節點引入一個成員變量來標志,我們稱為<行為狀態>。
行為狀態一般有2種:
?
- ready(可執行)
- running(正在執行)
另外可根據自己實際項目需求來定制狀態(例如加入fail狀態)。
行為節點示例實現
?
裝飾節點
裝飾節點,顧名思義,是用來裝飾輔助的節點。
例如執行結果取反/并/或,重復執行若干次等輔助修飾節點的作用,均可做成裝飾節點。
?
OneChildNonLeafNode是指最多可擁有一個子節點的非葉節點類,這里就不做具體實現。
總結
到這里,我們可以看到行為樹的本質:
?
- 把所有行為(走,跑,打,站等等)分離出來作為各種<行為節點>,
- 然后以不同的<控制節點><條件節點><裝飾節點>將這些行為復合在一起,組合成一套復雜的AI。
相比較傳統的有限狀態機:
?
- 易腳本化/可視化的決策邏輯
- 邏輯和實現的低耦合,可復用的節點
- 可以迅速而便捷的組織較復雜的行為決策
這里并不是說有限狀態機一無所用:
?
- 狀態機可以搭配行為樹:狀態機負責智能體的身體狀態,行為樹則負責智能體的智能決策。這樣在行為樹做決策前,得考慮狀態機的狀態。
- 狀態機適用于簡單的AI:對于區區需兩三個狀態的智能,狀態機解決綽綽有余。
- 狀態機運行效率略高于行為樹:因為狀態機的運行總是在當前狀態開始,而行為樹的運行總在根開始,這樣就額外多了一些要遍歷的節點(也就多了一些運行開銷)。
在《殺手:赦免》的人群系統里,人群的狀態機AI只有簡單的3種狀態,由于人群的智能體數量較多,若采取行為樹AI,則會大大影響性能。
簡言之:行為樹是適合解決復雜AI的解決方案。
對于Unity用戶,Unity商店現在已經有一個比較完善的行為樹設計(Behavior Designer)插件可供購買使用。
關于行為樹運行流程,有兩個實現方式,博主只簡單實現過第二個實現方式,這里對于第一種方式就不做詳解。
?
- 一次性流程:Unity行為樹插件目前的實現方式。一次性流程可能會阻塞在延時行為,但是可以利用協程等待該其行為完成而不產生阻塞。
- 每幀重復從根節點開始自頂向下執行的流程:這種方式不會產生阻塞,因為若有延時行為在運行中則視為執行成功,繼續往下走。
缺點是每幀都得從根節點重新往下走,效率可能不如一次性流程(一次性走完行為樹,而不會從頭走起)。
額外
?
- 可讓根節點記錄該AI要操控的智能體引用(指針),每次進行決策,傳給子節點當前要操控的智能體引用。這樣就可以使AI行為樹容易改變寄主。
(例如1個喪尸死了被釋放內存了,寄生它的AI行為樹不必釋放并標記為可用。一旦產生新的喪尸,就可以給這個行為樹根節點更換新的寄主,標記再改回來)
?
- 得益于樹狀結構,重復執行次數節點(或其他類似的節點),可以讓它執行完相應的次數后,解開與父節點的連接,釋放自己以及自己的子節點。
- 共享節點型行為樹是可供多個智能體共用的一種行為樹,是節省內存的一種設計:
- LOD優化技術:LOD原本是3D渲染的優化技術。對于遠處的物體,渲染面數可以適當減少,對于近處的物體,則需要適當增加細節渲染面數。
同樣的可以用于AI上,對于遠處的AI,不需要精準每幀執行,可以適當延長到每若干幀執行。
總結
以上是生活随笔為你收集整理的游戏AI之决策结构—行为树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从零开始做一个SLG游戏(一):六边形网
- 下一篇: 建立海盗的天堂:盗贼之海的AI(一)