最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

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

2017-10-11 18:21 作者:皮皮關(guān)做游戲  | 我要投稿

本系列第三節(jié)花了很長(zhǎng)篇幅介紹怎樣用有限狀態(tài)機(jī)(FSM)實(shí)現(xiàn)一個(gè)AI敵人。當(dāng)時(shí)實(shí)現(xiàn)的敵人狀態(tài)較少,智商也很捉急。本來我打算在第四節(jié)改進(jìn)那個(gè)敵人,為它加上尋找掩體等策略。但是由于邏輯過于復(fù)雜,不利于講解,就改為討論其他內(nèi)容了。

其實(shí)不繼續(xù)講狀態(tài)機(jī)還有個(gè)原因——用狀態(tài)機(jī)實(shí)現(xiàn)敵人AI之后,我又嘗試了行為樹的模式以及Behavior Tree插件,花了幾天時(shí)間才摸索出了大概,將AI邏輯重新實(shí)現(xiàn)了一遍。如果你能和我一起對(duì)比著用狀態(tài)機(jī)和行為樹實(shí)現(xiàn)同樣的AI,那么收獲會(huì)非常大,也會(huì)比較有成就感。

本文將再次探討AI邏輯的核心問題,(不像前兩章那樣在外圍問題上劃水了( ̄ y ̄))。那么,和我一起來進(jìn)入AI的世界吧!


1、行為樹與狀態(tài)機(jī)對(duì)比

舉個(gè)簡(jiǎn)單的例子,還是咱們第二節(jié)講過的問題。設(shè)計(jì)一個(gè)簡(jiǎn)單的AI士兵,他具有以下特性:

他具有真實(shí)視野,發(fā)現(xiàn)敵人則進(jìn)攻。

進(jìn)攻時(shí)會(huì)瞄準(zhǔn)敵人并射擊,同時(shí)跑向敵人。

離初始位置過遠(yuǎn),有可能是被調(diào)虎離山了,這時(shí)要回到初始位置。

進(jìn)攻時(shí)離敵人過遠(yuǎn),代表敵人逃跑成功,也要回到初始位置。

畫一個(gè)簡(jiǎn)單的狀態(tài)機(jī):

很簡(jiǎn)單吧,我們之前在Update函數(shù)中用一些 if else 條件判斷,就實(shí)現(xiàn)了這個(gè)狀態(tài)機(jī)。后來我想改進(jìn)這個(gè)狀態(tài)機(jī),但是改進(jìn)的AI士兵過于復(fù)雜了,咱們看看到底能復(fù)雜到什么程度:

對(duì)于游戲設(shè)計(jì)師來說,我們只增加了一個(gè)簡(jiǎn)單的功能:當(dāng)AI士兵被遠(yuǎn)距離狙擊時(shí),就跑到建筑內(nèi)部的指定位置,以躲避狙擊手的攻擊。這個(gè)功能會(huì)增加兩個(gè)狀態(tài),就是上圖左邊的:跑向掩體和掩體待機(jī)。

問題是——數(shù)一數(shù)連線,最早我們有4條有效線段(去掉2個(gè)“無”的連線就是4條),代表4種狀態(tài)轉(zhuǎn)移的條件。而改進(jìn)后的AI士兵,多出了5條線段,比原來的復(fù)雜度擴(kuò)大了一倍還多。

9種狀態(tài)轉(zhuǎn)移并不多,問題是:這樣子的AI士兵還遠(yuǎn)遠(yuǎn)達(dá)不到設(shè)計(jì)要求,為了讓AI能應(yīng)付各種情況,我們最終可能需要12種狀態(tài)。那么這12種狀態(tài)如果用狀態(tài)機(jī)管理,線段會(huì)有多少條呢?去掉某些不可能的狀態(tài)轉(zhuǎn)移,少說需要幾十條線段吧……這可不是什么好消息。


下面我們直觀的看一下用行為樹解決同樣的問題,是什么樣子的:

是不是有一種耳目一新的感覺? ( ̄ y ̄)~*

從直觀感受上來說,狀態(tài)機(jī)是以多個(gè)狀態(tài)為核心,以狀態(tài)轉(zhuǎn)移為線索的一種圖表。

而行為樹是以行為邏輯為框架,以具體行動(dòng)作為節(jié)點(diǎn)的一種樹狀圖。

上圖我們簡(jiǎn)單改變一下寫法,就變成了更容易理解的形式:

