給貓看的游戲AI實(shí)戰(zhàn)(七)行為樹和Behavior Designer插件(下篇)

上節(jié)課我們已經(jīng)對行為樹和Behavior Designer有了感性和有點(diǎn)理性的認(rèn)識 :)。但是通過一個例子就掌握這個插件……那是不可能的。所以這節(jié)課我們繼續(xù),先幫助大家梳理一些基本知識,再提出幾個深入的問題,最后繼續(xù)擴(kuò)展之前的例子。這一節(jié)將是鴻篇巨制,大家可以慢慢享用。
一、重點(diǎn)提示
在講解之前要說明幾個問題:
1、行為樹是一種邏輯工具,對工具的學(xué)習(xí)方法肯定是實(shí)用優(yōu)先。
特地說這個是因?yàn)锽ehavior Designer提供的功能其實(shí)比我們要用的多。作為使用者,務(wù)必記住要先把基本功能搞清楚,在初期那些不必要的高級功能只會把我們的思路搞亂而已。設(shè)計(jì)AI本身已經(jīng)是很燒腦的工作,不建議使用一些很不直觀的修飾器和組合器給自己添亂。而且基本功能已經(jīng)足夠我們組合出非常復(fù)雜而強(qiáng)大的行為樹了。 :)
2、行為樹中的節(jié)點(diǎn),會在某一幀中被調(diào)用,然后立即得到一個結(jié)果:成功Success、失敗Failure、運(yùn)行中Running,只能取三者其一。然后組合器和修飾器會根據(jù)返回值進(jìn)行下一步,這是行為樹的基本邏輯。
3、節(jié)點(diǎn)不是多線程并行的,被調(diào)用的節(jié)點(diǎn)都必須迅速執(zhí)行完畢并返回Running、Success或者Failure。所有事件就算是同時發(fā)生,也總有先后之分。
二、組合器的詳細(xì)介紹
注:本段先略讀一遍,然后下一段咱們會做實(shí)驗(yàn),邊實(shí)驗(yàn)邊閱讀效果更佳。
Sequence 串行的AND
Sequence 類似于編程語言中的"&&"符號,它從左到右,每幀只執(zhí)行一個子節(jié)點(diǎn)。
1、如果當(dāng)前子節(jié)點(diǎn)返回Running,那么Sequence也返回Running。下一幀繼續(xù)執(zhí)行當(dāng)前這個子節(jié)點(diǎn)。
2、如果當(dāng)前子節(jié)點(diǎn)返回失敗,那么Sequence節(jié)點(diǎn)本身返回失敗。
3、如果當(dāng)前子節(jié)點(diǎn)返回成功,如果還有下一個子節(jié)點(diǎn),那么Sequence本身返回Running,下一幀會切換到下一個子節(jié)點(diǎn); 如果所有子節(jié)點(diǎn)都完畢了,則Sequence節(jié)點(diǎn)返回成功,整個節(jié)點(diǎn)結(jié)束。
Selector 串行的OR
Selector與Sequence執(zhí)行順序相同,邏輯正巧是“||”的邏輯。它也是從左到右,每幀只執(zhí)行一個子節(jié)點(diǎn)。
1、如果當(dāng)前子節(jié)點(diǎn)返回Running,那么Selector也返回Running。下一幀繼續(xù)執(zhí)行當(dāng)前這個子節(jié)點(diǎn)。
2、如果當(dāng)前子節(jié)點(diǎn)返回失敗,那么Selector節(jié)點(diǎn)本身返回Running,下一幀執(zhí)行下一個子節(jié)點(diǎn);如果所有子節(jié)點(diǎn)都失敗了,就返回失敗。
3、如果當(dāng)前子節(jié)點(diǎn)返回成功,那么Selector返回成功。
Parallel 并行的AND
Parallel 從返回值來看它是 “&&” 邏輯。與Sequence的區(qū)別是,在每一楨,它都執(zhí)行所有子節(jié)點(diǎn)一次~~。
1、所有子節(jié)點(diǎn)都Running,那么Parallel節(jié)點(diǎn)也返回Running。
2、有任何一個節(jié)點(diǎn)返回失敗,那么Parallel立刻結(jié)束,返回失敗。還處于Running的子節(jié)點(diǎn)也會終止(從界面上可以看出,正在Running的被假設(shè)為失?。?/p>
3、有任何一個節(jié)點(diǎn)返回成功,那么該子節(jié)點(diǎn)下一幀就不會被調(diào)用了,但是Parallel本身仍然返回Running,直到所有子節(jié)點(diǎn)都返回成功,Parallel才返回成功。
Parallel Selector 并行的OR
Parallel Selector 從返回值來看是 “||” 邏輯。它是并行的,每一楨執(zhí)行所有子節(jié)點(diǎn)一次~~。
1、所有子節(jié)點(diǎn)都Running,那么Parallel Selector節(jié)點(diǎn)也返回Running。
2、有任何一個節(jié)點(diǎn)返回失敗,那么Parallel Selector 本身返回Running,直到所有子節(jié)點(diǎn)都失敗了,它才返回失敗。
3、有任何一個節(jié)點(diǎn)返回成功,Parallel Selector 直接返回成功。
好的,我們解釋了四種最基本的節(jié)點(diǎn),只需它們就足夠組成行為樹的骨架。下圖是Composites全圖,我給它們分了組,前面介紹的就是最上面一排基本組。

其它節(jié)點(diǎn)就容易了,我們繼續(xù)看看:
Random Sequence 變體的Sequence(串行)
Sequence是從左到右串行,Random Sequence 也是串行完全一樣,只是它從還沒執(zhí)行過的N個子節(jié)點(diǎn)中隨機(jī)挑選一個執(zhí)行。
Priority Selector, Random Selector 變體的Selector(串行)
這二者是Selector的變體,也都是串行。分別是根據(jù)優(yōu)先級挑選、隨機(jī)挑選、自定義挑選順序。
★ 再強(qiáng)調(diào)一下,串行情況下,如果有節(jié)點(diǎn)還在running,那么肯定先執(zhí)行running的節(jié)點(diǎn)?!疤暨x”的意思是說,在沒有running的節(jié)點(diǎn)時,從還沒執(zhí)行過的節(jié)點(diǎn)中,根據(jù)規(guī)則挑出一個。
Selector Evaluator, Utility Selector 特殊順序的Selector
這兩種類型的特殊之處在于:在每一幀,都要重新計(jì)算子節(jié)點(diǎn)的優(yōu)先級或者效用,就算節(jié)點(diǎn)正在running,也有可能因?yàn)閮?yōu)先級變化而切換節(jié)點(diǎn)。它們既不是并行也不是串行。
Utility Selector 是一種選擇器,它是基于“Utility”也就是“效用”進(jìn)行選擇,用在《模擬人生》這種游戲中會非常有效,就是當(dāng)你面對吃法、睡覺、上廁所這三件事時,你選效用最大的那一件事去做即可,而且如果有必要可以隨時終止當(dāng)前正在做的事情。
Selector Evaluator 涉及到優(yōu)先級的問題,暫且不表。
三、組合器和返回值實(shí)驗(yàn)+詳解
前面的講解過于抽象,咱們可以做下面這樣的一個行為樹,掛在任意一個GameObject上面,直觀感受各種組合器的特性:

