AI 컨트롤러 기본 구현
언리얼 엔진의 AI 시스템은 게임 내 인공지능 캐릭터를 구현하는 강력한 도구를 제공합니다.
이 절에서는 C++를 사용하여 AI 컨트롤러를 구현하는 방법을 살펴보겠습니다.
AAIController 클래스
AAIController는 AI 캐릭터의 행동을 제어하는 기본 클래스입니다.
UCLASS()
class AMyAIController : public AAIController
{
GENERATED_BODY()
public:
AMyAIController();
virtual void BeginPlay() override;
virtual void Tick(float DeltaTime) override;
// AI 행동 함수들
void MoveToRandomLocation();
void RotateTowardsTarget(AActor* Target);
private:
// AI 상태 변수들
UPROPERTY()
AActor* CurrentTarget;
UPROPERTY()
FVector TargetLocation;
};
AMyAIController::AMyAIController()
{
PrimaryActorTick.bCanEverTick = true;
}
void AMyAIController::BeginPlay()
{
Super::BeginPlay();
MoveToRandomLocation();
}
void AMyAIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (CurrentTarget)
{
RotateTowardsTarget(CurrentTarget);
}
}
AI 폰 생성 및 제어
AI 폰을 생성하고 AI 컨트롤러와 연결하는 방법
void AMyGameMode::SpawnAIPawn()
{
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
ACharacter* AIPawn = World->SpawnActor<ACharacter>(ACharacter::StaticClass(), SpawnLocation, SpawnRotation, SpawnParams);
if (AIPawn)
{
AMyAIController* AIController = World->SpawnActor<AMyAIController>(AMyAIController::StaticClass(), SpawnParams);
if (AIController)
{
AIController->Possess(AIPawn);
}
}
}
}
기본적인 AI 행동 구현
이동
void AMyAIController::MoveToRandomLocation()
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
FNavLocation RandomLocation;
if (NavSys->GetRandomReachablePointInRadius(GetPawn()->GetActorLocation(), 1000.0f, RandomLocation))
{
MoveToLocation(RandomLocation.Location);
}
}
}
회전
void AMyAIController::RotateTowardsTarget(AActor* Target)
{
if (Target && GetPawn())
{
FVector Direction = Target->GetActorLocation() - GetPawn()->GetActorLocation();
FRotator NewRotation = Direction.Rotation();
NewRotation.Pitch = 0.0f;
NewRotation.Roll = 0.0f;
GetPawn()->SetActorRotation(NewRotation);
}
}
목표 추적
void AMyAIController::SetTarget(AActor* NewTarget)
{
CurrentTarget = NewTarget;
if (CurrentTarget)
{
MoveToActor(CurrentTarget);
}
}
AI 인식 시스템 구현
시야 구현
UCLASS()
class AMyAIController : public AAIController
{
// ...
UPROPERTY(VisibleAnywhere, Category = "AI")
class UPawnSensingComponent* PawnSensingComp;
UFUNCTION()
void OnPawnSeen(APawn* SeenPawn);
};
AMyAIController::AMyAIController()
{
PawnSensingComp = CreateDefaultSubobject<UPawnSensingComponent>(TEXT("PawnSensingComp"));
PawnSensingComp->SetPeripheralVisionAngle(60.0f);
PawnSensingComp->SightRadius = 1000.0f;
}
void AMyAIController::BeginPlay()
{
Super::BeginPlay();
PawnSensingComp->OnSeePawn.AddDynamic(this, &AMyAIController::OnPawnSeen);
}
void AMyAIController::OnPawnSeen(APawn* SeenPawn)
{
if (SeenPawn && SeenPawn->IsPlayerControlled())
{
SetTarget(SeenPawn);
}
}
청각 구현
UCLASS()
class AMyAIController : public AAIController
{
// ...
UFUNCTION()
void OnNoiseHeard(APawn* NoiseInstigator, const FVector& Location, float Volume);
};
void AMyAIController::BeginPlay()
{
Super::BeginPlay();
PawnSensingComp->OnHearNoise.AddDynamic(this, &AMyAIController::OnNoiseHeard);
}
void AMyAIController::OnNoiseHeard(APawn* NoiseInstigator, const FVector& Location, float Volume)
{
if (NoiseInstigator && NoiseInstigator->IsPlayerControlled())
{
MoveToLocation(Location);
}
}
AI 상태 관리
간단한 상태 머신을 구현하여 AI의 행동을 관리할 수 있습니다.
UENUM(BlueprintType)
enum class EAIState : uint8
{
Idle,
Patrolling,
Chasing,
Attacking
};
UCLASS()
class AMyAIController : public AAIController
{
// ...
UPROPERTY(VisibleAnywhere, Category = "AI")
EAIState CurrentState;
void UpdateAIState();
};
void AMyAIController::UpdateAIState()
{
switch (CurrentState)
{
case EAIState::Idle:
// 대기 상태 로직
break;
case EAIState::Patrolling:
// 순찰 상태 로직
break;
case EAIState::Chasing:
// 추적 상태 로직
break;
case EAIState::Attacking:
// 공격 상태 로직
break;
}
}
플레이어와의 상호작용
플레이어와 AI의 상호작용을 구현하는 예
void AMyAIController::OnPawnSeen(APawn* SeenPawn)
{
if (SeenPawn && SeenPawn->IsPlayerControlled())
{
CurrentState = EAIState::Chasing;
SetTarget(SeenPawn);
}
}
void AMyAIController::AttackPlayer()
{
if (CurrentTarget && CurrentState == EAIState::Chasing)
{
float DistanceToTarget = FVector::Distance(GetPawn()->GetActorLocation(), CurrentTarget->GetActorLocation());
if (DistanceToTarget <= AttackRange)
{
CurrentState = EAIState::Attacking;
// 공격 로직 구현
}
}
}
AI 시스템의 성능 최적화 전략
- 틱 최적화
void AMyAIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
TickCounter += DeltaTime;
if (TickCounter >= TickInterval)
{
PerformAILogic();
TickCounter = 0.0f;
}
}
- LOD (Level of Detail) 시스템 구현
void AMyAIController::UpdateAILOD()
{
float DistanceToPlayer = FVector::Distance(GetPawn()->GetActorLocation(), PlayerPawn->GetActorLocation());
if (DistanceToPlayer < NearLODDistance)
{
// 높은 수준의 AI 로직 실행
}
else if (DistanceToPlayer < MediumLODDistance)
{
// 중간 수준의 AI 로직 실행
}
else
{
// 낮은 수준의 AI 로직 실행
}
}
디버깅 기법
- 시각적 디버깅
void AMyAIController::DrawDebugInfo()
{
if (GetPawn())
{
FVector Location = GetPawn()->GetActorLocation();
DrawDebugSphere(GetWorld(), Location, 50.0f, 12, FColor::Red, false, -1.0f, 0, 1.0f);
DrawDebugString(GetWorld(), Location + FVector(0, 0, 100), AIStateToString(CurrentState), nullptr, FColor::White, 0.0f, true);
}
}
- 로그 출력
void AMyAIController::LogAIState()
{
UE_LOG(LogAI, Verbose, TEXT("AI State: %s, Target: %s"), *AIStateToString(CurrentState), *GetNameSafe(CurrentTarget));
}
확장 가능한 AI 아키텍처 설계
모듈화된 AI 시스템을 구현하여 확장성을 높일 수 있습니다.
UCLASS(Abstract)
class UAIModule : public UObject
{
GENERATED_BODY()
public:
virtual void UpdateModule(float DeltaTime) PURE_VIRTUAL(UAIModule::UpdateModule, );
};
UCLASS()
class AModularAIController : public AAIController
{
GENERATED_BODY()
private:
UPROPERTY()
TArray<UAIModule*> AIModules;
public:
void AddAIModule(TSubclassOf<UAIModule> ModuleClass);
virtual void Tick(float DeltaTime) override;
};
void AModularAIController::AddAIModule(TSubclassOf<UAIModule> ModuleClass)
{
UAIModule* NewModule = NewObject<UAIModule>(this, ModuleClass);
if (NewModule)
{
AIModules.Add(NewModule);
}
}
void AModularAIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
for (UAIModule* Module : AIModules)
{
Module->UpdateModule(DeltaTime);
}
}
모듈화된 접근 방식을 통해 새로운 AI 기능을 쉽게 추가하고 기존 기능을 재사용할 수 있습니다.
언리얼 엔진의 AI 시스템은 강력하고 유연한 도구를 제공하여 복잡한 AI 행동을 구현할 수 있게 해줍니다.
AAIController 클래스를 기반으로 커스텀 AI 컨트롤러를 만들고, 다양한 AI 행동을 구현할 수 있습니다.
기본적인 이동, 회전, 목표 추적 등의 기능부터 시작하여, 시야와 청각을 포함한 인식 시스템, 상태 관리, 그리고 플레이어와의 상호작용까지 구현할 수 있습니다.
이를 통해 게임 세계에서 자연스럽게 반응하고 행동하는 AI 캐릭터를 만들 수 있습니다.