黃色節(jié)點(diǎn)別我翻譯為大家容易理解的 and &&、or || 和while循環(huán),暫且可以這么理解。而紅色節(jié)點(diǎn),是一種判斷行為(相當(dāng)于if語句),綠色節(jié)點(diǎn),是真正的行動(dòng)Action節(jié)點(diǎn)。這個(gè)圖是從示例工程中實(shí)際用到的行為樹簡(jiǎn)化而來的,非常具有說服力。

而實(shí)際使用中樹的結(jié)構(gòu)遠(yuǎn)遠(yuǎn)沒有這么簡(jiǎn)單,行為樹會(huì)引出很多新的概念與使用要點(diǎn),而且我們會(huì)用Behavior Designer這款大名鼎鼎的行為樹插件來作為示范,在后面我們會(huì)詳細(xì)解釋。


2、Behavior Designer 簡(jiǎn)單介紹

借用Behavior Designer官方文檔的介紹:

Behavior Designer 是一個(gè)行為樹插件!是為了讓設(shè)計(jì)師,程序員,美術(shù)人員方便使用的可視化編輯器!Behavior Designer 提供了強(qiáng)大的 API 可以讓你輕松的創(chuàng)建 tasks(任務(wù)),配合 uScript 和 PlayMaker 這樣的插件,可以不費(fèi)吹灰之力就能夠創(chuàng)建出強(qiáng)大的 AI 系統(tǒng),而無需寫一行代碼! 

其實(shí),按照我的理解,Behavior Designer的主要作用并非可以不寫代碼(還是要寫不少代碼的),而是能讓游戲中邏輯最混亂的模塊——AI模塊能更有序的組織,方便查看、調(diào)試和修改。

可以打開我們的示例工程,或者安裝Behavior Designer插件。該插件是以Package的形式提供的,可以任意拷貝,本文不提供盜版下載( ̄工 ̄lll) 。

1、打開Behavior Designer窗口的方法如圖:

2、為任意對(duì)象添加Behavior組件:

如圖,在上面的菜單里選擇“Add Behavior Tree”即可。觀察該GameObject的屬性,可以在下圖中可以看到這個(gè)組件實(shí)際上是一個(gè)腳本,默認(rèn)參數(shù)目前不需要任何修改。

下面開始按步驟實(shí)現(xiàn)并講解一個(gè)基本的行為添加過程,如有懵圈的情況,及時(shí)詢問或查找網(wǎng)上詳細(xì)的Behavior Designer資料。


3、現(xiàn)在可以編輯這個(gè)對(duì)象的Behavior Tree了:

要點(diǎn)1、按照步驟1可以打開Behavior Designer編輯窗口,在窗口打開的情況下點(diǎn)擊包含了BTree組件的對(duì)象,就可以對(duì)它進(jìn)行編輯:

這里添加一個(gè)Task -> Decorators -> UntilSuccess節(jié)點(diǎn),它是一種Decorator即修飾器,修飾器在行為樹中起到骨架的作用,就像是程序里的循環(huán)和判斷一樣不可或缺。我現(xiàn)在要實(shí)現(xiàn)視野范圍的功能,在發(fā)現(xiàn)敵人以后,把敵人信息記下來。這就需要一個(gè)定制化的動(dòng)作——判斷敵人是否在視野中,這個(gè)動(dòng)作起名為WithInSight。

先添加一個(gè)WithInSight.cs腳本才能加入到Behavior Designer窗口里,代碼如下,已經(jīng)加上了詳細(xì)注釋:


public class WithinSight : Conditional

{

    // 視野角度

    public float fieldOfViewAngle;

    // 目標(biāo)物體的Tag

    public string targetTag;

    // 發(fā)現(xiàn)目標(biāo)時(shí),將目標(biāo)對(duì)象設(shè)置到BahaviorTree共享變量里面去

    public SharedTransform target;

    public SharedVector3 targetPos;

    // 所有指定Tag的物體的數(shù)組

    private Transform[] possibleTargets;


    // 重載函數(shù),Behavior Designer專用的Awake

    public override void OnAwake()

    {

        // 根據(jù)Tag查找到所有物體,全部加入數(shù)組

        var targets = GameObject.FindGameObjectsWithTag(targetTag);

        possibleTargets = new Transform[targets.Length];

        for (int i = 0; i < targets.Length; ++i)

        {

            possibleTargets[i] = targets[i].transform;

        }

    }