說明:新建GameObject到場景中,并為它創(chuàng)建一個行為樹如上圖。四個動作節(jié)點(diǎn)是Wait節(jié)點(diǎn),在行為樹的Inspector窗口里,把Wait節(jié)點(diǎn)的等待時間分別改為2、1、2、3秒。然后執(zhí)行效果如下:

通過一些簡答的例子,瞬間就對組合器功能有了直觀認(rèn)識。之后再看前面的介紹,就可以掌握這些組合器的用法了。

小技巧:用Replace功能即可快速替換節(jié)點(diǎn)類型。
還有修飾器的作用就不再詳細(xì)表述了,大概列舉如下:
Inverter:條件判斷或動作的返回結(jié)果取反,成功變失敗,失敗變成功,Running不變。
Reapter:循環(huán)執(zhí)行,可以調(diào)節(jié)循環(huán)次數(shù)等參數(shù)。
Return Failure:返回值無論成功或失敗都返回失敗,但Running還是Running。
Return Success:同上,相反。
Until Failure:循環(huán)直到失敗,換句話說如果成功就再次執(zhí)行子節(jié)點(diǎn)。
Until Success:同上,相反。
還有4種其他裝飾器自行查閱。
四、變量應(yīng)當(dāng)保存在行為樹中,還是角色腳本中?
思考這樣一個問題:如果我們不用Bahavior Designer插件,那么腳本中也有各種角色相關(guān)的參數(shù)和變量,而如果用了插件,那么也可以把變量放在行為樹里面,就好比之前用過的SharedTransform等等變量。
選擇多了麻煩也多,到底把變量放在角色腳本中還是行為樹里面呢?好在有辦法可以在行為樹中訪問角色的變量,這樣一來還是比較方便的。
例如,AI角色開火動作的腳本:
public class FireAction : Action
{
// The transform that the object is moving towards
CharacterData chaData;
public override void OnAwake()
{
chaData = gameObject.GetComponent<CharacterData>();
}
}
如意上面的CharacterData chaData,這個就是我的NPC角色身上的一個腳本組件。用上面的寫法,就可以在動作腳本中訪問其他腳本的變量或者調(diào)用角色的函數(shù)了:
// 還是FireAction的OnUpdate函數(shù)
public override TaskStatus OnUpdate()
{ // 調(diào)用角色的方法
chaData.Fire();
return TaskStatus.Success;
}
到底哪些函數(shù)和變量放在角色腳本中,哪些函數(shù)和變量放在行為樹的動作腳本中?這是一個工程問題了,沒有統(tǒng)一的方法。
個人建議:所有角色通用的變量和方法,放在角色腳本中。因?yàn)榧词共挥肂ehavior Designer插件,這些變量和方法也是有用的。而只用于AI的變量,放在行為樹的Variables里面即可,由專門負(fù)責(zé)AI設(shè)計(jì)的人員維護(hù)。例如:角色移動速度、開火CD時間,都是角色本身的變量。而發(fā)現(xiàn)敵人的transform、距離敵人的距離,如果只在AI邏輯中用到,就應(yīng)該放在行為樹中。
★ 也有辦法在角色腳本中訪問行為樹的變量,可以查閱官方文檔和資料。但是我們盡可能避免這種用法,這種反向耦合對項(xiàng)目整潔不利。
五、復(fù)雜一些的行為樹實(shí)戰(zhàn)
為了綜合運(yùn)用所有行為樹的知識,我又做了一個復(fù)雜的例子。
我們的目標(biāo)是在之前的例子的基礎(chǔ)上,給AI增加如下功能:
1、靈活利用建筑進(jìn)行防御。當(dāng)玩家從右側(cè)接近,則走到預(yù)定地點(diǎn)1防御;如果玩家從左側(cè)接近,則走到預(yù)定地點(diǎn)2防御;如果玩家從中間接近,走到預(yù)定地點(diǎn)3防御。

