1. 태스크 노드(BTTaskBase 상속)

(1) 코드

#include "BTTask_Shoot.h"
#include "AIController.h"
#include "ShooterCharacter.h"

UBTTask_Shoot::UBTTask_Shoot()
{
    NodeName = TEXT("Shoot");
}

EBTNodeResult::Type UBTTask_Shoot::ExecuteTask(UBehaviorTreeComponent &OwnerComp, uint8 *NodeMemory)
{
    Super::ExecuteTask(OwnerComp, NodeMemory);

    // AI의 폰을 호출하고 Shoot을 호출
    if (OwnerComp.GetAIOwner() == nullptr)
        return EBTNodeResult::Failed;

    AShooterCharacter* AIPawn = Cast<AShooterCharacter>(OwnerComp.GetAIOwner()->GetPawn());

    if (AIPawn == nullptr)
        return EBTNodeResult::Failed;

    AIPawn->Fire();

    return EBTNodeResult::Succeeded;
}

여기에서는 BTTaskBase를 상속받는 Shoot 태스크 노드를 생성한다.

이전에 ShooterCharacter 클래스 내에 총을 발사하는 Fire 함수를 생성했었는데, 해당 기능을 사용하기 위해서는 AI 폰을 가져오는 것이 필요하다.

폰은 반드시 컨트롤러가 필요하므로, 컨트롤러를 통해 폰을 꺼내올 수 있다.

 

 

(2) 비헤이비어 트리에 노드 추가

이제 시퀀스를 추가하여 Loop 데코레이터를 추가하고 1초마다 발사하도록 변경한다.

 

 

2. 서비스 노드

상태를 주기적으로 업데이트하고, 조건 충족 여부를 수시로 확인한다.

다른 시퀀스가 작동 중일 때에도 병렬적으로 작동한다.

 

(1) 플레이어 폰을 따라 회전하지 않는 버그

AI 폰이 플레이어 폰을 발견하고 이동한 이후, 포커스를 회전하지 않는 버그를 수정한다.

Default Focus를 추가하고 키를 PlayerLocation으로 변경한다.

 

 

(2) ShooterAIController 기능 옮기기

이제 서비스 노드가 도입되었으므로 ShooterAIController에서 플레이어 폰을 찾아 이동하는 코드를 서비스로 대체할 수 있게 되었다.

#include "BTService_PlayerLocationIfSeen.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/Pawn.h"
#include "ShooterAIController.h"

UBTService_PlayerLocationIfSeen::UBTService_PlayerLocationIfSeen()
{
    NodeName = TEXT("Update Player Location If Seen");
}

void UBTService_PlayerLocationIfSeen::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
    Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);

    APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
    
    if (PlayerPawn == nullptr)
        return;

    if (OwnerComp.GetAIOwner() == nullptr)
        return;

    if (OwnerComp.GetAIOwner()->LineOfSightTo(PlayerPawn))
    {
        OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), PlayerPawn->GetActorLocation());
    }
    else
    {
        OwnerComp.GetBlackboardComponent()->ClearValue(GetSelectedBlackboardKey());
    }
}

기존에 AIController에서 AI 폰의 시선에 따라 플레이어 폰의 위치를 업데이트하는 코드를 서비스로 옮겨온다.

AIController의 Tick 함수에서 해당하는 부분을 삭제한다.

이후에 해당 서비스를 셀렉터에 추가한다.