본문 바로가기
포트폴리오 제작/Project_P

Project_P 행동트리 모델 NPC 정찰 구현

by k99812 2024. 12. 6.

NPC 정찰 구현


 

NPC 정찰을 위한 행동트리 No Target 데코레이터는 추후 작성 예정

Sequence 컴포짓으로 대기, 정찰위치 지정, Move To 태스크 순서로 실행

 

Nav Mesh 생성 및 디버깅 방법


정찰 위치를 지정하기 위해 Nav Mesh가 필요

 

Place Actors → Volumes 에서 Nav Mesh Bounds Volume을 생성 후 NPC가 움직일 땅을 Nav Mesh 로 (영역안으로)지정

 

키보드 'P'를 누르면 Nev Mesh에 지정된 영역을 확인할 수 있음

 

 

플레이 버튼을 눌러 실행한 뒤 키보드 '를 누르면 AI 컨트롤러를 가지고 있는 액터를 디버깅할 수 있음

추후 구현할 Perception 및 행동트리, 네브 매쉬등 어떻게 동작하고 있는지 확인할 수 있음

 

BTTask_FindPatrolPos 생성


 

BTTaskNode를 상속받아 BTTask_FindPatrolPos을 생성

BTTaskNode_BlackboardBase는 BTTaskNode를 상속받아 BlackboardKey관련 함수 및 변수들이 추가 구현되어있음

 

BTTask_FindPatrolPos 헤더파일

#include "CoreMinimal.h"
#include "BehaviorTree/BTTaskNode.h"
#include "BTTask_FindPatrolPos.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT_P_API UBTTask_FindPatrolPos : public UBTTaskNode
{
	GENERATED_BODY()
	
public:
	UBTTask_FindPatrolPos();

	virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
};

 

  • UBTTask_FindPatrolPos();
    • 생성자
  • virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
    • TaskNode가 실행될때 호출되는 ExecuteTask를 상속받음

 

Cpp 파일

생성자

UBTTask_FindPatrolPos::UBTTask_FindPatrolPos()
{
	NodeName = TEXT("FInd Patrol Position");
}

 

생성자에서 행동트리에서 표시되는 노드이름을 지정

 

ExecuteTask

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

	//#include "AIController.h" 추가
	//BTTask의 OwnerComp(BT)에서 GetAIOwner로 BT를 소유하고있는 AI컨트롤러를 가져와 AI컨트롤러를 소유하고있는 폰을 가져옴
	APawn* ControlPawn = OwnerComp.GetAIOwner()->GetPawn();
	if (!IsValid(ControlPawn))
	{
		return EBTNodeResult::Failed;
	}

	//#include "NavigationSystem.h" 추가
	//ControlPawn을 이용해 월드를 가져와 월드에서 UNavigationSystemV1을 가져옴
	UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetNavigationSystem(ControlPawn->GetWorld());
	if (!IsValid(NavSystem))
	{
		return EBTNodeResult::Failed;
	}

	//블랙보드에 있는 데이터 가져오기
	//#include "BehaviorTree/BlackboardComponent.h" 추가
	FVector OriginPos = OwnerComp.GetBlackboardComponent()->GetValueAsVector(BBKEY_HOMEPOS);
	FNavLocation NextPatrolPos;
	float NextPosRadius;

	//#include "AbilitySystemComponent.h", #include "Attribute/PPGruntAttributeSet.h"
	//#include "AbilitySystemBlueprintLibrary.h" 추가
	UAbilitySystemComponent* ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(ControlPawn);
	if (!IsValid(ASC))
	{
		return EBTNodeResult::Failed;
	}

	const UPPGruntAttributeSet* AttributeSet = ASC->GetSet<UPPGruntAttributeSet>();
	if (!IsValid(AttributeSet))
	{
		return EBTNodeResult::Failed;
	}

	NextPosRadius = AttributeSet->GetAIPatrolRadius();

	//GetRandomPointInNavigableRadius(기준 위치, 반지름(반경), 위치를 받아올 FNavLocation)
	if (NavSystem->GetRandomPointInNavigableRadius(OriginPos, NextPosRadius, NextPatrolPos))
	{
		OwnerComp.GetBlackboardComponent()->SetValueAsVector(BBKEY_PATROLPOS, NextPatrolPos.Location);
		return EBTNodeResult::Succeeded;
	}

	return EBTNodeResult::Failed;
}

 

ExeucteTask 함수는 Task의 실행결과를 반환함

  • EBTNodeResult::Type Result = Super::ExecuteTask(OwnerComp, NodeMemory);
    • 노드결과를 저장하는 Result변수를 생성
    • 부모함수를 실행하여 결과 저장
  • APawn* ControlPawn = OwnerComp.GetAIOwner()->GetPawn();
    if (!IsValid(ControlPawn))
    {
           return EBTNodeResult::Failed;
    }
    • ExecuteTask의 매개변수로 들어오는 OwnerComp를 이용하여 Pawn을 가져옴
    • 만약 가져오지 못했다면 실패를 리턴
    • #include "AIController.h" 추가
  • UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetNavigationSystem(ControlPawn->GetWorld());
    if (!IsValid(NavSystem))
    {
         return EBTNodeResult::Failed;
    }
    • #include "NavigationSystem.h" 추가
    • UNavigationSystemV1의 스태이틱 함수 GetNavigationSystem를 사용하여 NavSystem을 가져옴
    • ControlPawn을 이용해 월드를 가져와 월드에서 UNavigationSystemV1을 가져옴
    • 만약 가져오지 못했다면 실패를 리턴
  • FVector OriginPos = OwnerComp.GetBlackboardComponent()->GetValueAsVector(BBKEY_HOMEPOS);
    • 블랙보드에 저장되어 있는 BBKEY_HOMEPOS를 가져와 저장
  • FNavLocation NextPatrolPos;
    • 네브매쉬를 이용해 다음 이동 장소를 저장할 변수
  • float NextPosRadius;
    • 네브매쉬의 반경을 저장할 변수
  • UAbilitySystemComponent* ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(ControlPawn);
    if (!IsValid(ASC))
    {
    return EBTNodeResult::Failed;
    }
    • #include "AbilitySystemComponent.h", #include "Attribute/PPGruntAttributeSet.h"
      #include "AbilitySystemBlueprintLibrary.h" 추가
    • ASC를 가져옴 못가져오면 실패 리턴
  • const UPPGruntAttributeSet* AttributeSet = ASC->GetSet<UPPGruntAttributeSet>();
    if (!IsValid(AttributeSet))
    {
    return EBTNodeResult::Failed;
    }
    • NPC의 어트리뷰트셋을 가져옴 못가져오면 실패 리턴
  • NextPosRadius = AttributeSet->GetAIPatrolRadius();
    • 어트리뷰트에 저장된 정찰 반경을 가져옴
  • NavSystem->GetRandomPointInNavigableRadius(OriginPos, NextPosRadius, NextPatrolPos)
    • GetRandomPointInNavigableRadius(기준 위치, 반지름(반경), 위치를 저장할 FNavLocation)
    • NavSystem을 이용하여 정찰위치를 가져옴
      • 가져온게 성공하면
      • OwnerComp.GetBlackboardComponent()->SetValueAsVector(BBKEY_PATROLPOS, NextPatrolPos.Location);
      • 블랙보드에 정찰위치를 저장
      • 태스크 결과 성공을 리턴
  • return EBTNodeResult::Failed;