본문으로 건너뛰기
Unreal Engine C++ 기초집LESSON 21

Enemy Spawner와 난이도 조절

난이도초급
예상 시간50분
선수지식20강의 적 AI

21강. Enemy Spawner와 난이도 조절

1. 이번 강의에서 해결할 문제

Spawner가 항상 같은 위치에 같은 적을 같은 속도로 생성합니다. 시간이 지나면 난도가 오르되 최대 활성 수와 게임 오버 상태를 지키는 생성 규칙을 만듭니다.

2. 학습 목표

3. 핵심 개념

Spawner는 적 행동을 소유하지 않고 “언제, 무엇을, 어디에” 만들지만 결정합니다. EnemyDataAsset은 능력치, EnemyBase는 행동, Spawner는 생성 규칙을 담당합니다.

난이도는 매 프레임 증가시킬 필요가 없습니다. Spawn 시점의 경과 시간으로 새 간격을 계산하면 충분합니다. 최대 활성 수는 성능과 플레이 가능성을 동시에 보호하는 안전장치입니다.

4. 단계별 실습

파일 경로: Source/TopDownSurvival/Systems/EnemySpawner.h · 설정

EnemySpawner.h · 핵심 필드
class AEnemyBase;
class UEnemyDataAsset;

UPROPERTY(EditDefaultsOnly, Category = "Spawning")
TArray<TObjectPtr<UEnemyDataAsset>> EnemyTypes;

UPROPERTY(EditDefaultsOnly, Category = "Spawning", meta = (ClampMin = "100.0", Units = "cm"))
float SpawnRadius = 1200.0f;

UPROPERTY(EditDefaultsOnly, Category = "Spawning", meta = (ClampMin = "0.1", Units = "s"))
float InitialInterval = 2.5f;

UPROPERTY(EditDefaultsOnly, Category = "Spawning", meta = (ClampMin = "0.1", Units = "s"))
float MinimumInterval = 0.6f;

UPROPERTY(EditDefaultsOnly, Category = "Spawning", meta = (ClampMin = "1"))
int32 MaxActiveEnemies = 40;

TArray<TWeakObjectPtr<AEnemyBase>> ActiveEnemies;

파일 경로: Source/TopDownSurvival/Systems/EnemySpawner.cpp · 생성 규칙

EnemySpawner.cpp · SpawnEnemy
#include "Data/EnemyDataAsset.h"
#include "Enemies/EnemyBase.h"
#include "NavigationSystem.h"
#include "Systems/SurvivalGameSubsystem.h"

void AEnemySpawner::SpawnEnemy()
{
USurvivalGameSubsystem* Game =
GetGameInstance()->GetSubsystem<USurvivalGameSubsystem>();
if (!Game || Game->IsGameOver())
{
GetWorldTimerManager().ClearTimer(SpawnTimerHandle);
return;
}

ActiveEnemies.RemoveAll(
[](const TWeakObjectPtr<AEnemyBase>& Enemy) { return !Enemy.IsValid(); });

if (ActiveEnemies.Num() >= MaxActiveEnemies || EnemyTypes.IsEmpty())
{
return;
}

UEnemyDataAsset* Data =
EnemyTypes[FMath::RandRange(0, EnemyTypes.Num() - 1)];
if (!Data || !Data->EnemyClass)
{
return;
}

FNavLocation NavLocation;
UNavigationSystemV1* Navigation =
FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (!Navigation ||
!Navigation->GetRandomReachablePointInRadius(
GetActorLocation(), SpawnRadius, NavLocation))
{
return;
}

FActorSpawnParameters Params;
Params.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;

AEnemyBase* Enemy = GetWorld()->SpawnActor<AEnemyBase>(
Data->EnemyClass, NavLocation.Location, FRotator::ZeroRotator, Params);
if (Enemy)
{
Enemy->InitializeFromData(Data);
ActiveEnemies.Add(Enemy);
}

const float Elapsed = GetWorld()->GetTimeSeconds();
const float NextInterval = FMath::Max(
MinimumInterval, InitialInterval - Elapsed * 0.015f);
if (!FMath::IsNearlyEqual(NextInterval, SpawnInterval, 0.05f))
{
RestartSpawnTimer(NextInterval);
}
}

Unreal Editor

BP_EnemySpawner의 EnemyTypes에 DA_EnemyBasic과 DA_EnemyFast를 추가합니다. Nav Mesh 안쪽 중앙에 배치하고 MaxActiveEnemies를 20으로 낮춰 첫 테스트를 진행합니다.

5. 코드가 동작하는 이유

약한 참조 목록은 적을 소유하지 않으며 제거된 Actor는 prune 단계에서 빠집니다. Navigation System은 이동 가능한 위치를 반환합니다. 경과 시간이 늘수록 간격이 줄지만 MinimumInterval 아래로 내려가지 않아 무한히 빨라지는 규칙을 막습니다.

6. 자주 하는 실수와 해결법

7. 직접 실습

8. 이해 점검 질문 3개

9. 핵심 요약

MINI QUIZ

Enemy Spawner와 난이도 조절 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“Enemy Spawner와 난이도 조절”에서 다음 단계로 넘어가기 전에 확인할 핵심은 무엇인가요?
  2. 문제 2“Enemy Spawner와 난이도 조절”에서 ‘Spawner가 적 이동과 공격까지 관리’ 문제가 생겼습니다. 가장 알맞은 진단 또는 대응은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

21강. Enemy Spawner와 난이도 조절 미완료 상태