Unreal 게임 개발/Unreal Tool 활용

Unreal BehaviorTree RequestExecution(), StopTree()

daisy0461 2024. 9. 24. 02:31

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자체를 중단시킨다. 전체 트리의 동작을 완전히 멈추고 더 이상 노드 실행이 되지 않는다.