유니티
[유니티] 디자인 패턴 : 오브젝트 풀링 (Object Pooling)
디버그러
2025. 2. 15. 11:46
오브젝트 풀링
게임에서 오브젝트를 사용할 때마다 새로 생성(instantiate)하고,
삭제(Destroy)하면 성능 저하가 발생한다.
이를 해결하는 방법 중 하나가 오브젝트 풀링이다.
오브젝트 풀링은 오브젝트를 필요할 때마다 생성하는 것이 아니라,
미리 생성해 두고 필요할 때 가져와 사용하는 방식이다.
사용이 끝난 오브젝트는 삭제하지 않고 다시 풀로 반환하여 재사용할 수 있다.
이 패턴은 게임 플레이 전에 특정 순간에 필요한 모든 오브젝트를 미리 인스턴스화해야 한다.
풀은 로딩 화면과 같이 플레이어가 끊김 현상을 알아차리지 못할 적절한 시기에 활성화한다.
원리
① 초기화 단계
- 게임이 시작될 때 미리 여러 개의 오브젝트를 생성해서 풀에 저장
② 사용 단계
- 오브젝트가 필요할 때 새로 생성하는 대신 풀에서 하나를 꺼내 사용
③ 반환 단계
- 사용이 끝난 오브젝트는 삭제하지 않고 풀로 되돌려서 나중에 다시 사용
장점
1. 성능 최적화
- 새 오브젝트를 생성할 때 CPU가 메모리를 할당하고 해제하는 작업을 줄일 수 있어 CPU 부담이 적다.
2. 메모리 관리 최적화
- 오브젝트를 반복해서 생성하고 삭제할 때 발생하는 가비지 컬렉션(GC)을 최소화하여 게임이 부드럽게 실행
3. 프레임 저하 방지
- 게임 도중 많은 오브젝트 생성 (ex. 총알)으로 인한 CPU, 메모리 부담을 줄여 프레임 저하 방지
사용 용도
▶ 빠르게 생성 또는 삭제되는 오브젝트 (ex. 총알, 이펙트, 몬스터)
▶ 게임 내에서 반복적으로 사용되는 오브젝트 (ex. UI 요소, NPC, 파편 효과)
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool : SingleTon<ObjectPool>
{
private Dictionary<int, Queue<GameObject>> pooledObjects;
private Dictionary<GameObject, int> instancePrefabKey;
protected override void Awake()
{
base.Awake();
pooledObjects = new Dictionary<int, Queue<GameObject>>();
instancePrefabKey = new Dictionary<GameObject, int>();
}
/// <summary>
/// 오브젝트 풀을 초기화하여 미리 생성
/// </summary>
public void InitializeObject(GameObject prefab, Transform poolContainer, int amount)
{
int key = prefab.GetInstanceID();
if (!pooledObjects.ContainsKey(key))
{
pooledObjects[key] = new Queue<GameObject>();
}
for (int i = 0; i < amount; i++)
{
GameObject obj = Instantiate(prefab, poolContainer);
obj.SetActive(false);
pooledObjects[key].Enqueue(obj);
instancePrefabKey[obj] = key;
}
}
/// <summary>
/// 특정 오브젝트를 풀에서 가져오기
/// </summary>
public GameObject GetFromPool(GameObject prefab, Vector3 position)
{
int key = prefab.GetInstanceID();
if (pooledObjects.ContainsKey(key) && pooledObjects[key].Count > 0)
{
GameObject obj = pooledObjects[key].Dequeue();
obj.SetActive(true);
obj.transform.position = position;
return obj;
}
else
{
Debug.Log($"풀이 비어 있어 새로 생성합니다.");
// 풀이 비어있으면 새로운 오브젝트 생성
GameObject newFruit = Instantiate(prefab, transform);
newFruit.SetActive(true);
newFruit.transform.position = position;
instancePrefabKey[newFruit] = key;
return newFruit;
}
}
/// <summary>
/// 사용이 끝난 오브젝트를 다시 풀에 반환
/// </summary>
public void ReturnToPool(GameObject prefab)
{
if (!instancePrefabKey.ContainsKey(prefab))
{
Debug.Log($"[Object Pool] {prefab.name} 반환할 수 없습니다.");
return;
}
int key = instancePrefabKey[prefab];
prefab.SetActive(false);
pooledObjects[key].Enqueue(prefab);
}
}