前言
之前實作了自主創作的角色匯入進UE4并成功控制其進行一系列動作,但目前的樣子距離基本的游戲架構還差了一個很大的模塊:NPC,而這部分是由電腦來進行自動控制,所以,我有一句話不知當講不當講(對,我又不滿足了( ?? ω ?? )?),由此,我又一次打開了官方檔案,開始對UE4中比較難啃的AI模塊進行探索,(前方少圖,請放心加載(笑))
正文
一.構成
先說一下UE4中AI的構成,一般如果是對玩家有威脅的敵人角色或者是跟隨玩家的npc角色,它們的配置一般有:1.行為樹;2.黑板;3.AI控制器;4.AI角色;5.相關服務;6.相關修飾器;7.相關任務,本次以類似于《合金裝備》里敵兵的AI構建為例,對各個配置進行說明與個人見解,
1.行為樹
UE4的AI角色核心,屬于決策層配置,大致樣貌如下:

如果你有一些程式設計基礎,那么大致通過名字就能判斷行為樹是由類似于if—else一類的決策組成的AI行動方案,并且行為樹是一種可以進行類似深度優先遍歷操作的資料結構,即比如在某個子樹的行為已經運行完成后,狀態改變,則此時根據改變的條件選擇需要遍歷的子樹接著進行遍歷與運行操作,而組成行為樹的節點可并不是類似于普通樹狀結構中單一僅含有權重的“小圓圈”,行為樹一般由以下幾種節點組成:Root,Composites和Tasks,接下來分別描述一下三種節點:
a.Root節點
又名根節點,是整個行為樹的核心與基礎,一但當Agent在場景中的實體化被呼叫時,那么,Root就是行為樹的呼叫入口,從此處開始AI行為邏輯,由于其固定的性質,所以Root節點不可進行編程,
b.Composites節點
直譯過來是“復合體”,根據其節點的作用,又分為Sequence,Selector和Simple Parallel(此節點暫不做敘述),其中,Sequence(佇列)是為了讓其下的子節點或子樹按照順序標號的順序進行執行的節點,而Selector則是行為樹中最重要的“選擇單元”,通過某些“特殊”的“玩意兒”實作對于狀態(一般是用整型,浮點,或列舉表示狀態)的判斷,從而選擇相關的子樹,與Root同樣含有固定的性質,所以不可編程,
c.Tasks節點
又名任務節點,即上述的“相關任務”,是相同的東西,這里具體記載著Agent的行為,當遍歷執行到這里時,Agent便會做出行動,比如跑,跳等,由于AI的行為依據具體情況決定,所以在引擎默認創建的幾個Tasks節點之外,開發者還可以撰寫新的Tasks節點,即Tasks節點是可編程的(行為樹插圖中紫色的部分),
2.黑板
黑板是用來存盤一系列Agent狀態變數的一種結構體,比如此時Agent發現了玩家并且向玩家跑過來,所以它此時的狀態至少就應該有這么幾個:玩家此時的位置,Agent的移動型別(Idle或者Running)以及玩家最后一次出現的位置,這些東西都要用變數存盤起來,然而各個任務節點與一系列的服務,修飾器之間是毫無聯系且相互獨立的,所以他們之間并不能直接修改對方的資料,這時候就要通過黑板里面的資料作為介面實作整個系統之間的通信,并且實際上這些資料并沒有保存在黑板里面,黑板只是提供了一種定義方式,行為樹會自行創建一個黑板,將所有資料存盤在那里面,只是行為樹創建的黑板會遵從原黑板的資料格式罷了,真不知道Epic官方是怎么起名的,叫“記憶體”或“資料塢”這種高大上狂拽酷炫屌炸天的名字它不香么?(或許Epic有這種想法的合理性吧,咱對人工智能的基礎理論知之甚少,如果有哪位大佬知道,歡迎補充)
3.AI控制器與AI角色
這便是AI在游戲場景中的實體化,也是最終展現給玩家的樣貌,由控制器控制角色進行相應的動作,由行為樹作為多觸手章魚玩家來操作多個控制器從而控制多個角色,
4.服務
開發人員以及玩家們都希望在做出一系列的舉止后(比如制造噪聲,向敵人扔個小黃書等),Agent可以根據這種舉止做出相應行動,即遵守圖靈機的基本定義“輸入一定的資料——處理資料——輸出相應的處理結果”,試想,如果不接受玩家的輸入從而在場景內亂跑,那豈不就與喵星人的掃地座駕以及辣個女人一樣么?

