개인프로젝트/IronBird

[IronBird #4] 적 스폰 시스템 구현

Client Side 2026. 5. 28. 13:06

IronBird 개발일지 #4

적 스폰 시스템 구현

Object Pool · 충돌 처리 · 터널링 · bSweep

🎯 목표

위에서 아래로 내려오는 적 기체를 스폰하고, 플레이어 총알과 충돌 시 처리한다. #3에서 증명한 Object Pool을 처음부터 적용해 Spawn/Destroy 비용을 원천 차단한다.

#3에서 Spawn/Destroy의 문제를 수치로 확인했고, 적 스폰 시스템 설계 시 처음부터 Object Pool을 적용했다.

🔧 구현 내용

클래스 역할
AEnemy -X 직선 이동, 화면 밖/충돌 시 Pool 반납
AEnemyPool 적 20개 사전 생성, GetEnemy() / ReturnEnemy()
AEnemySpawner 2초 타이머, X=1500 고정 / Y축 랜덤(-400~400) 스폰
// 충돌 처리 흐름 (Enemy 쪽에서만 처리 - 이중 호출 방지)
Enemy::OnOverlapBegin(총알 감지)
  → HitBullet->ReturnToPool()  // 총알 → BulletPool 반납
  → this->ReturnToPool()      // 적 → EnemyPool 반납
총알에 적이 충돌하면 적과 총알이 사라진다.
오브젝트 풀링으로 구현하여 적을 처치해도 액터가 아웃라이너에 존재한다.

📖 터널링(Tunneling)이란?

고속으로 이동하는 오브젝트가 충돌 감지를 건너뛰는 현상이다. 물리 엔진은 매 프레임마다 오브젝트의 위치를 체크하는데, 오브젝트가 한 프레임에 이동하는 거리가 충돌 대상의 두께보다 크면 충돌 판정 없이 통과해버린다.

IronBird에서 발생한 상황:

총알 이동 속도: 1500cm/s
30fps 기준 프레임당 이동 거리: 50cm
적 큐브 두께 (scale 0.5): 50cm
→ 한 프레임에 적을 완전히 통과 → Overlap 이벤트 미발생
// Before: 터널링 발생
AddActorLocalOffset(Delta);

// After: bSweep=true로 이동 경로 상 충돌 감지
AddActorLocalOffset(Delta, true);  // bSweep=true

모바일 관점 메모: bSweep=true는 이동 경로를 물리 엔진에서 추적하므로 단순 위치 업데이트보다 연산 비용이 크다. 총알처럼 수가 많은 오브젝트에 남용하면 모바일에서 부담이 된다. 총알 수가 매우 많은 탄막 슈팅이라면 LineTrace 기반 충돌 처리가 더 효율적인 대안이다.

🔥 트러블슈팅

문제 1. Z값 불일치로 충돌 미발생

원인: Enemy 스폰 Z=0 고정, 총알 Z=플레이어 위치 기준으로 서로 달랐다. 탑뷰라 눈으로는 겹쳐 보이지만 3D 공간에서 콜리전 박스가 실제로 겹치지 않음.

해결: EnemySpawner에서 스폰 Z값을 GetPlayerPawn()->GetActorLocation().Z로 동기화.

문제 2. 터널링으로 Overlap 이벤트 미발생

원인: 총알 속도 1500cm/s, 30fps 기준 프레임당 50cm 이동. 적 큐브 두께도 50cm라 한 프레임에 완전히 통과.

해결: AddActorLocalOffset(Delta, true) bSweep=true로 이동 경로 상 충돌 감지.

문제 3. ObjectType 충돌 채널 미분리

원인: Enemy/Bullet 모두 기본 ECC_WorldDynamic으로 동일 채널. UE5 Chaos에서 같은 채널끼리 간헐적 감지 실패 가능.

해결: Enemy에 SetCollisionObjectType(ECC_Pawn) 추가로 채널 분리.

문제 4. 이중 처리 방지

원인: Overlap 이벤트 연속 발생 시 이미 반납된 Enemy/Bullet에 중복 ReturnToPool() 호출 가능.

해결: if (IsHidden()) return guard 추가.

💡 배운 것

• 탑뷰에서 눈으로 겹쳐 보여도 Z축이 다르면 충돌이 발생하지 않는다. 3D 공간에서 생각해야 한다.

• 터널링은 고속 오브젝트에서 반드시 고려해야 한다. bSweep=true가 해결책이지만 연산 비용이 있으므로 남용은 금물이다.

• 충돌 채널은 오브젝트 타입별로 명시적으로 분리해야 한다. 기본값에 의존하면 간헐적 버그가 발생한다.

• Pool 반납 시 이중 처리 방지 guard는 필수다. Overlap 이벤트는 연속 발생할 수 있다.

📌 다음 작업

[IronBird #5] 보스 페이즈 구현

3페이즈 패턴 전환 보스 구현. HP 비율에 따라 이동 패턴과 공격 패턴이 바뀌는 상태머신 설계.