Unreal Engine 4 系列教程 Part 9:AI教程

原文:Unreal Engine 4 Tutorial: Artificial Intelligence
做者:Tommy Tran
譯者:Shuchang Liuhtml

在本篇教程中,你將學習如何使用行爲樹和AI感知來建立一個能四處走動,攻擊敵人的簡單AI。web

在視頻遊戲中,人工智能(AI)一般指的是擁有自主決策行爲的非玩家角色。AI能夠是看到玩家而後進行攻擊的簡單角色,也能夠是即時策略(RTS)遊戲裏的強大對手。app

在Unreal引擎裏,咱們能夠經過行爲樹建立AI。行爲樹是一個決定AI作哪一種行爲的實時決策系統。好比,若是AI有戰鬥和逃跑兩種行爲。你能夠建立行爲樹,讓AI在高於50%血量時進行戰鬥,低於50%血量時逃跑。dom

在本篇教程中,你將學習到:編輯器

  • 建立AI實體用於控制角色單位
  • 建立並使用行爲樹和黑板
  • 使用AI感知讓角色單位得到視野
  • 建立行爲讓角色單位四處走動並攻擊敵人

注意:本篇教程只是Unreal Engine 4系列教程的其中一篇:ide

起步入門

下載示例項目並解壓。進入項目文件夾,雙擊MuffinWar.uproject打開項目。svg

按下Play運行遊戲,在圍欄內點擊左鍵生成蘑菇小人。學習

在本例中,咱們將建立一個能四處走動的AI,當其餘蘑菇小人進入AI的視野時,AI會追逐對方並進行攻擊。動畫

要建立一個AI角色,咱們須要三個元素:ui

  1. 身體:這個是角色的物理表現,在本例中,蘑菇小人就是身體
  2. 靈魂:這個是控制角色行爲的實體,既能是玩家自己,也能夠是AI
  3. 大腦: AI進行決策行爲的邏輯,咱們能夠用C++代碼,藍圖或者是行爲樹來實現邏輯。

如今咱們已經有了身體,接着要搞來靈魂和大腦。首先,咱們要建立控制器做爲靈魂。

什麼是控制器?

控制器是一個能控制角色單位的非物理Actor。這裏所說的「控制」,具體指的是什麼意思呢?

對於玩家而言,控制指的是能經過按鍵操控角色單位。控制器獲取玩家輸入,並將輸入直接傳給角色。固然,控制器也能夠獲取輸入進行處理,而後再告訴角色單位作哪一個行爲。

對於AI來講,角色單位就是由控制器或「大腦」(取決於實現方式)來通知其作什麼行爲的。

爲了用AI控制蘑菇小人,咱們須要建立一類特殊的控制器——AI控制器

建立AI控制器

打開Characters\Muffin\AI目錄並建立Blueprint Class,選中AIController做爲父類並命名爲AIC_Muffin

接着,咱們須要讓蘑菇小人使用這個AI控制器,打開Characters\Muffin\Blueprints並雙擊打開BP_Muffin

默認狀況下,Details面板會顯示藍圖的默認設置,若是沒有顯示,就點擊Toolbar的Class Defaults

在Details面板找到Pawn設置,將AI Controller Class設爲AIC_Muffin,這樣當蘑菇小人生成時,就會對應生成一個AI控制器實例。

因爲咱們要動態生成蘑菇小人,Auto Possess AI要設成Spawned。這樣當蘑菇小人生成時,AIC_Muffin就會自動控制BP_Muffin

點擊Compile並關閉BP_Muffin

如今,咱們要來建立決策蘑菇小人行爲的邏輯,就要用上行爲樹

建立行爲樹

打開Characters\Muffin\AI目錄,並選擇Add New\Artificial Intelligence\Behavior Tree,將其命名爲BT_Muffin並打開。

行爲樹編輯器

行爲樹編輯器包含3個新面板:

  1. Behavior Tree:這個圖表面板用於建立行爲樹節點
  2. Details:展現選中節點的參數
  3. Blackboard:展現黑板的全部鍵值(後續講解)和其對應數值。只有在遊戲運行時纔會有顯示

像藍圖同樣,行爲樹也是由節點構成的。行爲樹有4類節點,前兩種分別是任務(tasks)組合(composites)節點。

什麼是任務和組合節點?

顧名思義,任務節點負責完成具體任務,能夠是表現一套連招這樣的複雜任務,也能夠是原地等待這樣的簡單任務。