所以,服務就是來干這事的,通常,服務一般用來接收此時場景內的情況輸入,比如玩家進入視錐,并且玩家并沒有躲在墻體后面,這時服務就會獲取這些資訊的輸入,并且通過邏輯處理從而將黑板中的狀態資料進行改變并更新,當然,由于一個Agent的行為在不同的游戲型別中(甚至不同關卡中)會接受多種不同的輸入,并且黑板上的資料也并不是一定相同的,所以,服務完完全全是可編程的(行為樹插圖中青色的部分),
5.修飾器
好的,這時候我們已經獲取了環境的資訊并改變了Agent的狀態,如果我們希望Agent根據此時的狀態做出相應的行為,那么就需要Selector進行選擇,然而,如何讓Selector進行選擇,總不能隨機選一個?如果是這樣,那么Selector以及Agent的狀態便毫無意義所以,這便是“那個玩意兒”——修飾器的作用:作為執行其負責的子樹部分的判定條件,也就是if—else結構中括號里填寫的那個條件,即定義的條件滿足后再運行相應子樹,若不滿足則退回上一節點,一般都使用基于參考黑板資料的修飾器,當然,判定條件也有可能與關卡狀態,玩家狀態甚至是你的電腦狀態等黑板中并沒有存盤的資料相關,所以,修飾器也是可編程的,(行為樹插圖中藍色的部分)
二.聯系
接下來以我最喜歡的PS2游戲之一《合金裝備2:自由之子》(我絕對不是因為可以在廁所里看海報而喜歡的)其中一個場景進行說明(請結合行為樹插圖食用更佳),具體場景如下所示:

此時玩家操縱的蛇叔處在A點,他要前往的B點(rush B)附近有敵兵看守,此敵兵假如說就是我們描寫的Agent,并且在相應的黑板中創建了這樣幾個變數State(Agent的狀態,列舉型別:Idle,Combat,Searching),Player(準確的說是Agent前往的地點),PlayerMarker(玩家在Agent可視范圍消失后最后一次出現的位置);這時,Agent在場景中實體化,所以,引擎會呼叫Agent所使用的行為樹的Root節點,此時行為樹開始作業,首先這時Agent面向墻壁,而在行為樹中的EnemyTrial_SearchVision服務為Agent正方向創造了一個110度(角度)的視錐,此時玩家并沒有在視錐范圍內,所以EnemyTrial_SearchVision服務將此時Agent的State設定為Idle,目標前往地點與玩家最后一次的位置設定為空,接下來進行選擇,Selector會通過自己所有分支中的修飾器進行選擇,既然此時狀態為Idle,那么State equal Idle修飾器符合條件,所以接下來執行此修飾器下的子樹,然后此時Agent的行為由EnemyTrial_SetWalkSpeed(設定此時Agent的行走速度:巡邏速度)以及EnemyTrial_MoveToRandomPoint(讓Agent隨機移動到某點)定義并控制Agent執行,在執行的程序中,服務以及修飾器也沒閑著,一旦此時Agent隨機移動的點在A點附近,Agent需要轉身,而此時玩家操作的蛇叔正好探頭與Agent對上了眼(進入視錐),那么,服務會立即改變此時Agent的State為Combat,而且將Player與PlayerMarker設定為此時玩家的位置,修飾器監視到State改變,所以會立即中斷自身的行為,并將進度立即返還到Selector,然后由Selector分配給State equal Combat,接著執行Combat對應的Agent行為,但是,如果此時蛇叔打開了光學迷彩(這是不可能的,因為從橋上跳下來時已經摔壞了),在視錐中消失,這時服務將Player設定為空,并會檢索黑板中的PlayerMarker,如果PlayerMarker非空,則Agent的Selector將進度交給State equal Searching,總不能Agent看不到敵人就放棄搜索吧(山貓:你已經被開除了),如果PlayerMarker為空,那么Agent的Selector會將進度交給State equal Idle(一般這種情況不可能,除非玩家開了修改器),接著,當Agent到達PlayerMarker后,在其搜索的時間內如果沒有在視錐內發現玩家,那么EnemyTrial_DestroyMarker任務便會清空Player與PlayerMarker,并且將State設定為Idle,這時修飾器監測到了State的改變,所以此時又會將進度返還給Selector,再由Selector將進度交給State equal Idle,實行Agent基礎的巡邏狀態,
結語
又開了一個坑……我也想好好做個游戲啊%&*#@!¥%%#(因言語過激而被踢出直播間),但是基礎不足,身邊也沒有學習美工與音效的小伙伴,只能自己慢慢摸索了,C++也在補STL方面的基礎,不過這倒是充足了網課后的課余時間(笑),如果我的這篇文章里面的相關名詞與解釋有不正確或是容易引發歧義的地方,歡迎在評論區指出,而且如果你對于其中的一些解釋還有不明白的地方,也歡迎騷擾,當然,老規矩,如果你認為這篇文章會對更多的人有幫助,歡迎轉載,只不過請注明轉載出處即可,以上,どうもありがとうございました!(都莫,阿里嘎多靠薩依瑪希大!)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/2218.html
標籤:其他
