커스텀 오브젝트 풀링
- 모듈화 및 풀링을 편리하게 사용하는 것이 목적
- 자체적으로 Dictonary를 구성
- 인터페이스를 통한 모듈화
- 필요 시, IMyObjectPool을 제네릭으로 구성해도 좋을 듯 함
public class MyObjectPool : MonoBehaviour, IMyObjectPool
{
[SerializeField] private int _initialSize = 1; // 초기 풀 크기
[SerializeField] private Transform _folder;
private Dictionary<string, Queue<GameObject>> _poolDict = new();
private Dictionary<string, List<GameObject>> _activeItemDict = new();
private const string PRIMARY_KEY = "Primary";
private void Awake()
{
if (_folder == null)
_folder = transform;
}
public void SetPool(GameObject prefab, string key = PRIMARY_KEY)
{
if (!_poolDict.ContainsKey(key))
{
_poolDict[key] = new();
_activeItemDict[key] = new();
}
// 초기 풀 크기만큼 오브젝트 생성하여 풀에 저장
for (int i = 0; i < _initialSize; i++)
CreatePooledItem(prefab, key);
}
// 오브젝트 생성 후 풀에 추가
private GameObject CreatePooledItem(GameObject prefab, string key = PRIMARY_KEY)
{
Queue<GameObject> pool = _poolDict[key];
GameObject obj = Instantiate(prefab, _folder, true);
obj.gameObject.SetActive(false);
pool.Enqueue(obj);
return obj;
}
// 오브젝트를 풀에서 가져오기
public GameObject Get(GameObject prefab, string key = PRIMARY_KEY)
{
Queue<GameObject> pool = _poolDict[key];
if (pool.Count == 0)
{
CreatePooledItem(prefab, key);
}
GameObject pooledObject = pool.Dequeue();
pooledObject.gameObject.SetActive(true);
_activeItemDict[key].Add(pooledObject);
return pooledObject;
}
// 오브젝트를 풀에 반환하기
public void Release(GameObject pooledObject, string key = PRIMARY_KEY)
{
if (!IsValidKey(key))
return;
pooledObject.gameObject.SetActive(false);
_poolDict[key].Enqueue(pooledObject);
_activeItemDict[key].Remove(pooledObject);
}
// 모든 오브젝트를 풀에 반환
public void Clear(string key = PRIMARY_KEY)
{
if (!IsValidKey(key))
return;
// 리스트의 뒤에서부터 요소를 하나씩 Release하고 제거
for (int i = _activeItemDict[key].Count - 1; i >= 0; i--)
Release(_activeItemDict[key][i], key);
_activeItemDict[key].Clear();
}
// 모든 풀을 반환
public void ClearAll()
{
foreach (var key in _poolDict.Keys)
Clear(key);
}
// 모든 오브젝트를 제거
public void DestroyAll(string key = PRIMARY_KEY)
{
if (!IsValidKey(key))
return;
_activeItemDict[key].ForEach(obj => Destroy(obj));
_activeItemDict[key].Clear();
while (_poolDict[key].Count > 0)
{
GameObject obj = _poolDict[key].Dequeue();
if (obj != null)
{
Destroy(obj);
}
}
}
// 풀링된 오브젝트 개수 반환
public int GetCount(string key = PRIMARY_KEY)
{
if (!IsValidKey(key))
return 0;
return _poolDict[key].Count + _activeItemDict[key].Count;
}
// 활성화된 오브젝트 개수 반환
public int GetActiveCount(string key = PRIMARY_KEY)
{
if (!IsValidKey(key))
return 0;
return _activeItemDict[key].Count;
}
// 유효 키 검증
private bool IsValidKey(string key)
{
if (!_poolDict.ContainsKey(key) || !_activeItemDict.ContainsKey(key))
{
Debug.LogError($"키({key})가 존재하지 않습니다.");
return false;
}
else return true;
}
}
public interface IMyObjectPool
{
public void SetPool(GameObject prefab, string key);
public GameObject Get(GameObject prefab, string key);
public void Release(GameObject pooledObject, string key);
public void Clear(string key);
public void ClearAll();
public void DestroyAll(string key);
public int GetCount(string key);
public int GetActiveCount(string key);
}
샘플
- 풀링이 필요한 곳 어디서든 쉽게 접근 가능
- 생성, 해제, 초기화, 제거 등 주요 기능들 제공
[RequireComponent(typeof(IMyObjectPool))]
public class Sample_ObjectPool : MonoBehaviour
{
[SerializeField] private GameObject _itemPrefab;
private IMyObjectPool _pool;
private void Awake()
{
_pool = transform.GetComponent<IMyObjectPool>();
}
private void Start() => Init();
private void Init()
{
_pool.SetPool(_itemPrefab.gameObject, _itemPrefab.name); // 오브젝트 & Key
}
public void Get() => _pool.Get(_itemPrefab.gameObject, (_itemPrefab.name);
public void Release(GameObject item) => _pool.Release(item, _itemPrefab.name);
public void Clear() => _pool.Clear(_itemPrefab.name);
public void DestroyAll() => _pool.DestroyAll(_itemPrefab.name);