要完成多個任務,咱們就要用上組合節點。一個行爲樹由許多分支(行爲)組成。每一個分支的根節點,都是一個組合節點。不一樣類型的組合節點,執行其子節點的方式也各不相同。

好比,咱們有一組以下序列的行爲:

要按順序執行每一個行爲,咱們就要用上Sequence組合節點,由於Sequence節點可以從左至右的執行子節點,圖表看起來是這樣的:

注意:從組合節點衍生出來的節點能夠稱爲子樹(subtree)。一般來講,這些節點就統稱爲一個行爲。好比,SequenceMove To EnemyRotate Towards EnemyAttack就統稱爲「攻擊敵人」行爲。

若是Sequence的任意節點執行失敗,整個Sequence節點就會中止執行。

好比,若是角色沒法移動到敵人身邊,Move To Enemy節點就執行失敗了,這樣Rotate Towards EnemyAttack節點也就沒法繼續執行了。反之,若是角色成功移動到敵人邊上,就能執行隨後兩個節點。

後續咱們還會學習Selector組合節點,不過如今先讓咱們用Sequence節點實現角色隨機移動到某個位置並原地停留。

隨機移動位置

首先,建立Sequence節點並與Root節點相連。

接着,咱們須要讓角色移動起來,建立MoveTo節點與Sequence節點相連,這個節點能夠驅動角色移動到特定位置或Actor。

隨後,建立Wait節點與Sequence節點相連,確保將其放置在MoveTo節點右邊,放置順序很是重要,由於子節點是按照從左到右的順序執行的。

注意:你能夠經過每一個節點右上角的數字確認其執行順序。數字越小執行順序越高。

恭喜你,你剛剛建立了你的第一個行爲!它將會驅動角色移動到指定位置並原地停留數秒。

爲了讓角色移動,咱們還須要指定要移動的位置。因爲MoveTo節點只接受由黑板提供的數值,咱們要先建立一個黑板。

建立黑板

黑板是一個單純用來存放變量(鍵值)的資源。咱們能夠將其理解爲AI的內存。

雖然黑板不是必須使用的,但它確實爲咱們讀取,存取數據提供了極大便利,這麼說的緣由是不少行爲樹節點只接受黑板鍵值做爲參數輸入。

要建立一個黑板,咱們在Content Browser選擇新建Add New\Artificial Intelligence\Blackboard,將其命名爲BB_Muffin並打開。

黑板編輯器

黑板編輯器由2個面板組成:

  1. Blackboard:展現全部鍵值列表
  2. Blackboard Details:展現所選鍵值的參數

如今,咱們要建立一個鍵值用於存放目標位置。

建立目標位置鍵值

因爲是3D空間裏的一個位置點,咱們須要用Vector來進行存儲。點擊New Key並選擇Vector,將其命名爲TargetLocation

接着,咱們須要隨機生成一個位置並將其存在黑板裏,咱們就須要用到第三種類型的行爲樹節點:服務(service)節點。

什麼是服務節點?

服務節點相似於任務節點,用於完成一些事情。然而,不一樣於操控角色作特定行爲,服務節點用於執行檢查或更新黑板操做。

服務並非獨立節點,而是依附於任務節點或者組合節點。這樣使得行爲樹更加簡潔易於組織,不會橫生太多節點。若是咱們用任務節點來實現,效果以下圖所示:

若是用服務節點來實現,則以下圖所示:

如今,讓咱們來建立一個生成隨機位置的服務吧。

建立服務

回到BT_Muffin並點擊New Service

這樣就會新建一個服務並自動打開,咱們回到Content Browser將其重命名爲BTService_SetRandomLocation

服務應當且僅當在角色準備移動時才執行,所以咱們要將它附着在MoveTo節點上。

打開BT_Muffin右鍵點擊MoveTo節點,從彈出菜單選擇Add Service\BTService Set Random Location

如今,當MoveTo激活執行時,BTService_SetRandomLocation也會跟着激活執行。

接着,咱們須要隨機生成目標點位置。

生成隨機位置

打開BTService_SetRandomLocation

爲了監聽獲知服務什麼時候觸發執行,咱們建立Event Receive Activation AI節點,這個節點會在服務父類(所附着的節點)激活時觸發執行。

注意:另外一個事件Event Receive Activation也有着相同的觸發時機,二者區別在於Event Receive Activation AI事件額外提供了Controlled Pawn參數。

爲了生成隨機位置,添加以下高亮節點,確保將Radius設置爲500

