Unreal BehaviorTree RequestExecution(), StopTree()
Player를 쫓아오는 AI를 만들 때 일정 거리에 도달하면 근처에서 대치하는 상황을 만들고 싶었다.
이때 이상한 현상이 발생했다.
코드는 다음과 같다.
void UBTD_IsWithInIdealRange::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
CalculateRawConditionValue(OwnerComp, NodeMemory);
if(!TickFlag){
UE_LOG(LogTemp, Display, TEXT("Tick Node!!"));
TickFlag = true;
}
}
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){
UE_LOG(LogTemp, Display, TEXT("return False"));
return false;
}
//return false;
UE_LOG(LogTemp, Display, TEXT("return true"));
return true;
}
그리고 현상과 관련된 영상이다.
Log를 살펴보면 Tick에 의해서 CalculateRawConditionValue는 정상적으로 실행이 되며 거리에 따라서 false와 true가 정확하게 Log에 남는다.
당연히 Obsersver aborts도 Lower Priority이다.
C++로 변환 이전에 Blueprint로 Decorator를 만들었고 동일하게 만들었지만 다르게 작동해서 자료를 찾아봤다.
Blueprint와 C++의 동작이 달랐던 이유는 링크에 적혀있다.
https://daisy0461.tistory.com/138
간단히 말하면 Check Condition Only if Blackboard Change라는 옵션이 있기 때문에 Blueprint는 자동으로 변경이 되었던 것이다.
해당 이유 때문에 처음에는 위 영상처럼 Tick에서 CalculateRawConditionValue를 실행시켰다.
그 결과는 위 영상처럼 제대로 되지 않아서 다시 자료를 찾다가 RequestExecution()을 발견했다.
RequestExecution()
이 함수의 역할은 Tree의 재평가를 요청하는 것이다.
즉, 조건이 변경되었을 때 트리의 나머지 부분이 다시 실행될 수 있도록 한다는 것이다.
내가 정확하게 원하는 것을 찾았다!
그래서 코드는 다음과 같이 변경시켰다.
void UBTD_IsWithInIdealRange::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
bool bConditionValue = CalculateRawConditionValue(OwnerComp, NodeMemory);
if (bConditionValue != LastConditionValue) {
OwnerComp.RequestExecution(this);
}
LastConditionValue = bConditionValue;
}
이렇게 변환하니 재평가 되어 바로 나를 쫓아온다.
그리고 관련해서 자료를 찾아보니
StopTree()도 많이 보여서 두 가지를 비교하자면
RequestExecution(): 트리의 재평가를 요청. 상황 변화에 따라 동작을 즉각적으로 갱신할 수 있도록 한다.
StopTree(): BehaviorTree자체를 중단시킨다. 전체 트리의 동작을 완전히 멈추고 더 이상 노드 실행이 되지 않는다.