포트폴리오 제작

Project_P 행동트리 모델 AI Perception

k99812 2024. 12. 9. 17:02

블랙보드의 타겟정보를 업데이트 하기위해 언리얼에서 제공해주는 AI Perception기능을 사용했다

AI Perception은 AI(NPC)가 액터나 다른 Pawn을 인식할 수 있는 여러 감각들을 제공해준다.

AIPerception은 시각, 듣기 등 여러 감각이 있어 이를 활용하였다

 

공식문서 : https://dev.epicgames.com/documentation/en-us/unreal-engine/ai-perception-in-unreal-engine

 

AI Perception 시각으로 블랙보드의 타겟변수를 설정 및 해지를 할것이다

 

APPAIController 헤더파일


AI Perception을 사용하기 위해 AIController에 AIPerceptionComponent, UAISenseConfig를 생성을 해야된다

 

#include "CoreMinimal.h"
#include "AIController.h"
#include "Perception/AIPerceptionTypes.h"
#include "PPAIController.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT_P_API APPAIController : public AAIController
{
//AI Section
protected:
	UFUNCTION()
	virtual void ActorPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus);

	UFUNCTION()
	virtual void ActorPerceptionForgetUpdated(AActor* Actor);

	virtual void PerceptionSensedSight(APawn* Pawn_);

	virtual void PerceptionSensedHearing(APawn* Pawn_);

	virtual void PerceptionSensedDamage(APawn* Pawn_);

	UPROPERTY(VisibleAnywhere)
	TObjectPtr<class UAIPerceptionComponent> AIPerceptionComp;

	UPROPERTY(VisibleAnywhere)
	TObjectPtr<class UAISenseConfig_Sight> SenseConfig_Sight;

	UPROPERTY(VisibleAnywhere)
	TObjectPtr<class UAISenseConfig_Hearing> SenseConfig_Hearing;

	UPROPERTY(VisibleAnywhere)
	TObjectPtr<class UAISenseConfig_Damage> SenseConfig_Damage;

	UPROPERTY(VisibleAnywhere, Category = "Data")
	TObjectPtr<class UPPGruntAIData> GruntAIData;
};

 

  • UPROPERTY(VisibleAnywhere)
    TObjectPtr<class UAIPerceptionComponent> AIPerceptionComp;
    • AI 인식 컴포넌트
  • UPROPERTY(VisibleAnywhere)
    TObjectPtr<class UAISenseConfig_Sight> SenseConfig_Sight;
    • AI 시야 Config
  • UPROPERTY(VisibleAnywhere)
    TObjectPtr<class UAISenseConfig_Hearing> SenseConfig_Hearing;
    • AI 청각 Config
  • UPROPERTY(VisibleAnywhere)
    TObjectPtr<class UAISenseConfig_Damage> SenseConfig_Damage;
    • AI 데미지 Config
  • UPROPERTY(VisibleAnywhere, Category = "Data")
    TObjectPtr<class UPPGruntAIData> GruntAIData;
    • AI Config파일의 변수들을 초기화할 데이터 애셋
    • 데이터 애셋을 만들어 생성자에서 이를 활용해 Config를 초기화
  • UFUNCTION()
    virtual void ActorPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus);

    UFUNCTION()
    virtual void ActorPerceptionForgetUpdated(AActor* Actor);
    • AIPerception에 따라 실행되는 델리게이트에 등록할 콜백함수들

 

Cpp 파일


생성자

APPAIController::APPAIController()
{
	static ConstructorHelpers::FObjectFinder<UPPGruntAIData> AIDataRef(TEXT("/Script/Project_P.PPGruntAIData'/Game/Project_P/Data/GruntAIData.GruntAIData'"));
	if (AIDataRef.Object)
	{
		GruntAIData = AIDataRef.Object;
	}
	
// AI Perception 설정
	AIPerceptionComp = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerceptionComp"));
	SetPerceptionComponent(*AIPerceptionComp);

// Sight Config
	SenseConfig_Sight = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SenseConfig_Sight"));

	SenseConfig_Sight->SightRadius = GruntAIData->SightRadius;
	SenseConfig_Sight->LoseSightRadius = GruntAIData->LoseSightRadius;
	SenseConfig_Sight->PeripheralVisionAngleDegrees = GruntAIData->AIVisionAngleDeg;
	//탐지하면 몇초(age)동안 탐지를 유지할지
	SenseConfig_Sight->SetMaxAge(GruntAIData->AISenseAge);
	//마지막으로 감지된 객체의 위치 탐지 성공 - 시야 범위를 벗어난 상태에서도 객체를 일정 거리 이내에서 추적할 수 있다.
	SenseConfig_Sight->AutoSuccessRangeFromLastSeenLocation = GruntAIData->AutoSuccessRange;

	SenseConfig_Sight->DetectionByAffiliation.bDetectEnemies = true;
	SenseConfig_Sight->DetectionByAffiliation.bDetectFriendlies = true;
	SenseConfig_Sight->DetectionByAffiliation.bDetectNeutrals = true;

	AIPerceptionComp->ConfigureSense(*SenseConfig_Sight);
	AIPerceptionComp->SetDominantSense(SenseConfig_Sight->GetSenseImplementation());

// Hearing Config
	SenseConfig_Hearing = CreateDefaultSubobject<UAISenseConfig_Hearing>(TEXT("SenseConfig_Hearing"));

	SenseConfig_Hearing->HearingRange = GruntAIData->HearingRange;
	SenseConfig_Hearing->SetMaxAge(GruntAIData->AISenseAge);

	SenseConfig_Hearing->DetectionByAffiliation.bDetectEnemies = true;
	SenseConfig_Hearing->DetectionByAffiliation.bDetectFriendlies = true;
	SenseConfig_Hearing->DetectionByAffiliation.bDetectNeutrals = true;

	AIPerceptionComp->ConfigureSense(*SenseConfig_Hearing);

// Damage Config
	SenseConfig_Damage = CreateDefaultSubobject<UAISenseConfig_Damage>(TEXT("SenseConfig_Damage"));

	AIPerceptionComp->OnTargetPerceptionUpdated.AddDynamic(this, &APPAIController::ActorPerceptionUpdated);
	AIPerceptionComp->OnTargetPerceptionForgotten.AddDynamic(this, &APPAIController::ActorPerceptionForgetUpdated);
}

 