這樣就能返回獲得該角色500單位半徑內的一個隨機可達目標點。

注意:GetRandomPointInNavigableRadius節點使用了導航數據(稱之爲NavMesh)來判斷一個點是否可達。在本例中,我已提早建立好了NavMesh。你能夠經過在Viewport選中Show\Navigation觀察NavMesh。


若是你想建立本身的NavMesh,請建立 Nav Mesh Bounds Volume,縮放其大小爲理想可達區域。

 

接下來,咱們須要將位置數據存儲到黑板裏。有兩種方式指定要存放的鍵值:

  1. 咱們可使用Make Literal Name節點指定鍵值名字
  2. 咱們能夠將變量暴露給行爲樹,這樣就能在行爲樹裏經過下拉列表選中變量

這裏咱們使用第二種方法。建立類型爲Blackboard Key Selector的變量。將其命名爲BlackboardKey並啓用Instance Editable,這樣行爲樹裏的服務就會出現對應變量。

隨後,建立以下高亮節點:

小結:

  1. Event Receive Activation AI節點會在其父類(本例中的MoveTo節點)激活時執行
  2. GetRandomPointInNavigableRadius節點返回角色500單位半徑內的一個隨機可達目標點
  3. Set Blackboard Value as Vector節點將一個黑板鍵值(BlackboardKey)數值設爲隨機位置點

點擊Compile並關閉BTService_SetRandomLocation

接着,咱們須要讓行爲樹來使用這個黑板值。

使用黑板

打開BT_Muffin並確保沒有選中任何東西。在Details面板的Behavior Tree設置處,將Blackboard Asset設爲BB_Muffin

而後MoveToBTService_SetRandomLocation就會自動使用黑板的第一個鍵值,在本例中,就是TargetLocation

最後,咱們須要讓AI控制器來運行行爲樹。

運行行爲樹

打開AIC_Muffin並鏈接Run Behavior Tree節點與Event BeginPlay節點,將BTAsset設爲BT_Muffin

這樣當AIC_Controller生成時就會執行BT_Muffin

點擊Compile並返回主編輯器,按下Play運行遊戲,生成一些蘑菇小人,觀察它們四處走動吧。

雖然設置很繁瑣,咱們仍是搞定了!接着,咱們要進一步設置AI控制器,讓它能夠在必定範圍內感知敵人所在。要實現這點,就要使用AI感知(AI Perception)

設置AI感知

AI感知是一個能夠添加給Actor的組件,經過它,咱們能夠給AI添加感官能力(如視覺和聽覺)

打開AIC_Muffin並添加AIPerception

接着,咱們要添加一個感官,因爲咱們想要蘑菇小人可以感知到其餘小人靠近,咱們給它加上視覺感官。

選中AIPerception並在Details面板的AI Perception設置處,給Senses Config添加新元素。

將元素0設置爲AI Sight config並展開它。

對於視覺有3個主要設置:

  1. Sight Radius:蘑菇小人的最遠視覺範圍,將其設置爲3000
  2. Lose Sight Radius:若是蘑菇小人已經看到了敵人,那敵人要逃離小人視野的距離,將其設置爲3500
  3. Peripheral Vision Half Angle Degrees:決定蘑菇小人視野的角度,將其設置爲45,蘑菇小人就會有90度的範圍視角。

默認狀況下,AI感知只檢測敵人(被指定爲不一樣隊伍(team)的Actor)。然而,Actor默認是沒有設置隊伍的,若是Actor沒有隊伍,AI感知就會將其認爲中立(neutral)角色。

截至目前,尚未方法能經過藍圖設置Actor的隊伍,退而求其次,咱們展開Detection by Affiliation設置,啓用Detect Neutrals

點擊Compile並回到主編輯器。按下Play運行遊戲來生成蘑菇。按下 ‘ 鍵能夠顯示AI調試信息,按下小鍵盤的數字鍵4能夠可視化AI感知組件。當蘑菇小人進入視野時,就會顯示綠色球體。

接着,咱們要讓蘑菇小人往敵人的方向走去。要實現這點,行爲樹就要了解敵人的信息,咱們經過在黑板存儲敵人的引用來完成這件事。

建立敵人鍵值

打開BB_Muffin並添加類型爲Object的鍵值,將其命名爲Enemy

如今,咱們還不能在MoveTo節點使用Enemy,由於其鍵值類型爲Object,但MoveTo只接受VectorActor類型的鍵值。