    // 重載函數(shù),Behavior Designer專用的Update

    public override TaskStatus OnUpdate()

    {

        // 判斷目標(biāo)是否在視野內(nèi),這個(gè)返回值TaskStatus很關(guān)鍵,會(huì)影響樹的執(zhí)行流程

        for (int i = 0; i < possibleTargets.Length; ++i)

        {

            if (withinSight(possibleTargets[i], fieldOfViewAngle, 10))

            {

                // 將目標(biāo)信息填寫到共享變量里面,這樣其它Action就可以訪問它們了

                target.Value = possibleTargets[i];

                targetPos.Value = target.Value.position;

                Debug.Log("Find Target" + targetPos.Value);

                // 成功則返回 TaskStatus.Success

                return TaskStatus.Success;

            }

        }

        // 沒找到目標(biāo)就在下一幀繼續(xù)執(zhí)行此任務(wù)

        return TaskStatus.Running;

    }


    // 判斷物體是否在視野范圍內(nèi)的方法

    public bool withinSight(Transform targetTransform, float fieldOfViewAngle, float distance)

    {

        Vector3 direction = targetTransform.position - transform.position;

        if (direction.magnitude > distance)

        {

            return false;

        }

        return Vector3.Angle(direction, transform.forward) < fieldOfViewAngle;

    }

}


WithInSight是一種判斷條件,而不是實(shí)際的行為動(dòng)作,所以繼承了Conditional,這種繼承表示了對(duì)Behavior的擴(kuò)展。重點(diǎn)函數(shù)是OnAwake和OnUpdate,這個(gè)有別于MonoBehavior,是Behavior Designer插件專用的。寫好了這段代碼之后,再到行為樹窗口里去,就多了一個(gè)選項(xiàng):

順理成章,把該連的線連起來。

現(xiàn)在你大概知道Behavior Designer的基本玩法了,簡(jiǎn)單來說就是用Decorator(修飾器)和Composites(組合器)搭邏輯框架,然后自定義Conditional(條件)和Action(動(dòng)作)來實(shí)際判斷和實(shí)際做出行為,僅此而已。

現(xiàn)在進(jìn)行測(cè)試,在玩家靠近AI時(shí),應(yīng)該能觸發(fā)Debug.Log,如果OK的話,說明你邁出了第一步。

這還沒完,咱們處理一下代碼中的2個(gè)shared變量。


4、關(guān)于Shared變量的介紹

注意代碼中的這兩個(gè)變量:

// 發(fā)現(xiàn)目標(biāo)時(shí),將目標(biāo)對(duì)象設(shè)置到BahaviorTree共享變量里面去

    public SharedTransform target;

    public SharedVector3 targetPos;

SharedXXXX類型代表這個(gè)變量雖然是在這個(gè)類中定義的,但是在正確綁定以后,其他Action或者Conditional也可以訪問到。簡(jiǎn)單來說,它們就是專門用在Behavior Designer內(nèi)部的變量。對(duì)這種變量不僅要在代碼里聲明,還要在Behavior Designer窗口里進(jìn)行正確設(shè)置。

之前咱們直接畫圖了,沒有用到這里的四個(gè)窗口。介紹一下

(1) Behavior 狀態(tài)樹整體的名稱和屬性,對(duì)咱們的小項(xiàng)目來說默認(rèn)就行,不用管。

(2) Tasks所有Conditional(條件)和Action(動(dòng)作)的列表,按照我的開發(fā)習(xí)慣,較少用到內(nèi)置的條件和動(dòng)作。理由是內(nèi)置動(dòng)作功能太單一,組合起來樹狀圖會(huì)變得極其復(fù)雜,還不如用代碼清晰。這個(gè)問題見仁見智了。

(3) Variables變量窗口,接下來咱們主要介紹這個(gè)。

(4) 行為樹節(jié)點(diǎn)的Inspector,是針對(duì)某個(gè)節(jié)點(diǎn)的詳細(xì)屬性。在這里面不僅可以設(shè)置參數(shù),還能綁定變量,接下來也要用到。


5、添加Shared變量

只要切換到Variables變量窗口,輸入變量名稱,選擇咱們腳本里定好的類型,然后Add即可。Add之后如下圖:

已經(jīng)添加好了,其他參數(shù)都不要變,紅圈也不要改,因?yàn)檫@個(gè)變量的賦值是由腳本負(fù)責(zé)的。

