간략하게, 총알을 예시로
총알을 미리 Spawn 하여 배열(풀)에 등록하여
쏠땐 set location, visible, collison... 세팅하고
필요없어지면 반대로 set hidden... 등등 해주는것.
=> 배열에서 총갯수를 조정가능하고, 재사용하기때문에 새로운 총알을 Spawn 할 필요가 없음.
[언리얼 엔진 최적화] 오브젝트 풀링 (Object Pooling)
총알이 빗발치는 슈팅 게임, 화려한 이펙트가 터지는 액션 게임을 만들다 보면 피할 수 없는 고민이 있습니다. 바로 성능 저하와 끊김(Stutter) 현상입니다. 수많은 오브젝트를 실시간으로 생성(Spawn)하고 파괴(Destroy)하는 과정은 엔진에 상당한 부하를 주게 됩니다.
오늘은 이러한 문제를 해결하고 게임의 퍼포먼스를 한 단계 끌어올릴 수 있는 핵심 최적화 기법, **오브젝트 풀링(Object Pooling)**에 대해 A to Z로 자세히 알아보겠습니다.
1. 오브젝트 풀링이란 무엇이며, 왜 필수적인가요?
오브젝트 풀링이란, 간단히 말해 **'오브젝트 재활용 시스템'**입니다.
게임 플레이 중에 필요할 때마다 오브젝트를 새로 생성하고 파괴하는 대신, 게임 시작 시점에 필요한 만큼의 오브젝트를 미리 만들어 '풀(Pool)'이라는 거대한 저장소에 보관합니다. 그리고 실제 게임에서는 이 풀에서 오브젝트를 '대여'해서 사용하고, 사용이 끝나면 파괴하지 않고 다시 풀에 '반납'하여 재활용하는 방식입니다.
오브젝트 풀링을 사용해야 하는 이유
- 성능 부하 감소: Spawn과 Destroy는 매우 비용이 높은 작업입니다. 특히 Destroy는 가비지 컬렉션(Garbage Collection)을 유발하여 게임의 순간적인 멈춤 현상, 즉 '스터터'의 주범이 됩니다. 오브젝트 풀링은 이 두 가지 작업을 최소화하여 게임을 매우 부드럽게 만듭니다.
- 안정적인 메모리 관리: 게임 시작 시 필요한 메모리를 미리 할당해두기 때문에, 플레이 도중 메모리가 급격하게 변동하는 것을 막아줍니다. 이는 예기치 못한 크래시를 방지하고 안정성을 높여줍니다.
C++를 사용하면 월드 서브시스템, 메모리 예약 등 더 효율적인 구현이 가능하지만, 블루프린트만으로도 매우 강력하고 효과적인 오브젝트 풀링 시스템을 구축할 수 있습니다. 이 가이드에서는 블루프린트 기반의 구현 방법을 중심으로 설명합니다.
2. 블루프린트로 오브젝트 풀링 시스템 구축하기 [단계별 가이드]
이제 본격적으로 총알(Bullet)을 재활용하는 오브젝트 풀 시스템을 만들어 보겠습니다.
1단계: 오브젝트 풀 매니저(Object Pool Manager) 생성
매니저는 우리의 오브젝트 풀을 총괄하는 컨트롤 타워입니다.
- BP_ObjectPoolManager라는 이름으로 새로운 액터 블루프린트를 생성합니다.
- Begin Play 이벤트에서, 우리가 사용할 만큼의 총알 액터를 미리 생성하는 로직을 구현합니다. 예를 들어, 500개의 총알이 필요하다면 For Loop를 사용하여 500번 반복하면서 총알 액터를 스폰합니다.
- 생성된 총알 액터들은 Bullet Pool이라는 이름의 액터 배열(Array) 변수에 차곡차곡 저장합니다. 이 배열이 바로 우리의 '오브젝트 풀'이 됩니다.
- 초기 로딩 시 약간의 지연이 발생할 수 있으나, 이는 로딩 화면 등에서 처리하면 되므로 실제 게임 플레이에는 영향을 주지 않습니다.
2단계: 재사용될 기본 오브젝트(Base Bullet) 생성
실제 게임 월드에서 총알의 역할을 수행할 기본 액터입니다.
- BP_BaseBullet라는 이름으로 새로운 액터 블루프린트를 생성합니다. 이 액터는 발사체 컴포넌트(Projectile Movement Component)와 외형을 위한 스태틱 메시 컴포넌트(Static Mesh Component) 등을 가집니다.
- 이 액터의 핵심은 **'활성화(Activate)'**와 '비활성화(Deactivate)' 상태를 갖는 것입니다. 이를 위한 커스텀 이벤트를 각각 만들어 줍니다.
3단계: 활성화(Activate) / 비활성화(Deactivate) 로직 구현
- 비활성화 (Deactivate) 로직:
- 이 함수는 총알이 풀에 보관될 때의 상태를 정의합니다.
- Set Actor Hidden In Game 노드를 사용하여 액터를 투명하게 만듭니다.
- Set Actor Enable Collision 노드를 사용하여 모든 충돌 기능을 끕니다.
- 발사체 컴포넌트의 Gravity Scale을 0으로, Velocity를 (0,0,0)으로 만들어 움직임을 완전히 멈춥니다.
- 가장 중요: 비활성화 로직 마지막에는 BP_ObjectPoolManager에게 "나 이제 다시 쉬어도 돼"라고 알려주고, 매니저는 이 총알을 다시 Bullet Pool 배열에 추가하는 로직이 필요합니다.
- 활성화 (Activate) 로직:
- 이 함수는 풀에서 총알을 꺼내 사용할 때의 상태를 정의합니다.
- Set Actor Hidden In Game으로 다시 보이게 만듭니다.
- Set Actor Enable Collision으로 충돌 기능을 켭니다.
- 총알을 발사할 위치와 방향을 Set Actor Location and Rotation으로 설정합니다.
- 발사체의 속도, 데미지, 외형 등 필요한 모든 초기값을 설정합니다.
4단계: 구조체(Struct)를 활용한 깔끔한 데이터 전달
활성화 시 필요한 데이터(위치, 방향, 속도, 데미지, 메시 등)는 매우 많습니다. 이를 깔끔하게 전달하기 위해 **구조체(Structure)**를 사용합니다.
- STR_BulletInfo라는 이름의 구조체를 새로 만듭니다.
- 이 구조체 안에 총알 활성화에 필요한 모든 변수(Transform, Damage, Speed, Static Mesh 등)를 추가합니다.
- 이제 Activate 함수는 이 구조체 변수 하나만 입력으로 받으면 됩니다. 이렇게 하면 코드가 훨씬 깔끔해지고, 나중에 데이터 테이블과 연동하여 수십 종류의 총알을 손쉽게 관리하는 기반이 됩니다.
5단계: 오브젝트 회수 및 재사용 로직
이제 사용이 끝난 총알을 자동으로 풀에 반납하는 시스템을 만듭니다.
- BP_BaseBullet 액터에서 On Component Hit 또는 On Actor Begin Overlap 이벤트를 사용합니다.
- 총알이 다른 오브젝트와 부딪혔을 때, 스스로 Deactivate 함수를 호출하도록 연결합니다.
- Deactivate 함수 내부 로직에 따라, 이 총알은 투명해지고 움직임을 멈춘 뒤 Object Pool Manager의 Bullet Pool 배열로 돌아가게 됩니다.
이제 모든 준비가 끝났습니다. 캐릭터가 총을 쏠 때 Spawn Actor를 호출하는 대신, BP_ObjectPoolManager에서 비활성화된 총알을 하나 가져와 Activate 함수를 호출하기만 하면 됩니다.
3. 주요 고려사항 및 고급 팁
- 월드 킬 Z (World Kill Z): 맵 밖으로 떨어진 오브젝트는 엔진이 자동으로 파괴합니다. 이를 방지하려면 Deactivate 시 중력을 0으로 설정하여 추락을 막는 것이 중요합니다.
- 적절한 풀 크기 설정: 동시에 화면에 존재해야 하는 오브젝트의 최대 개수를 신중하게 예측하여 풀의 크기를 정해야 합니다. 풀이 너무 작으면 필요할 때 쓸 총알이 없는 'Pool Empty' 상황이 발생할 수 있습니다.
- 다양한 오브젝트 타입 관리: 여러 종류의 총알이나 이펙트가 필요하다면, 모든 로직을 담은 하나의 기본 클래스를 상속하여 사용하거나, 타입별로 여러 개의 풀을 만드는 전략을 사용할 수 있습니다.
오브젝트 풀링은 처음에는 조금 복잡해 보일 수 있지만, 한번 시스템을 구축해두면 어떤 프로젝트에서든 재사용 가능합니다.
'Unreal5 > 기본개념' 카테고리의 다른 글
| [언리얼5] DECLARE_(DYNAMIC)_MULTICAST_DELEGATE_TwoParams 차이 (0) | 2025.08.03 |
|---|---|
| [최적화] Render 관련 순서(Game, Render, Anim...) (4) | 2025.08.02 |
| [설계] BP vs C++ (2) | 2025.06.08 |
| [C++] 델리게이트 사용법 (2) | 2025.06.02 |
| [언리얼5] BP 아이콘 (0) | 2025.02.24 |