爲了解決這點,咱們選中Enemy並展開Key Type,將Base Class設置爲Actor。這樣行爲樹就能將Enemy識別爲Actor了。

關閉BB_Muffin,如今,咱們要建立一個行爲讓AI向敵人走去。

朝敵人移動

打開BT_Muffin並斷開SequenceRoot鏈接。咱們能夠經過按住Alt鍵點擊連線來作到,並將移動子樹移到一邊。

接着,建立以下高亮節點,並將Blackboard Key設置爲Enemy

這樣角色就會朝Enemy走去。有時候,角色不會恰好面對着它的目標,因此咱們還須要用上Rotate to face BB entry節點。

如今,咱們須要在AI感知檢測到其餘蘑菇時,將其設置爲Enemy的值。

設置敵人鍵值

打開AIC_Muffin並選中AIPerception組件,添加Perception Updated事件。

只要感官發生更新,這個事件就會觸發執行。在本例中,當AI得到或丟失了某物體的視野,這個事件就會執行,並提供了其當前所能感知到的Actor列表。

添加以下高亮節點,並確保將Make Literal Name節點設置爲Enemy

這樣就能夠判斷AI目前有沒有敵人對象,若是沒有,咱們就要給它設置一個敵人,所以添加以下節點:

小結:

  1. IsValid節點負責判斷Enemy鍵值是否有值
  2. 若是還沒設置,遍歷當前全部檢測到的Actor
  3. Cast To BP_Muffin節點負責檢查Actor是否爲蘑菇
  4. 若是是蘑菇,進一步判斷是否已死亡
  5. 若是IsDead返回false,將蘑菇設置爲新敵人,並退出循環

點擊Compile並關閉AIC_Muffin,按下Play運行遊戲並生成兩個蘑菇小人,其中一個生成暴露在另外一個面前,後者就會自動向前者走過去。

接着,你要建立一個自定義任務,讓蘑菇小人能夠表演攻擊行爲。

建立攻擊任務

咱們能夠直接在Content Browser建立任務,而無須經過行爲樹編輯器。建立新的Blueprint Class類,並將BTTask_BlueprintBase做爲其父類。

將新建類命名爲BTTask_Attack並打開,添加Event Receive Execute AI節點,這個節點會在行爲樹激活BTTask_Attack時觸發執行。

首先,你須要讓蘑菇執行攻擊行爲。BP_Muffin包含一個IsAttacking變量,當變量設置爲true時,蘑菇會執行一次攻擊,所以咱們添加以下高亮節點:

若是這個任務節點在這裏就結束了,那行爲樹執行就會卡在這個節點上,由於行爲樹並不知道該節點已執行完畢了,因此咱們要在節點鏈末端添加Finish Execute節點。

接着,啓用Success,因爲咱們用的是Sequence,這樣就能讓BTTask_Attack的後續節點得以執行。

如今圖表看起來應該是這樣的:

小結:

  1. 當行爲樹激活BTTask_Attack節點時,Event Receive Execute AI節點就會一同觸發執行。
  2. Cast To BP_Muffin節點會檢查Controlled Pawn是否爲BP_Muffin類型
  3. 若是是,則設置IsAttacking變量爲true
  4. 經過Finish Execute節點退出當前節點,讓行爲樹繼續往下執行

點擊Compile並關閉BTTask_Attack

如今,咱們須要將BTTask_Attack節點添加到行爲樹中。

行爲樹添加攻擊行爲

打開BT_Muffin,隨後,將BTTask_Attack節點添加到Sequence節點後面。

接着,將Wait節點添加到Sequence節點後面,並將Wait Time設置爲2。確保蘑菇小人不會攻擊個不停。

回到主編輯器點擊Play運行遊戲,像上次同樣生成兩個蘑菇小人。蘑菇小人會朝着敵人走去。隨後,它會嘗試攻擊,而後休息兩秒。當它發現另外一個敵人時,又會重複以上行爲。

在最後一部分,咱們要將攻擊和移動兩顆子樹合併在一塊兒。

合併子樹

爲了合併子樹,咱們要用上Selector組合節點。相似於Sequence節點,它也是按從左向右的順序執行的。然而,Selector節點會在子節點返回成功而非失敗時中止執行。利用這個特性,就能夠確保行爲樹每次只執行一顆子樹。

打開BT_Muffin並在Root節點下建立Selector節點。隨後,以下圖鏈接兩個子樹:

