Behavior Designer插件(下)

一、重點(diǎn)提示

1、行為樹是一種邏輯工具,對工具的學(xué)習(xí)方法肯定是實(shí)用優(yōu)先。
特地說這個是因?yàn)锽ehavior Designer提供的功能其實(shí)比我們要用的多。作為使用者,務(wù)必記住要先把基本功能搞清楚,在初期那些不必要的高級功能只會把我們的思路搞亂而已。設(shè)計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è)為失?。?。
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全圖,我給它們分了組,前面介紹的就是最上面一排基本組。

image.png

其它節(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

這兩種類型的特殊之處在于:在每一幀,都要重新計算子節(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上面,直觀感受各種組合器的特性:


image.png

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


image.gif

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

小技巧:用Replace功能即可快速替換節(jié)點(diǎn)類型。

還有修飾器的作用就不再詳細(xì)表述了,大概列舉如下:
1.Inverter:條件判斷或動作的返回結(jié)果取反,成功變失敗,失敗變成功,Running不變。
2.Reapter:循環(huán)執(zhí)行,可以調(diào)節(jié)循環(huán)次數(shù)等參數(shù)。
3.Return Failure:返回值無論成功或失敗都返回失敗,但Running還是Running。
4.Return Success:同上,相反。Until Failure:循環(huán)直到失敗,換句話說如果成功就再次執(zhí)行子節(jié)點(diǎn)。5.Until Success:同上,相反。

四、變量應(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è)計的人員維護(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防御。


image.jpeg

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


image.jpeg

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

感謝皮皮關(guān)的分享,轉(zhuǎn)載于皮皮關(guān)的>https://zhuanlan.zhihu.com/p/29598709

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容