Unreal 게임 개발/Unreal Tool 활용
Unreal Behavior Tree 'Check Condition Only if Blackboard Changes'
daisy0461
2024. 9. 23. 23:18
Behavior Tree의 Decorator를 Blueprint로 만든 것에서 CPP로 만들다가 발생한 이슈에 대한 풀이이다.
이 영상이 의도한대로 구현된 것이다.
일정거리 이상 멀어지면 바로 따라오도록 만들고 싶었다.
Blueprint로 만든 것은 이렇게 생겼다.
이걸 그대로 구현하고 싶어서 cpp에는 이렇게 만들었다.
#include "Enemy/EnemyAI/Decorator/BTD_IsWithInIdealRange.h"
#include "AIController.h"
#include "BehaviorTree/BlackboardComponent.h"
UBTD_IsWithInIdealRange::UBTD_IsWithInIdealRange()
{
}
bool UBTD_IsWithInIdealRange::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
bool bResult = Super::CalculateRawConditionValue(OwnerComp, NodeMemory);
APawn* ControllingPawn = OwnerComp.GetAIOwner()->GetPawn();
if(!ControllingPawn) return true;
UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
if (!BlackboardComp) return true;
UObject* AttackTargetObject = BlackboardComp->GetValueAsObject(AttackTargetKey.SelectedKeyName);
AActor* AttackTargetActor = Cast<AActor>(AttackTargetObject);
float IdealRange = BlackboardComp->GetValueAsFloat(IdealRangeKey.SelectedKeyName);
AAIController* AIController = OwnerComp.GetAIOwner();
float AttackTargetDistance = AttackTargetActor->GetDistanceTo(ControllingPawn);
UE_LOG(LogTemp, Display, TEXT("BTD_IsWithInIdealRange : %s"), *AttackTargetActor->GetName());
if((AttackTargetDistance - ErrorMarin) <= IdealRange){
return false;
}
//return false;
return true;
}
FString UBTD_IsWithInIdealRange::GetStaticDescription() const
{
return FString::Printf(TEXT("\nAttackTargetKey: %s\nIdealRangeKey: %s"), *AttackTargetKey.SelectedKeyName.ToString(), *IdealRangeKey.SelectedKeyName.ToString());
}
근데 이상하게 흘러간다..
BehaviorTree가 이렇게 만들어져있는데 왼쪽 Sequnece를 다 돌고 온다.
당연히 Observer Obert는 Lower Priority이다.
이렇게까지 했는데 원하는 방식으로 진행되지 않아서 고민이었다.
이게 왜 이런 일이 발생하는지 계속 살펴보고 만져보다가
이런걸 발견했다.
이걸 Uncheck해서 Tick이 활성화되어 계속 검사했기 때문이다.
그래서 CPP를 이렇게 바꾸니까 해결됐다.
#include "Enemy/EnemyAI/Decorator/BTD_IsWithInIdealRange.h"
#include "AIController.h"
#include "BehaviorTree/BlackboardComponent.h"
UBTD_IsWithInIdealRange::UBTD_IsWithInIdealRange()
{
bNotifyTick = true;
bNotifyBecomeRelevant = true;
bNotifyCeaseRelevant = true;
LastConditionValue = false;
}
void UBTD_IsWithInIdealRange::OnBecomeRelevant(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
Super::OnBecomeRelevant(OwnerComp, NodeMemory);
bNotifyTick = true;
UE_LOG(LogTemp, Display, TEXT("OnBecomeRelevant Call"));
}
void UBTD_IsWithInIdealRange::OnCeaseRelevant(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
Super::OnCeaseRelevant(OwnerComp, NodeMemory);
bNotifyTick = false;
UE_LOG(LogTemp, Display, TEXT("OnCeaseRelevant Call"));
}
void UBTD_IsWithInIdealRange::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
bool bConditionValue = CalculateRawConditionValue(OwnerComp, NodeMemory);
if (bConditionValue != LastConditionValue) {
OwnerComp.RequestExecution(this);
}
LastConditionValue = bConditionValue;
}
bool UBTD_IsWithInIdealRange::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const
{
bool bResult = Super::CalculateRawConditionValue(OwnerComp, NodeMemory);
APawn* ControllingPawn = OwnerComp.GetAIOwner()->GetPawn();
if(!ControllingPawn) return true;
UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
if (!BlackboardComp) return true;
UObject* AttackTargetObject = BlackboardComp->GetValueAsObject(AttackTargetKey.SelectedKeyName);
AActor* AttackTargetActor = Cast<AActor>(AttackTargetObject);
float IdealRange = BlackboardComp->GetValueAsFloat(IdealRangeKey.SelectedKeyName);
AAIController* AIController = OwnerComp.GetAIOwner();
float AttackTargetDistance = AttackTargetActor->GetDistanceTo(ControllingPawn);
if((AttackTargetDistance - ErrorMarin) <= IdealRange){
return false;
}
return true;
}
FString UBTD_IsWithInIdealRange::GetStaticDescription() const
{
return FString::Printf(TEXT("\nAttackTargetKey: %s\nIdealRangeKey: %s"), *AttackTargetKey.SelectedKeyName.ToString(), *IdealRangeKey.SelectedKeyName.ToString());
}
위 처럼 Tick Node를 활용해서 풀어냈다.
여기서 갑자기 추가된
OnBecomeRelevant, OnCeaseRelevant는 다음 링크에 들어가면 설명이 되어있다.