2、處理被狙擊的特殊情況:如果玩家從超遠(yuǎn)距離狙擊到AI,如果AI不做出反應(yīng),則會被玩家慢慢消滅掉。所以AI必須對狙擊情況做出回應(yīng)——也就是進(jìn)攻。

實(shí)現(xiàn)這兩點(diǎn)之后,咱們的AI雖然精簡,但也算是麻雀雖小五臟俱全了,哈哈。抽象地說,我們的AI現(xiàn)在既能利用環(huán)境做出掩護(hù)動作,又能處理特殊情況防止玩家利用BUG。下圖是我最終做完的行為樹截圖:

六、總結(jié)
至此,我們的Behavior Designer學(xué)習(xí)已經(jīng)告一段落了。感覺大概還有40%的東西我們沒有用到也沒有講到,不過已經(jīng)不重要了,畢竟這個插件本身也在演化進(jìn)步之中,說不定過半年又會升級推出更多新功能。
最重要的是我們能將它用在我們的項(xiàng)目里,做出更好的AI,不是嗎?對游戲開發(fā)來說最好的方法還是邊做邊學(xué)。
我個人很佩服這些行為樹可視化插件的設(shè)計(jì)思路,感覺一旦用了這些插件,可以從更高的層次觀察和理解AI邏輯的設(shè)計(jì),和之前自己用 if else 寫出的狀態(tài)機(jī),有了巨大的變化。
這種慢慢造輪子的行為也值得我們學(xué)習(xí),游戲開發(fā)行業(yè)很需要這種基本工具的研究和積累,再發(fā)展幾年,這些工具有可能像UGUI一樣,成為新的事實(shí)標(biāo)準(zhǔn)。
做完了這一期專欄,我感覺自己已經(jīng)掌握了大部分游戲AI開發(fā)的基礎(chǔ)知識,不知道讀者有沒有和我一樣的感覺。曾經(jīng)提到過一本書《The Practical Game AI Programming》,本系列文章至此已經(jīng)涵蓋了該書大部分內(nèi)容,而且是從另一個角度做的示范,可以起到對照學(xué)習(xí)的作用。
下一期的內(nèi)容還沒有想好做什么,大概可能是繼續(xù)做多AI交互的例子吧(比如體育游戲),可能會多花一些時間準(zhǔn)備,敬請期待!
工程地址:
https://github.com/mayao11/PracticalGameAI/tree/master/AI_Enemy3_Behavior
(我的PracticalGameAI工程的AI_Enemy3_Behavior目錄)
對游戲開發(fā)感興趣的同學(xué),歡迎圍觀我們:【皮皮關(guān)游戲開發(fā)教育】 ,會定期更新各種教程干貨,更有別具一格的線下小班教育。
我們的官網(wǎng)地址:http://levelpp.com/
我們的游戲開發(fā)技術(shù)交流群:610475807
我們的微信公眾號:皮皮關(guān)