AISenseConfig 관련 설정들은 생성자에서 초기화 되기때문에 다른 함수들(ex : BeginPlay, PostInitializeComponents)에서 설정을 해도 적용되지 않음

  • static ConstructorHelpers::FObjectFinder<UBlackboardData> BlackboardRef(TEXT("~~~'"));
    if (BlackboardRef.Object)
    {
         BBAsset = BlackboardRef.Object;
    }
    •  데이터애셋 가져오기
  • AIPerceptionComp = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerceptionComp"));
    SetPerceptionComponent(*AIPerceptionComp);
    • AIPerception 생성 및 등록
  • SenseConfig_Sight = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("SenseConfig_Sight"));
    • 시야 Config 생성
  • SenseConfig_Sight->SightRadius = GruntAIData->SightRadius;
    SenseConfig_Sight->LoseSightRadius = GruntAIData->LoseSightRadius;
    SenseConfig_Sight->PeripheralVisionAngleDegrees = GruntAIData->AIVisionAngleDeg;
    • 각각 시야 반경, 시야에서 나갈 때 반경, 시야각 설정
  • SenseConfig_Sight->SetMaxAge(GruntAIData->AISenseAge);
    • 시야에서 나가면 데이터를 몇초 유지할 것인지 설정
  • SenseConfig_Sight->AutoSuccessRangeFromLastSeenLocation = GruntAIData->AutoSuccessRange;
    • 위치 탐지 성공하고 시야 범위를 벗어난 상태에서도 객체를 설정한 거리 이내에서 추적할 수 있다.
  • SenseConfig_Sight->DetectionByAffiliation.bDetectEnemies = true;
    SenseConfig_Sight->DetectionByAffiliation.bDetectFriendlies = true;
    SenseConfig_Sight->DetectionByAffiliation.bDetectNeutrals = true;
    • AI가 적, 아군, 중립을 감지할지 설정
    • GenericTeamAgentInterface를 활용해 사용하는 것 같음
    • 액터를 인터페이스를 통해 지정하지 않으면 중립(Neutrals)

https://forums.unrealengine.com/t/ai-perception-mark-player-as-enemy/354383

 

AI Perception Mark player as enemy

How do I mark the player as an enemy for my AI so that they will sense him when using “Detection by Affiliation: Detect Enemies”? Also see this old topic: https://answers.unrealengine.com/questions/285849/aiperception-how-to-use-detection-by-affiliatio

forums.unrealengine.com

 

  • AIPerceptionComp->ConfigureSense(*SenseConfig_Sight);
    • PerceptionComp에 설정한 시각 Configure을 등록
  • AIPerceptionComp->SetDominantSense(SenseConfig_Sight->GetSenseImplementation());
    • 시야를 PerceptionComp의 메인감각으로 지정
  • SenseConfig_Hearing = CreateDefaultSubobject<UAISenseConfig_Hearing>(TEXT("SenseConfig_Hearing"));
    • 청각 Config 생성
  • SenseConfig_Hearing->HearingRange = GruntAIData->HearingRange;
    SenseConfig_Hearing->SetMaxAge(GruntAIData->AISenseAge);
    • 청각 범위, 정보 유효 시간 설정
  • SenseConfig_Hearing->DetectionByAffiliation.bDetectEnemies = true;
    SenseConfig_Hearing->DetectionByAffiliation.bDetectFriendlies = true;
    SenseConfig_Hearing->DetectionByAffiliation.bDetectNeutrals = true;
    • 감지 설정
  • AIPerceptionComp->ConfigureSense(*SenseConfig_Hearing);
    • PerceptionComp에 청각 등록
  • AIPerceptionComp->OnTargetPerceptionUpdated.
    AddDynamic(this, &APPAIController::ActorPerceptionUpdated);

    AIPerceptionComp->OnTargetPerceptionForgotten.
    AddDynamic(this, &APPAIController::ActorPerceptionForgetUpdated);
    • PerceptionComp의 감각 업데이트, 감각 잊힘 델리게이트에 콜백함수 등록

 