添加好另一個(gè)變量TargetPos:

我們要讓AI角色發(fā)現(xiàn)敵人時(shí),記住他的transform和位置,以便后續(xù)處理,這些變量就和他的大腦記憶一樣。所以,要把WithInSight節(jié)點(diǎn)和這些變量關(guān)聯(lián)起來。


6、關(guān)聯(lián)節(jié)點(diǎn)和變量

點(diǎn)擊WithInSight節(jié)點(diǎn),點(diǎn)擊Inspector,可以看到這個(gè)節(jié)點(diǎn)的所有屬性。普通的屬性就直接設(shè)定初始值就ok了,比如第一個(gè)屬性可視范圍是45。重點(diǎn)是對(duì)Shared變量進(jìn)行綁定操作,現(xiàn)在是紅色的None。

如果這里和我的截圖不一致,就點(diǎn)擊黑色圓點(diǎn),切換一下綁定方式。如下圖操作:

這樣就能把Variables窗口里剛才新建的Target變量,和WithinSight判斷中的Target變量徹底聯(lián)系起來。同理對(duì)TargetPos也要做同樣操作。


7、試著加一個(gè)動(dòng)作,進(jìn)行試驗(yàn)

增加一個(gè)動(dòng)作AimAction瞄準(zhǔn)動(dòng)作,實(shí)際上就是轉(zhuǎn)向Target的方向即可,如果AI能轉(zhuǎn)向敵人方向,就代表我們的綁定成功了。先按咱們第3步也就是創(chuàng)建WithinSight的方法,創(chuàng)建一個(gè)腳本叫AimAction.cs,內(nèi)容如下:


public class AimAction : Action

{

    public SharedTransform target;


    // 是否正在面對(duì)入侵者,即已經(jīng)正確瞄準(zhǔn)

    bool IsFacingTarget()

    {

        if (target.Value == null)

        {

            return false;

        }

        Vector3 v1 = target.Value.position - transform.position;

        v1.y = 0;

        if (Vector3.Angle(transform.forward, v1) < 1)

        {

            return true;

        }

        return false;

    }


    // 轉(zhuǎn)向入侵者方向,每次只轉(zhuǎn)一點(diǎn),速度受turnSpeed控制

    void RotateToTarget()

    {

        if (target.Value == null)

        {

            return;

        }

        Vector3 v1 = target.Value.position - transform.position;

        v1.y = 0;

        Vector3 cross = Vector3.Cross(transform.forward, v1);

        float angle = Vector3.Angle(transform.forward, v1);

        transform.Rotate(cross, Mathf.Min(2, Mathf.Abs(angle)));

    }


    public override void OnAwake()

    {

    }


    public override TaskStatus OnUpdate()

    {

        if (IsFacingTarget())

        {

            // 返回值不同對(duì)狀態(tài)樹會(huì)產(chǎn)生巨大影響,可以對(duì)比測(cè)試

            //return TaskStatus.Success;

            return TaskStatus.Running;

        }

        RotateToTarget();

        return TaskStatus.Running;

    }

}


然后修改行為樹的圖,添加AimAction和一些聯(lián)系用的Composite節(jié)點(diǎn),改為下面的形式:

中間的Sequence代表下面的兩個(gè)子節(jié)點(diǎn)依次執(zhí)行,UntilSuccess構(gòu)成一個(gè)局部的反復(fù)執(zhí)行邏輯,AI會(huì)在左邊的子節(jié)點(diǎn)重復(fù),直到發(fā)現(xiàn)敵人,Until節(jié)點(diǎn)中斷,執(zhí)行Aim瞄準(zhǔn)動(dòng)作。

別忘了給AimAction節(jié)點(diǎn)也綁定兩個(gè)Shared變量。如果發(fā)現(xiàn)像下圖這樣,和之前說好的不一樣,就點(diǎn)擊黑色圓點(diǎn),切換一下綁定方式。點(diǎn)擊黑點(diǎn)實(shí)際上是切換兩種不同的變量使用方式。

現(xiàn)在,如果你操作沒錯(cuò),那么播放游戲,看看敵人是否在發(fā)現(xiàn)你之后,就瞄準(zhǔn)你:

如上圖,不僅AI角色能正確瞄準(zhǔn)主角,而且在Behavior Designer窗口中,還能實(shí)時(shí)看到目前邏輯進(jìn)行的狀態(tài),這個(gè)是Behavior Designer插件威力最大的功能之一——查看邏輯進(jìn)展?fàn)顟B(tài),將AI思考過程可視化(符合咱們第四節(jié)講的原則 :D)。


