문제 상황
언리얼 엔진에서 보스 캐릭터로부터 적 캐릭터를 동적으로 생성했을 때, AI 컨트롤러가 제대로 초기화되지 않아 적 캐릭터들이 움직이지 않는 문제가 발생했습니다. 그러나 같은 적 캐릭터를 레벨에 정적으로 배치했을 때는 정상적으로 작동했습니다.
흥미로운 점: 동일한 캐릭터 클래스와 AI 컨트롤러를 사용함에도 불구하고 생성 방식에 따라 작동 여부가 달라졌습니다.
원인 분석
문제의 핵심은 다음과 같았습니다:
- 동적으로 생성된 적 캐릭터의 경우, AI 컨트롤러의
OnPossess
함수가 호출되지 않았습니다. - 이는 캐릭터가 생성된 후 AI 컨트롤러가 자동으로 생성되고 캐릭터에 부착되는 과정에서 문제가 발생했다는 의미입니다.
- 정적으로 배치된 적 캐릭터는 레벨 로드 시 정상적인 순서로 초기화되어 문제가 없었습니다.
해결 방법
해결책: 적 캐릭터의 BeginPlay
메서드에서 SpawnDefaultController
함수를 명시적으로 호출하여 AI 컨트롤러를 강제로 초기화했습니다.
적 캐릭터 클래스의 BeginPlay 수정
void AEnemy::BeginPlay() `
{ Super::BeginPlay();
`// 기존 코드... // AI 컨트롤러가 없으면 강제로 생성 if (!GetController()) { SpawnDefaultController(); } `
}
`
왜 ?
SpawnDefaultController
함수는 APawn
클래스의 메서드로, 다음 작업을 수행합니다:
- AIControllerClass 속성을 확인하여 적절한 AI 컨트롤러 클래스를 찾습니다.
- 해당 클래스의 인스턴스를 생성합니다.
- 해당 컨트롤러가 폰(Pawn)을 제어하도록 설정합니다.
- 이 과정에서
OnPossess
함수가 호출됩니다.
동적으로 생성된 AI 캐릭터의 경우, 자동 컨트롤러 초기화에만 의존하지 말고 추가적인 초기화 단계가 필요할 수 있습니다.
1. 지연된 초기화 사용
// 소환 코드 내에서
AEnemy* SpawnedEnemy = World->SpawnActor(...); if (SpawnedEnemy) { // 지연된 컨트롤러 초기화 FTimerHandle TimerHandle; FTimerDelegate TimerDel; TimerDel.BindLambda([SpawnedEnemy]() { if (!SpawnedEnemy->GetController()) { SpawnedEnemy->SpawnDefaultController(); } });
GetWorld()->GetTimerManager().SetTimer(TimerHandle, TimerDel, 0.2f, false);
}
2. 수동으로 컨트롤러 생성 및 부착
AEnemy* SpawnedEnemy = World->SpawnActor<AEnemy>(...);
if (SpawnedEnemy)
{ // 기존 컨트롤러 분리
if (SpawnedEnemy->GetController()) { SpawnedEnemy->GetController()->UnPossess();
}
// 새 AI 컨트롤러 직접 생성
AAIController* NewController = World->SpawnActor<AAIController>(
AEnemyAIController::StaticClass(), SpawnedEnemy->GetActorLocation(),
SpawnedEnemy->GetActorRotation));
if (NewController)
{
// 직접 Possess 호출 NewController->Possess(SpawnedEnemy);
}
포인트
- 생명주기 이해: 언리얼 엔진에서 액터와 컨트롤러의 생명주기와 초기화 순서를 이해하는 것이 중요합니다.
- 디버깅 접근법: 로그를 통해
OnPossess
와 같은 함수가 호출되는지 확인하는 것이 문제 발견에 도움이 됩니다. - 다양한 해결책: 언리얼 엔진은 같은 문제를 해결하기 위한 여러 접근 방식을 제공합니다.
- 정적 vs 동적 생성: 동일한 클래스의 액터라도 생성 방식에 따라 초기화 과정이 달라질 수 있습니다.