ActorPerceptionUpdated

void APPAIController::ActorPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
{
	APawn* PerceptionedPawn = Cast<APawn>(Actor);

	if (PerceptionedPawn && PerceptionedPawn->GetController()->IsPlayerController())
	{
		TSubclassOf<UAISense> SensedStimulsClass = UAIPerceptionSystem::GetSenseClassForStimulus(this, Stimulus);

		if (SensedStimulsClass == UAISense_Sight::StaticClass())
		{
			PerceptionSensedSight(PerceptionedPawn);
		}

		if (SensedStimulsClass == UAISense_Hearing::StaticClass())
		{
			PerceptionSensedHearing(PerceptionedPawn);
		}
	}
}

 

OnTargetPerceptionUpdated 델리게이트에 등록된 콜백함수

해당 함수에서 감각에 대한 처리를 해줘야 처리한 감각이 디버깅 화면에 드로우 된다

 

  • APawn* PerceptionedPawn = Cast<APawn>(Actor);
    • 감각으로 들어온 액터를 Pawn으로 캐스팅
  • if (PerceptionedPawn && PerceptionedPawn->GetController()->IsPlayerController())
    • Pawn이 캐스팅 되었고 해당폰이 플레이어라면(플레이어 컨트롤러가 있으면)
  • TSubclassOf<UAISense> SensedStimulsClass = 
    UAIPerceptionSystem::GetSenseClassForStimulus(this, Stimulus);
    • UAIPerceptionSystem의 GetSenseClassForStimulus함수로 FAIStimulus의 감각 클래스를 얻을 수 있다
      UAISense 서브클래스로 감각을 받아온다
  • if (SensedStimulsClass == UAISense_Sight::StaticClass())
    {
    PerceptionSensedSight(PerceptionedPawn);
    }

    if (SensedStimulsClass == UAISense_Hearing::StaticClass())
    {
    PerceptionSensedHearing(PerceptionedPawn);
    }
    • 감각별로 미리 생성한 함수로 처리를 해준다

 

ActorPerceptionForgetUpdated

void APPAIController::ActorPerceptionForgetUpdated(AActor* Actor)
{
	APawn* PerceptionedPawn = Cast<APawn>(Actor);

	if (PerceptionedPawn && PerceptionedPawn->GetController()->IsPlayerController())
	{
		UE_LOG(LogTemp, Log, TEXT("ActorPerceptionForgetUpdated : %s"), *Actor->GetName());

		APawn* Target = Cast<APawn>(GetBlackboardComponent()->GetValueAsObject(BBKEY_TARGET));
		if (PerceptionedPawn == Target)
		{
			GetBlackboardComponent()->SetValueAsObject(BBKEY_TARGET, nullptr);
			AActor::SetActorTickEnabled(false);
		}
	}
}

 

  • APawn* PerceptionedPawn = Cast<APawn>(Actor);
    • Pawn으로 캐스팅
  • if (PerceptionedPawn && PerceptionedPawn->GetController()->IsPlayerController())
    • 캐스팅 확인 및 플레이어 확인
  • APawn* Target = Cast<APawn>(GetBlackboardComponent()->GetValueAsObject(BBKEY_TARGET));
    • 블랙보드 타겟 가져오기
  • if (PerceptionedPawn == Target)
    • 타겟이 잊혀진 폰이랑 같으면
    • GetBlackboardComponent()->SetValueAsObject(BBKEY_TARGET, nullptr);
      • 블랙보드 초기화

 

PerceptionSensedSight

void APPAIController::PerceptionSensedSight(APawn* PerceptionedPawn)
{
	UE_LOG(LogTemp, Log, TEXT("ActorPerceptionUpdated : %s"), *PerceptionedPawn->GetName());

	UAbilitySystemComponent* ASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(PerceptionedPawn);
	if (ASC)
	{
		GetBlackboardComponent()->SetValueAsObject(BBKEY_TARGET, PerceptionedPawn);
		AActor::SetActorTickEnabled(true);
	}
}

 

시각처리 함수

  • UAbilitySystemComponent* ASC = 
    UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(PerceptionedPawn);
    • ASC 가져오기
  • GetBlackboardComponent()->SetValueAsObject(BBKEY_TARGET, PerceptionedPawn);
    • 타겟 설정

 

추가적으로


청각처리 함수는 지금은 단순 로그만 나오게 구현함

 

시각함수는 Config만 설정하여 등록해주면 자동으로 감지하지만 청각, 데미지 감지는 따로 스테이틱 함수를 실행해야 됨

ex) 청각 리포트 함수 UAISense_Hearing::ReportNoiseEvent