8、擴(kuò)展實(shí)現(xiàn)所有功能

老師領(lǐng)進(jìn)門,修行在個(gè)人。所有基本功能都介紹完畢,至于實(shí)際的使用方法需要大家自己分析一下了。

我用Behavior Designer重現(xiàn)了咱們第三節(jié)講過的內(nèi)容,得到了很好的效果,而且很好修改。

不要被嚇著了哦,這是一步一步做了很久的最終效果。而且雖然節(jié)點(diǎn)很多,我加上注釋之后,其實(shí)也就分三大塊而已,看起來還是有狀態(tài)機(jī)的影子,不難理解。

如上圖,和咱們講行為樹原理時(shí)用的概念圖基本是一致的。某些Composite前面沒講,下一篇文章會(huì)繼續(xù)深入,當(dāng)然自己查資料試驗(yàn)才是最好的學(xué)習(xí)方法。


3、總結(jié)

首先,讀者如果做下來的話,建議再回到本文開頭,看一下我畫的狀態(tài)機(jī)和行為樹對(duì)比圖,加深一下理論印象,而且如果我的概念圖不好的話,歡迎提出自己的看法寫在評(píng)論里。狀態(tài)機(jī)和行為樹的問題,屬于工程問題,不是科學(xué)問題,每個(gè)人會(huì)找到自己的理解,而且還有可能發(fā)現(xiàn)更厲害的抽象方法。

對(duì)于工程問題來說,就和寫代碼一樣,有很多要點(diǎn):

1、細(xì)節(jié)多且雜,函數(shù)返回值、Composite的使用這些細(xì)節(jié)的設(shè)計(jì)決定了成敗。

2、除非自己動(dòng)手試驗(yàn),發(fā)現(xiàn)問題、解決問題,否則不可能掌握。

所以,我會(huì)在之后再次深入討論Behavior Designer。


我自己開始寫這個(gè)例子的時(shí)候,光比較Unity不同的行為樹插件之間的區(qū)別(現(xiàn)在Asset Store里面的同類插件非常多),就花費(fèi)了兩天時(shí)間,最終根據(jù)資料數(shù)量、插件功能確定了Behavior Designer是工程的首選方案。然后翻官方文檔和前人總結(jié)的博客資料,才慢慢理解了行為樹的大致用法。

在探索過程中會(huì)走很多彎路,現(xiàn)在將我的經(jīng)驗(yàn)總結(jié)成此文,為初學(xué)者解決剛開始的那種迷茫的狀態(tài)會(huì)非常有用。

行為樹的實(shí)際使用博大精深,遠(yuǎn)遠(yuǎn)不是幾篇文章能覆蓋到的。畢竟AI是游戲開發(fā)中最龐大的系統(tǒng)之一,而行為樹又是AI的核心。希望讀者們能理解方法,體會(huì)樂趣,不要過早陷入技術(shù)細(xì)節(jié)之中。

咱們下一節(jié)還是繼續(xù)行為樹,下期再見。


工程地址:https://github.com/mayao11/PracticalGameAI/tree/master/AI_Enemy3_Behavior_basic



對(duì)游戲開發(fā)感興趣的同學(xué),歡迎圍觀我們:【皮皮關(guān)游戲開發(fā)教育】 ,會(huì)定期更新各種教程干貨,更有別具一格的線下小班教育。

我們的官網(wǎng)地址:http://levelpp.com/

我們的游戲開發(fā)技術(shù)交流群:610475807

我們的微信公眾號(hào):皮皮關(guān)


給貓看的游戲AI實(shí)戰(zhàn)(六)行為樹和Behavior Designer插件(上篇)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
五河县| 咸阳市| 资阳市| 井陉县| 彝良县| 土默特左旗| 公主岭市| 桓仁| 余干县| 拉孜县| 博客| 禹州市| 高安市| 辽中县| 吴桥县| 仲巴县| 浦北县| 南部县| 浦江县| 黔江区| 东宁县| 南阳市| 三明市| 防城港市| 岳阳市| 天柱县| 甘泉县| 漳平市| 木里| 长春市| 依安县| 永登县| 衡东县| 吉林市| 邻水| 右玉县| 会泽县| 雷山县| 揭阳市| 佛坪县| 赤峰市|