行為樹Decorator節點說明
- 用於決定節點能否執行/ 對節點的返回結果進行修改,Compositie節點和Task節點上都可以添加
- 右鍵節點 -> Add Decorator
內置Decorator
- Blackboard:判斷blackboard裡某Key的值的狀態
- Check Gameplay Tags On Actor:判斷blackboard裡某個Actor類型Key的Gameplay Tags
- Compare BBEntries:對blackboard裡的兩個Key進行比較
- Composite:組合Decorator藍圖,可以自定義添加多個Decorator及And/Or/Not組合成一個複合條件Decorator
- Conditional Loop:黑板的某個Key符合條件就一直循環
- Cone Check:從Cone Origin開始,往Cone Direction建立一個半角為Cone Half Angle的視錐,判斷Observed是否在視錐範圍裡
- Cooldown:進入節點後,會開始指定時間的計時,在計時結束前每次嘗試進入該節點,都會直接返回
- Does Path Exist: 判斷黑板中的兩個Actor Key之間是否有可尋路徑
- Path Query Type:
- Navmesh Raycast 2D 最快,使用NavMesh網格的數據進行尋路
- Hierarchical Query / Regular Pathfinding 都比較慢,使用UE的另一套的尋路算法進行尋路
- Path Query Type:
//BTDecorator_DoesPathExist.cpp
bool UBTDecorator_DoesPathExist::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
const UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
if (BlackboardComp == NULL)
{
return false;
}
FVector PointA = FVector::ZeroVector;
FVector PointB = FVector::ZeroVector;
const bool bHasPointA = BlackboardComp->GetLocationFromEntry(BlackboardKeyA.GetSelectedKeyID(), PointA);
const bool bHasPointB = BlackboardComp->GetLocationFromEntry(BlackboardKeyB.GetSelectedKeyID(), PointB);
bool bHasPath = false;
const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(OwnerComp.GetWorld());
if (NavSys && bHasPointA && bHasPointB)
{
const AAIController* AIOwner = OwnerComp.GetAIOwner();
const ANavigationData* NavData = AIOwner ? NavSys->GetNavDataForProps(AIOwner->GetNavAgentPropertiesRef(), AIOwner->GetNavAgentLocation()) : NULL;
if (NavData)
{
FSharedConstNavQueryFilter QueryFilter = UNavigationQueryFilter::GetQueryFilter(*NavData, AIOwner, FilterClass);
if (PathQueryType == EPathExistanceQueryType::NavmeshRaycast2D)
{
#if WITH_RECAST
const ARecastNavMesh* RecastNavMesh = Cast<const ARecastNavMesh>(NavData);
bHasPath = RecastNavMesh && RecastNavMesh->IsSegmentOnNavmesh(PointA, PointB, QueryFilter);
#endif
}
else
{
EPathFindingMode::Type TestMode = (PathQueryType == EPathExistanceQueryType::HierarchicalQuery) ? EPathFindingMode::Hierarchical : EPathFindingMode::Regular;
bHasPath = NavSys->TestPathSync(FPathFindingQuery(AIOwner, *NavData, PointA, PointB, QueryFilter), TestMode);
}
}
}
return bHasPath;
}
//NavigationSystem.cpp
bool UNavigationSystemV1::TestPathSync(FPathFindingQuery Query, EPathFindingMode::Type Mode, int32* NumVisitedNodes) const
{
SCOPE_CYCLE_COUNTER(STAT_Navigation_PathfindingSync);
CSV_SCOPED_TIMING_STAT(NavigationSystem, PathfindingSync);
if (Query.NavData.IsValid() == false)
{
Query.NavData = GetDefaultNavDataInstance();
}
bool bExists = false;
if (Query.NavData.IsValid())
{
if (Mode == EPathFindingMode::Hierarchical)
{
bExists = Query.NavData->TestHierarchicalPath(Query.NavAgentProperties, Query, NumVisitedNodes);
}
else
{
bExists = Query.NavData->TestPath(Query.NavAgentProperties, Query, NumVisitedNodes);
}
}
return bExists;
}
- Force Success: 節點返回值修改為true
- Is At Location:判斷當前位置是否在blackboard的某個Vector key的指定半徑範圍內
- Inverse Condition:判斷是否不在範圍內
- 可以用來做一些攻撃/交互可行性的判斷
- Is BBEntry Of Class:檢查blackboard裡的某個Object是否為指定類型
- Keep in Cone:判斷某被觀察對象是否在某觀察者對象的視錐範圍內,半角可指定
- Loop:循環次數,判斷某個節點的進入次數是否 < Num Loops
- Set Tag Cooldown / Tag Cooldown:給指定Gameplay Tag設置冷卻時間,具體判定邏輯與Cooldown一樣
- Time Limit:對節點的運行時間增加時間限制,節點運行超時後返回Failed,每次進入節點時計時器都會重置
自定義Decorator
- 行為樹上方 -> New Decorator
- Decorator藍圖提供了5式10種事件函數和1式2種的一般函數重載,最重要的就是這個一般函數 Perform Condition Check,其他都是一些時機事件。且帶AI後綴的才能拿到AI Controller,否則只能拿到Actor
- Receive Execution Finish(AI):依附的Task節點執行完成
- Receive Execution Start(AI):依附的Task節點開始執行
- Receive Observer Activated(AI) (Deactivated(AI))
- Observer指的abort observer的observer
- abort observer == self
- observer為當前子樹
- observer activated:當前執行保留在子樹時激活
- deactivated: 當前執行離開子樹時激活
- abort observer == Lower priority
- observer為低優先級子樹
- activated: 當前執行保留在低優先級子樹時激活
- deactivated: 當前執行離開在低優先級子樹時激活
- abort observer == both
- observer為當前子樹及低優先級子樹
- activated:當前執行保留在子樹時和在低優先級子樹時激活(待實驗)
- deactivated:當前執行離開子樹和低優先級子樹時激活(待實驗)
- Receive Tick(AI):當Observer被激活時,Receive Observer Activated被調用,Receive Tick事件才開始每幀被調用
- Perform Condition Check(AI):核心重載,用來寫Decorator的具體判斷邏輯
每Tick和BlackboardKey值改變時會被調用
//BTDecorator_BlueprintBase.cpp
void UBTDecorator_BlueprintBase::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
if (AIOwner != nullptr && (ReceiveTickImplementations & FBTNodeBPImplementationHelper::AISpecific))
{
ReceiveTickAI(AIOwner, AIOwner->GetPawn(), DeltaSeconds);
}
else if (ReceiveTickImplementations & FBTNodeBPImplementationHelper::Generic)
{
ReceiveTick(ActorOwner, DeltaSeconds);
}
// possible this got ticked due to the decorator being configured as an observer
if (GetNeedsTickForConditionChecking())
{
RequestAbort(OwnerComp, EvaluateAbortType(OwnerComp));
}
}
UBTDecorator_BlueprintBase::EAbortType UBTDecorator_BlueprintBase::EvaluateAbortType(UBehaviorTreeComponent& OwnerComp) const
{
if (PerformConditionCheckImplementations == 0)
{
return EAbortType::Unknown;
}
if (FlowAbortMode == EBTFlowAbortMode::None)
{
return EAbortType::NoAbort;
}
const bool bIsOnActiveBranch = OwnerComp.IsExecutingBranch(GetMyNode(), GetChildIndex());
EAbortType AbortType = EAbortType::NoAbort;
if (bIsOnActiveBranch)
{
if ((FlowAbortMode == EBTFlowAbortMode::Self || FlowAbortMode == EBTFlowAbortMode::Both) && CalculateRawConditionValue(OwnerComp, /*NodeMemory*/nullptr) == IsInversed())
{
AbortType = EAbortType::DeactivateBranch;
}
}
else
{
if ((FlowAbortMode == EBTFlowAbortMode::LowerPriority || FlowAbortMode == EBTFlowAbortMode::Both) && CalculateRawConditionValue(OwnerComp, /*NodeMemory*/nullptr) != IsInversed())
{
AbortType = EAbortType::ActivateBranch;
}
}
return AbortType;
}
bool UBTDecorator_BlueprintBase::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
bool CurrentCallResult = false;
if (PerformConditionCheckImplementations != 0)
{
// can't use const functions with blueprints
UBTDecorator_BlueprintBase* MyNode = (UBTDecorator_BlueprintBase*)this;
if (AIOwner != nullptr && (PerformConditionCheckImplementations & FBTNodeBPImplementationHelper::AISpecific))
{
CurrentCallResult = MyNode->PerformConditionCheckAI(MyNode->AIOwner, MyNode->AIOwner->GetPawn());
}
else if (PerformConditionCheckImplementations & FBTNodeBPImplementationHelper::Generic)
{
CurrentCallResult = MyNode->PerformConditionCheck(MyNode->ActorOwner);
}
}
return CurrentCallResult;
}
打斷設置
- 當Decorator觀察的條件變動時,可以指定打斷自己/同級行為樹的邏輯 => Observer aborts
- None
- 不打斷,等待本次執行結束
- Self
- 一旦狀態更改且不滿足Decorator條件,當前子樹就會立即中止
- LowerPriority
- 一旦狀態更改且滿足Decorator條件,低優先級子樹就會立即中止,並執行跳轉到Decorator所在節點
- Both
- 一旦狀態更改且不滿足Decorator條件,子樹就會立即中止
- 一旦狀態更改且滿足Decorator條件,低優先級子樹就會立即中止,並執行跳轉到Decorator所在節點
- None