Unreal Avoidance系統(下)

在上篇 我們提到使用Unreal內建的Detour迴避有一些限制
這些限制主要是純BP專案無法透過C++修改行為造成的。

所以本篇是以C++專案為基礎,說明如果要使用Detour的功能,可能要做哪些修改才能達到。
需要注意的是這篇文章為了重現當初改寫時遇到的過程,會有大量的文字描述,沒興趣的話應該會啃不下去。
但是裡面有一些遇到問題的解法,對解題過程有興趣的人可以看看,或是有更好的建議也歡迎~

本篇會介紹下面幾個項目,可以先看看有沒有你要的內容再繼續~
1. 為什麼要複寫CrowdManager
2. 繼承並複寫CrowdManager
3. 在執行時期更改AvoidanceGroup

為什麼要複寫CrowdManager


這是個很冗長的流程,當初我發現RVO可以動態透過API修改Avoidance Group,
但是用CrowdPathFollowingComponent(以下簡稱CPFC)卻不行時,我也是嚇了一跳。

於是我跑去追CPFC的程式碼,發現要修改Detour迴避行為的函式全部都被定義為private(圖1),
然後標頭檔的最後標示了CPFC的friend class是 CrowdManager。
顯示即使繼承CPFC也沒辦法改寫迴避的行為,設定全部都必須要透過CrowdManager處理。

圖1. 設定Detour的函式都是private。


然後跑去CrowdManager挖程式碼,卻發現CPFC那些設定相關的函式都沒被用到...
代表要繼承CrowdManager才能達到我希望能修改迴避參數的目標。

繼承並複寫CrowdManager


首先當然就是先繼承CrowdManager:
在Unreal Editor->New C++ Class->Show all classes
輸入CrowdManager,並選擇CrowdManager,輸入名字與路徑就可以完成繼承。

在Edit->Project Settings->Engine->NavigationSystem內有CrowdManagerClass
將此欄位指向剛剛創好的MyCrowdManager。可參考圖2
圖2. 在Engine設定CrowdManagerClass為自己複寫的class。

然後你就可以使用自定義的CrowdManager了...........................
.
.
.
.
.
.
.
.
.
.
如果是這樣我幹嘛寫這篇!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

由於這部份有點複雜,我也是追了一陣子的程式碼才找到原因(其實是有點久我忘記細節了)
詳情可以用中斷點追一下NavigationSystem.cpp裡面的
constructor,InitializeForWorld,
PostInitProperties,CreateCrowdManager這幾個函式裡面有關CrowdManagerClass的部份。

總之那個CrowdManagerClass雖然開成可編輯的項目,實際上根本沒作用,程式執行過程中不會吃你設定的值。

所以回過頭來,該如何讓我們的CrowdManager生效呢?
我找到這篇討論有說明在不修改引擎程式碼的狀況下,如何使自定義的CrowdManager起作用
https://answers.unrealengine.com/questions/100941/ucrowdfollowingcomponent-51st-bot-does-not-move.html?childToView=102163#answer-102163
縮短網址:
https://goo.gl/HR12Aj

這邊就直接step by step說明:
1. 繼承NavigationSystem
2. 複寫NavigationSystem::CreateCrowdManager()
直接在函式內生成你的CrowdManager並傳進SetCrowdManager:
SetCrowdManager(NewObject<UCrowdManagerBase>(this, UMyCrowdManager::StaticClass()));
3. 到Engine/Config/DefaultEngine.ini加下面兩行
[/Script/Engine.Engine]
NavigationSystemClassName=/Script/[YourProjectName].[NavigationClassName]
4.關掉Editor重開,就可以試試看你的CrowdManager是不是運作了~

4.20之後,在地圖的WorldSettings裡面多了NavigationSystemConfig,必須要將自定義的NavigationClass填入NavigationSystemClass才有作用。

在執行時期更改AvoidanceGroup


複寫的CrowdManager之後,所有要對CPFC設定的行為就都可以達到了,大致上就是寫類似的函式:
void UMyCrowdManager::SetAvoidanceGroup(UCrowdFollowingComponent* AgentComponent,
int32 GroupFlags, bool bUpdateAgent = true)
{
AgentComponent->SetAvoidanceGroup(GroupFlags, bUpdateAgent);
}
void UMyCrowdManager::SetCrowdSlowdownAtGoal(UCrowdFollowingComponent* AgentComponent, bool bEnable, bool bUpdateAgent)
{
    AgentComponent->SetCrowdSlowdownAtGoal(bEnable, bUpdateAgent);
}

有時候為了方便能夠能在blueprint作處理會比較好,而CrowdManager好像在bp是拿不到的,
反正都C++了就直接作一個吧~

定義
UFUNCTION(BlueprintCallable, Category = "MyCrowdManager")
static UCrowdManagerBase* GetMyCrowdManager(UObject* WorldContextObject);
實作
UCrowdManagerBase* UMyCrowdManager::GetMyCrowdManager(UObject* WorldContextObject)
{
    UNavigationSystem* navSystem = WorldContextObject->GetWorld()->GetNavigationSystem();
    return navSystem->GetCrowdManager();
}

後續其實還是有東西可以寫,大致上是Detour有提供視覺化的Debug功能,可以介紹如何開啟以及觀察;如何設定/調整自定義的迴避樣板;如何迴避玩家。不過這次就先寫到這裡就好~

留言

這個網誌中的熱門文章

UE4 除錯技巧分享 (一)

UE4 GameplayAbilitySystem - GameplayEffect & GameplayCue 如何設定參數

UE4 GameplayAbilityTask介紹