반응형

좀비 디펜스 게임은 웨이브마다 적의 수와 종류가 달라져야 합니다. 또한 플레이어 바로 앞에서 적이 갑자기 생기면 어색하기 때문에, 웨이브 데이터와 생성 지점 선택을 분리해서 관리했습니다.

1. 웨이브마다 코드의 숫자를 바꾸지 않기

초기 지연 시간, 생성 간격, 적 수, 좀비 종류의 비율, 체력 배율과 같은 값을 코드에 고정하면 밸런스를 조정하기 번거로워집니다.

WaveCampaignSO에서는 각 웨이브의 정보를 WaveDefinition으로 관리하도록 했습니다.

[Serializable]
public class WaveDefinition
{
    public int enemyCount = 12;
    public float initialDelay = 0.5f;
    public float spawnInterval = 0.8f;
    public int burstSize = 4;
    public Vector4 archetypeWeights = new(1f, 0f, 0f, 0f);
    public float healthMultiplier = 1f;
    public ZombieAI.TargetMode targetMode = ZombieAI.TargetMode.Player;
}

실제로는 거리·드롭 확률·보스 등장 여부 등의 값도 포함됩니다. 이러한 데이터를 GameCycleDirector가 읽어 GateWaveSpawner.ConfigureWave()에 전달합니다.

2. 생성 위치를 플레이어 시야와 거리로 결정하기

적 생성 위치는 단순히 스폰 포인트 배열을 순서대로 읽는 것이 아닙니다. GateWaveSpawner.SelectSpawnPoint()에서는 플레이어와 스폰 위치 사이의 거리를 계산하고, 카메라에 보이는 위치인지 검사합니다.

float distance = player != null
    ? Vector3.Distance(player.position, point.position)
    : minimumSpawnDistance;
if (distance < minimumSpawnDistance) continue;

bool visible = camera != null &&
    IsVisibleFromCamera(camera, point.position + Vector3.up);
if (hiddenOnly && visible) continue;

지정된 최소 거리보다 가까우면 우선 제외하고, 숨겨진 지점만 찾는 동안에는 카메라에 보이는 포인트를 제외합니다. IsVisibleFromCamera()는 WorldToViewportPoint()로 카메라 화면 안인지 검사하고 Raycast로 차폐 여부를 확인합니다.

3. 숨겨진 위치를 찾지 못하면 어떻게 할까?

화면 밖 포인트가 없다는 이유로 웨이브가 영원히 멈춰서는 안 됩니다. 그래서 FindHiddenSpawnPointAsync()는 지정된 시간 동안 숨겨진 위치를 재검색하다가, 시간을 넘기면 일반 후보 선택으로 전환합니다.

즉 시야 밖 생성을 우선하되, 웨이브 진행이 멈추지 않도록 대체 경로를 마련했습니다. 이 경우에는 상황에 따라 화면에 보이는 위치가 선택될 수도 있습니다. 따라서 모든 적이 반드시 화면 밖에서 생성된다고 설명하는 것은 정확하지 않습니다.

4. 반복 생성 비용은 풀링으로 줄이기

GateWaveSpawner는 Awake()에서 정해진 수의 EnemyHealth를 미리 생성하고, 종류별 Queue에 보관합니다. 스폰할 때는 큐에서 꺼내 활성화하고, 제거될 때는 다시 풀에 반환합니다.

또 _spawnFinished와 _alive.Count를 함께 확인하여 예정된 소환을 모두 마쳤고 살아 있는 적도 없을 때 웨이브 완료 이벤트를 발생시킵니다. 첫 번째 조건이 없으면 스폰 사이의 잠깐 빈 시간에 웨이브가 끝난 것으로 판단할 수 있습니다.


정리

WaveCampaignSO는 수치와 등장 구성을, GateWaveSpawner는 생성 위치·시점·풀링을 담당합니다. 수치 조정과 실제 생성 처리를 분리했기 때문에 웨이브 구성 변경 시 스폰 로직을 건드리는 범위를 줄일 수 있습니다.


참고한 구현 코드

반응형