게임개발/Unreal C++

[Unreal]#19_Custom Behavior Tree Service

Hardii2 2022. 10. 5. 23:55

 

[Unreal]#19_Custom Behavior Tree Service

Unreal 개발 중 "Behavior Tree Service"에 대해 알아보겠습니다.

"Simple Shooter Game"의 AI Player 개발 내용입니다.

 

 


 

Behavior Tree Service
  • BT_Service는 Background Task들을 실행하기 위한 노드로서, AI가 필요로 하는 정보들을 Update 합니다.

 

UBTService_UpdateLastKnownPlayerLocation

 

* UpdatePlaeyrLocation 네이밍을 UdpateLastKnownPlayerLocation이라 가정하겠습니다. 

1. UBTService_UpdateLastKnownPlayerLocation

  • Blackboard에서 생성한 Key 값과 관련된 서비스를 생성하기 위해 Blackboard Base의 클래스를 생성합니다.

 

2. UBTService_UpdateLastKnownPlayerLocation.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/Services/BTService_BlackboardBase.h"
#include "BTService_UpdatePlayerLocation.generated.h"

/**
 * 
 */
UCLASS()
class SIMPLESHOOTERNEW_API UBTService_UpdatePlayerLocation : public UBTService_BlackboardBase
{
	GENERATED_BODY()

public:
	UBTService_UpdatePlayerLocation();

protected:
	// From UBTService
	virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
};

 

3. UBTService.h

virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
  • 매 Frame마다 호출되는 메서드입니다.

 

4. UBTService_UpdateLastKnownPlayerLocation.cpp

// Fill out your copyright notice in the Description page of Project Settings.


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

UBTService_UpdatePlayerLocation::UBTService_UpdatePlayerLocation()
{
    NodeName = "Update Player Location";    
}

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

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

    OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), PlayerPawn->GetActorLocation());
}
  • Blackboard의 "Last Know Player Location"키의 값을 매 Frame마다 업데이트합니다.

 

UBTService_PlayerLocationIfFound

1. UBTService_PlayerLocationIfFound.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "BehaviorTree/Services/BTService_BlackboardBase.h"
#include "BTService_PlayerLocationIfFound.generated.h"

/**
 * 
 */
UCLASS()
class SIMPLESHOOTERNEW_API UBTService_PlayerLocationIfFound : public UBTService_BlackboardBase
{
	GENERATED_BODY()
	
public:
	UBTService_PlayerLocationIfFound();

protected:
	// From UBTService
	virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
};

 

2. UBTService_PlayerLocationIfFound

// Fill out your copyright notice in the Description page of Project Settings.


#include "BTService_PlayerLocationIfFound.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/Pawn.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "AIController.h"


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

void UBTService_PlayerLocationIfFound::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;


    // 1. AI Controller의 Line Of Signt(시야)에 Player가 감지되는지 검사합니다.
    if(OwnerComp.GetAIOwner()->LineOfSightTo(PlayerPawn))
    {
        // 2. 감지 되었다면, Player Location 키 값을 설정합니다.
        OwnerComp.GetBlackboardComponent()->SetValueAsVector(GetSelectedBlackboardKey(), PlayerPawn->GetActorLocation());
    }
    else
    {
        // 3. 감지 되지 않았다면, Player Location 키 값을 Clear합니다.
         OwnerComp.GetBlackboardComponent()->ClearValue(GetSelectedBlackboardKey());
    }
}

 

AShooterAIController

1. AShooterAIController.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "ShooterAIController.h"
#include "Kismet/GameplayStatics.h"
#include "BehaviorTree/BlackboardComponent.h"

void AShooterAIController::BeginPlay()
{
    Super::BeginPlay();

    PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
    if(AIBehaviorTree != nullptr)
    {
        RunBehaviorTree(AIBehaviorTree);

        // Bloackboard Component Setting Here
        GetBlackboardComponent()->SetValueAsVector(TEXT("Player Location"), PlayerPawn->GetActorLocation());
        GetBlackboardComponent()->SetValueAsVector(TEXT("Start Location"), GetPawn()->GetActorLocation());
    }
}

void AShooterAIController::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

/*
    if(PlayerPawn != nullptr)
    {
        if(LineOfSightTo(PlayerPawn))
        {
            GetBlackboardComponent()->SetValueAsVector(TEXT("Player Location"), PlayerPawn->GetActorLocation());
            GetBlackboardComponent()->SetValueAsVector(TEXT("Last Known Player Location"), PlayerPawn->GetActorLocation());
        }else
        {
            GetBlackboardComponent()->ClearValue(TEXT("Player Location"));
        }
    }
*/
}
  • Tick 메서드에 작성했던 "LineOfSightTo" 관련 코드들을 모두 지워줍니다.

 

Behavior Tree

1. Service 추가 화면

  • Set Default Focus(제공 서비스): Focal Point의 기준이 될 위치 혹은 객체를 설정합니다.
  • Update Player Location If Seen: Line Of Sight에 Player가 인지되었다면, Player Location 값을 설정합니다.
  • Update (Last Known) Player Location : Last Known Player Location 키 값을 설정합니다.

 

테스트 화면