본문 바로가기

게임프로그래밍/실습1

[언리얼 실습] 60. 에너미의 순찰, 추격, 공격 상태 구분하기


목차

  1.  Enum으로 캐릭터 상태 구분하기
  2.  거리가 멀면 다시 순찰하게 하기

1. Enum으로 캐릭터 상태 구분하기

 

에너미에게 열거형으로 상태를 지정해 해당 상태에 맞는 행동을 하게 하려고 한다. 열거형을 모아둔 CharacterType 파일이 있고 이곳에서 새로운 열거형을 만들자.

 

UENUM(BlueprintType)
enum class EEnemyState : uint8
{
	EES_Patrolling UMETA(DisplayName = "Patrolling"),
	EES_Chasing UMETA(DisplayName = "Chasing"),
	EES_Attacking UMETA(DisplayName = "Attacking")

};

 

이제 에너미 클래스에서 이 열거형을 바탕으로 상태를 지정해주자.

 

EEnemyState EnemyState = EEnemyState::EES_Patrolling;

 

이제 Tick 함수에서 캐릭터에 상황에 맞는 함수를 호출하면 된다.

 

void AEnemy::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if (EnemyState > EEnemyState::EES_Patrolling)
	{
		CheckCombatTarget();
	}
	else
	{
		CheckPatrolTarget();
	}
}
// 순찰 대상이 범위안에 들어왔으면 다음 타겟을 정하고 순찰을 진행하는 함수

void AEnemy::CheckPatrolTarget()
{
	if (InTargetRange(PatrolTarget, PatrolRadius))
	{
		PatrolTarget = ChoosePatrolTarget();
		const float WaitTime = FMath::RandRange(WaitMin, WaitMax);
		GetWorldTimerManager().SetTimer(PatrolTimer, this, &AEnemy::PatrolTimerFinished, WaitTime);
	}
}

 

// 상대가 전투 반경 안에 들어온게 아니면 전투상대를 초기화하고 체력바를 안보이게 하는 함수

void AEnemy::CheckCombatTarget()
{
	if (!InTargetRange(CombatTarget, CombatRadius))
	{
		CombatTarget = nullptr;
		if (HealthBarWidget)
		{
			HealthBarWidget->SetVisibility(false);
		}
	}
}

 

 

전에 우리는 타겟을 관찰하면 작동시킬 콜백함수를 만들었다. 플레이어를 봤으니 추격상태로 변경해야할 것이다.

 

void AEnemy::PawnSeen(APawn* SeenPawn)
{

}

 

여기서 SeenPawn 변수가 플레이어인지 확인을 해야한다. 캐스팅을 해서 플레이어가 맞는지 확인할 수 있지만 본 함수는 상당히 자주 호출되는 함수이고 그때마다 캐스팅을 하면 비용이 많이든다.

 

그렇기 때문에 캐릭터 클래스에 태그를 추가하고 SeenPawn에 해당 태그가 있으면 이후 동작을 진행하려고 한다.

 

그러기 위해서 캐릭터 클래스의 BeginPlay로 들어가 태그를 추가해주자.

 

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

	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(KnightMappingContenxt, 0);
		}
	}

	Tags.Add(FName("KnightCharacter"));
	
}

 

이제 태그를 비교하고 에너미가 플레이어를 추격할 수 있게 해보자.

 

void AEnemy::PawnSeen(APawn* SeenPawn)
{
	if (EnemyState == EEnemyState::EES_Chasing) return;

	if (SeenPawn->ActorHasTag(FName("KnightCharacter")))
	{
		EnemyState = EEnemyState::EES_Chasing;
		GetWorldTimerManager().ClearTimer(PatrolTimer);
		GetCharacterMovement()->MaxWalkSpeed = 300.f;
		CombatTarget = SeenPawn;
		MoveToTarget(CombatTarget);
	}
}

 

이제 이렇게 하면 캐릭터를 추격할 수 있게 된다.

 

현재 추격이 가능하지만 한번 플레이어를 놓치면 다시는 어떠한 행동도 하지 않는다.  이 부분을 고치도록 해보자


2. 거리가 멀면 다시 순찰하게 하기

 

현재 에너미가 플레이어랑 멀리 떨어지면 그저 멀뚱멀뚱 서있을 뿐이다. 순찰을 다시 하도록 바꿔보자.

 

현재 이미 CheckCombatTarget() 함수에서 플레이어랑 에너미랑 거리가 먼지 아닌지를 확인하고 거리가 멀면 Combat 타겟을 초기화 하고 있다. 이 함수에서 구문을 추가하면 된다.

 

void AEnemy::CheckCombatTarget()
{
	if (!InTargetRange(CombatTarget, CombatRadius))
	{
		CombatTarget = nullptr;
		if (HealthBarWidget)
		{
			HealthBarWidget->SetVisibility(false);
		}
        
        // 순찰상태로 바꾸고
		EnemyState = EEnemyState::EES_Patrolling; 
        // 속도를 다시 걷는 속도로 바꾼다.
		GetCharacterMovement()->MaxWalkSpeed = 125.f;
        // 그리고 순찰대상을 향해 이동하도록 한다.
		MoveToTarget(PatrolTarget);
	}
}

 

이렇게 하면 흥미를 잃었을때 다시 순찰을 하게 된다.