15、追逐玩家
現在我們的AI無法做出任何決策,它總是執行相同的決策。我們先把感知系統中的相關信息提供給AI,讓AI知道如何做出決策,然后我們會修改行為樹。我們首先需要創建新的黑板鍵,這樣我們就能在行為樹上保存這些信息。打開”BB_EnenmyAI “,在左側的“黑板”選項卡中,點擊“新建”按鈕,選擇“對象”類型,將這個新建命名為“Target Actor”。在右側的“黑板詳細信息”面板中,在“鍵”分段中展開“鍵類型”選項,在“基類”下拉菜單中將類型從“object”改為“Actor”。回到“黑板”面板,再次點擊“新建”,這次選擇布爾類型,把這個黑板鍵命名為“Has Line Of Sight”。
接下來,首先在內容瀏覽器中找到“AIC ThirPerson Character”然后打開它,接着創建一些輔助函數以便讓黑板鍵處理起來更加容易。第一個函數有一個Actor輸入,並會把某個黑板鍵設置為相同的Actor值,在“我的藍圖”面板中點擊“函數”分段中的加號圖標,新建一個函數,將函數命名為“Update Target Actor Key”。在細節面板中,點擊“輸入”分段中的加號,新建一個輸入,選擇“Actor”作為輸入類型,將此輸入命名為“Target Actor”。第一步是獲取當前的黑板。右鍵事件圖表,搜索“Get Blackboard”,選擇“AI”>“變量”中的“Get Blackboard”。拖動“黑板”組件對象引用的引腳然后輸入“Set value as Object”,這樣會自動把黑板對象作為“Set value as Object”的目標參數。接下來,我們需要創建一個鍵名,它指向與行為樹相關聯的黑板中的黑板鍵。為此,我們拖動“Set value as Object”的“key name”,選擇提升至變量,將這個新變量命名為“Target Actor Name”。保持選中狀態,移到細節面板,找到類別面板,新建一個類別,在這個選項框中輸入“Key Names”並以此命名。注意,這樣會把我們的新變量自動歸類到“我的藍圖”面板中的“Keys Names”類型。下一步是設置“Target Actor Name”的默認值,在此之前,我們需要編譯藍圖。
編譯后細節面板中“Target Actor Name”的默認值為無,將其設置為與黑板鍵名相同,即“Target Actor”。“Set value as Object”需要的最后一個參數就是傳遞給黑板的數值。這里就要用到我們此前創建的輸入。將“Target Actor”連到“set values as object”的“object value”,把這個函數的執行引腳連到“Set value as Object”。
我們要創建的下一個函數擁有一個黑板鍵,它能保存AI當前能否通過感知系統觀察到玩家。和之前一樣,先在“我的藍圖”面板中新建一個函數,將它命名為“UpdateHasLineOfSightKey”,在細節面板的“輸入”分段中,點擊加號添加新的輸入,確保這個輸入是布爾類型,將這個新參數命名為“HasLineOfSight”。和之前的函數一樣,我們首先要獲得正確的黑板,右鍵點擊事件圖表,然后輸入“Get Blackboard”,選擇“AI”>”變量”中的函數,拖動這個黑板引用的引腳,然后輸入“Set value as Bool”,拖動“Key name”變量,選擇“提升為變量”,將其命名為“HasLineOfSightName”。 下一步是設置“HasLineOfSightName”的默認值,在此之前,我們需要編譯藍圖。編譯后細節面板中“HasLineOfSightName”的默認值為無,將其設置為與黑板鍵名相同,即Has Line Of Sight”。然后將兩個函數引腳相連,將“Has Line Of Sight”輸入引腳與“bool value”相連。
現在我們要回到角色的“on target perception updated”事件,然后使用這個時間來更新我們新建的黑板鍵。這意味着每當感知狀態發生改變時,相關的黑板鍵也會更新。
在內容瀏覽器中找到“AICharacter1”,首先在觸發“target lost”事件時,清空“target actor”黑板鍵。當前,這個事件會在目標超出視野5秒后觸發,這表示AI完全遺忘了目標Actor,並放棄進行跟蹤。為了訪問我們剛剛創建的函數,我們首先要獲取AI控制器的引用。右鍵點擊事件圖表,然后輸入“Get Controller”,直到出現這個函數,這個函數會返回一個泛型控制器引用。我們需要將這個泛型類型轉換為“AIC ThirdPerson Character”。為此,我們可以拖動返回值,輸入“Cast”,選擇“Cast to AIC ThirdPerson Character”。為了保證圖表條理清晰便於理解,我們可以轉換成純類型轉換,右鍵點擊節點選擇“轉換為純類型轉換”。現在拖動“AIC ThirdPerson Character”引腳然后引用我們定義的函數即“Update Target Actor Key”,然后連接設置節點的執行引腳,如圖所示:
此時,期望的黑板狀態是讓“target actor”的值為空,也就是現在沒有target actor。為此,這個函數不需要提供任何參數。
讓我們來看這個事件的另一種結果。我們需要使用“”line of sight”鍵來表示這種情況,我們利用函數把“HasLineOfSight”鍵設置為假,我們可以復制這兩個節點“Get Controller”和“Cast to AIC ThirdPerson Character”,他們允許我們獲得“AIC ThirdPerson Character”AI控制器。再次拖動“AIC ThirdPerson Character”引腳然后引用我們定義的函數即“update has line if sight key”, 然后連接設置節點的執行引腳,如圖所示:
復制這三個節點“Get Controller”和“Cast to AIC ThirdPerson Character”和“Update Target Actor Key”,將他們的執行節點連接到“clear and invalidate timer by handle“。這個句柄當前連到分支的”真“值引腳,這種情況下我們需要提供”target actor“。所幸我們之前已經設置好了,找到”我的藍圖“面板,找到”PerceivedActor“,按住Ctrl鍵,左鍵點擊它並拖到事件圖表中,將”PerceivedActor“連到”target Actor“。”PerceivedActor“是之前在事件中定義的變量,我們獲取了事件和感知系統提供的Actor,然后將”PerceivedActor“賦值給它,最后,復制上面這三個節點 “Get Controller”和“Cast to AIC ThirdPerson Character”和“update has line if sight key”,它們用於獲取“update has line of sight key“函數,將他們連接到“Update Target Actor Key”。注意,分支的這個部分用於定義Actor剛剛被感知系統被感知到時的狀態。這里要把“has line of sight“設置為真,勾選函數中的復選框。
讓我們快速回顧一下做了那些修改。可以看到,這個“on target perception updated“事件為AI定義了三種不同狀態。第一種狀態,Actor被感知時,這個狀態定義了AI看到Actor時的情況。第二種結果,返回值為”假“的結果,定義了看不到Actor時的狀態。即使看不到,我們仍在黑板中保留了被感知Actor的值,這種狀態表示AI記得看到了某個對象,只是當前看不到了。最后是”target lost“事件,它會在對象消失5秒后觸發,這種狀態表示AI當前看不到,當前感知的Actor並且對它喪失了興趣。值得一提的是,我們之所以設置黑板鍵,就是為了提供信息,並通過黑板鍵將信息提供給行為,它不做任何決策,這由行為樹決定。
下面我們在行為樹中把所有這些修改內容拼合起來。我們將借助新的黑板鍵和裝飾器,讓AI有能力決定是追逐玩家還是繼續隨機漫游。首先,在內容瀏覽器中打開行為樹資源“BT_EnemyAI“,我們將使用當前保存在黑板中的信息,在兩個序列中二選一。刪除第一個序列,拖動根節點,在菜單中選擇”選擇器“,把選擇器連至現在的”隨機漫游“序列。隨機漫游序列的優先級比我們創建的序列要低。在行為樹中執行順序是從左到右。通過剛剛創建的選擇器新建一個序列,在細節面板中的”描述“分段中,將節點名稱改為”chase player“,拖動”chase player“序列,選擇”move to task“,我們需要將AI移到目標Actor的位置,為此,在右側細節面板中的”黑板“分段點擊”黑板鍵“,在下拉菜單中選擇”Target Actor“。”Target Actor“是我們創建的用於和函數一起更新的黑板鍵。接着,再次拖動追逐玩家序列,在”move to“節點的右側新建一個節點,選擇”Wait“節點,在細節面板的”等待“分段中,將等待時間五秒改為一秒。通常在任務結束后會添加一些等待任務,因為這能避免行為樹在不同序列間快速循環,從而顯得不自然。添加這兩個序列后,”追逐玩家“序列基本完成了,考慮到行為樹的執行順序,這個新的分支會始終在隨機漫游序列之前執行。
接下來,需要使用裝飾器控制這個分支的執行方式,只有當前需要設置目標Actor時才執行它。右鍵點擊,”追逐玩家“序列,選擇”添加裝飾器“>”黑板“。接下來,選中這個新的裝飾器,在細節面板中的描述段將節點名稱改為”is a target actor set“。我們來看看要使用哪些黑板鍵,由於我們關心的是目標Actor,所以應該在細節面板中”黑板“分段中,在”黑板鍵“下拉菜單中選中”SetActor“。接下來我們需要思考的是和鍵有關的是什么內容。”黑板“分段允許我們定義我們期望的結果。上面的”流程控制“分段允許我們控制觀察到期望結果時執行流程會發生什么。”通知觀察者“下拉菜單有兩個選項,默認選項是”on result change“。每當鍵查詢的結果發生變化時,我們會按照”observer aborts“描述的內容更改執行流程。