這樣同一時間只有一顆子樹會獲得執行,下面是每顆子樹的執行狀況:

  • 攻擊: Selector節點會首先運行第一顆子樹,若是全部任務都成功了,Sequence節點也會返回執行成功。Selector節點得知執行成功,就會中止執行後面的節點,這樣就不會再執行移動節點。

  • 移動: Selector節點會嘗試運行前面的攻擊子樹,若是Enemy尚未值,MoveTo節點就會執行失敗,Sequence節點也就一樣失敗。因爲第一個子樹失敗了,Selector節點就會執行後續這顆移動子樹。

回到主編輯器,按下Play運行遊戲,生成一些蘑菇小人試試看吧!

「等等,爲何圖中這個蘑菇小人沒有立刻攻擊另外一隻呢?」

在傳統的行爲樹設計裏,行爲樹每幀都會從根節點開始執行,意味着每幀更新,它都會嘗試執行第一顆攻擊子樹,而後再執行第二顆移動子樹,這也意味着當Enemy值發生變化時,行爲樹就會立刻切換執行另外一顆子樹。

然而,Unreal的行爲樹並非這樣設計執行的。在Unreal裏,行爲樹會繼續執行上一幀選中的那顆子樹。圖中因爲AI感知沒有立刻感知到另外一隻蘑菇小人的存在,行爲樹開始執行移動子樹,因而行爲樹就只能乖乖等待移動子樹執行完畢,才能從新評估肯定執行攻擊子樹。

爲了解決這個問題,咱們須要用上最後一種類型節點:裝飾(decorators)節點。

建立裝飾節點

相似於服務節點,裝飾節點也依附於任務或組合節點。一般而言,裝飾節點用於作前置檢查。若是檢查結果爲true,裝飾節點就返回true,反之亦然。經過裝飾節點,就能控制其依附節點是否可以執行。

裝飾節點也有能力停止子樹的運行,這意味着咱們能實現一旦Enemy有設值,就當即停止移動子樹。這樣蘑菇小人就能在發現敵人的第一時間攻擊敵人。

要實現停止功能,咱們可使用Blackboard裝飾節點,這個節點只是簡單地檢查某個黑板鍵值是否有值。打開BT_Muffin,並在攻擊子樹的Sequence節點點擊右鍵,從彈出菜單選中Add Decorator\Blackboard,這樣Sequence節點就會添加上Blackboard節點。

接着,選中Blackboard裝飾節點,並在Details面板將Blackboard Key設爲Enemy

這樣能夠判斷Enemy是否有值,若是沒有值,節點返回失敗,從而致使Sequence失敗,從而讓移動子樹獲得執行。

爲了停止移動子樹,咱們須要用上Observer Aborts設置。

使用Observer Aborts

Observer Aborts可以實現所選中的黑板鍵值發生變化時,停止執行子樹,這裏分爲兩種類型的停止:

  1. Self: 該設置容許當Enemy值失效時,當即停止運行攻擊子樹,這種狀況發生在攻擊子樹還未運行完畢,而Enemy又死亡的時候。
  2. Lower Priority:該設置容許當Enemy有值時,停止運行較低優先度的子樹。因爲移動子樹放在攻擊子樹後面,它就是較低優先度子樹。

咱們將Observer Aborts設爲Both,同時啓用兩種類型的停止。

如今,當AI已經沒有敵人目標時,能夠立刻從攻擊子樹切換運行移動子樹。一樣的,當AI檢測到敵人目標時,又能從移動子樹切換運行攻擊子樹。

如下是完整的行爲樹圖表:

攻擊子樹小結:

  1. Enemy有值,Selector開始運行攻擊子樹
  2. 一旦運行子樹,角色開始朝敵人走去
  3. 隨後,進行攻擊
  4. 最後,角色原地停留2秒

移動子樹小結:

  1. Enemy沒有值,攻擊子樹運行失敗時,Selector繼續運行移動子樹
  2. BTService_SetRandomLocation生成一個隨機位置
  3. 角色朝指定位置移動
  4. 隨後,角色原地停留5秒

關閉BT_Muffin並按下Play運行遊戲,生成一些蘑菇小人進行一場你死我活的決鬥吧!

後續學習

你能夠在這裏下載完整項目。

如你所見,製做簡單AI還算一件不難的事。若是你想建立一個更加高級的AI,請查閱場景查詢系統,這個系統容許AI收集場景數據並做出相應的反饋。

若是你還想繼續學習引擎其餘內容,點擊下篇教程,將教你如何製做一個簡單的第一人稱射擊遊戲。

相關文章
相關標籤/搜索