메모리 할당 최소화 : 오브젝트를 매번 생성하고 파괴하면 메모리 할당과 해제가 빈번하게 발생하여 성능이 저하 됨. 오브젝트 풀링은 이러한 문제를 해결하기 위해 미리 필요한 수의 오브젝트를 생성해둠
재사용(Reusing) : 한 번 생성된 오브젝트는 풀 안에서 재사용됨. 새 오브젝트를 만들지 않고, 사용이 끝난 오브젝트를 다시 활용하므로 성능을 크게 향상시킴
풀 관리 : 풀에서 오브젝트를 필요로 할 때는 사용 가능한 오브젝트를 꺼내 사용하고, 사용이 끝나면 다시 풀에 반환함. 이 과정을 통해 효율적인 메모리 관리를 유지할 수 있음
유니티의 오브젝트 풀링에서 사용하는 자료구조는 특정한 요구 사항에 맞춰 선택할 수 있으며, 특정 자료구조로 고정되어 있지는 않음
리스트(List) : 간단하고 직관적인 자료구조로, 오브젝트를 순차적으로 관리하기에 좋음. 삽입과 제거가 빠르지 않지만, 적은 수의 오브젝트를 관리할 때는 적합함
큐(Queue) : 선입선출(FIFO) 방식으로 오브젝트를 재사용할 때 유용함. 오브젝트가 필요할 때 앞에서 꺼내고, 사용이 끝난 오브젝트는 다시 뒤로 넣는 방식
스택(Stack) : 후입선출(LIFO) 방식으로 사용됨. 가장 최근에 사용한 오브젝트를 다시 사용하고 싶은 경우 적합
딕셔너리(Dictionary) or 해시맵(HashMap) : 특정 키를 기준으로 오브젝트에 접근해야 할 때 유용함. 오브젝트 풀링에서 오브젝트의 종류나 상태에 따라 구분해야 할 때 유용할 수 있음
Object Type (오브젝트 타입)
Initial Pool Size (초기 풀 크기)
Max Pool Size (최대 풀 크기)
Auto Expand (자동 확장)
Recycle Objects (오브젝트 재활용)
Factory Method (팩토리 메소드)
유니티에서 제공하는 객체 풀링 시스템을 다루는 네임스페이스
Object Pooling (객체 풀링)
자주 생성되고 파괴되는 객체를 미리 생성하여 풀에 저장해 두었다가 필요할 때 다시 사용함. 이렇게 하면 매번 객체를 새로 생성하고 메모리를 할당하는 비용을 줄일 수 있음
Generic ObjectPool 클래스
일반적인 풀링 기능을 제공. 풀에서 객체를 빌리고, 사용이 끝난 객체를 다시 반환하는 구조를 제공함
Collection Pooling
리스트나 딕셔너리 같은 컬렉션 객체들을 풀링하여, 큰 리스트를 반복적으로 생성하거나 파괴하는 상황에서 메모리 사용을 최적화함
// CollectionPool을 사용하여 List<GameObject>를 필요할 때마다 풀에서 가져와 사용한 뒤, 다시 풀로 반환하는 방식
using UnityEngine;
using UnityEngine.Pool;
using System.Collections.Generic;
public class CollectionPoolingExample : MonoBehaviour
{
public GameObject prefab;
void Start()
{
// 오브젝트를 생성 및 관리할 리스트를 풀에서 가져옴
List<GameObject> pooledObjects = CollectionPool<List<GameObject>, GameObject>.Get();
// 리스트에 오브젝트 생성 후 추가
for (int i = 0; i < 10; i++)
{
GameObject obj = Instantiate(prefab);
obj.transform.position = new Vector3(i * 2.0f, 0, 0); // 오브젝트 위치 설정
pooledObjects.Add(obj);
}
// 풀에서 가져온 리스트의 오브젝트 사용 (예: 위치 업데이트)
foreach (var obj in pooledObjects)
{
obj.transform.position += Vector3.up;
}
// 리스트 사용이 끝나면 풀에 반환하여 메모리 해제
CollectionPool<List<GameObject>, GameObject>.Release(pooledObjects);
}
}
// PooledObject를 사용하여 적 캐릭터를 자동으로 풀에 반환하는 방법
using UnityEngine;
using UnityEngine.Pool;
public class Enemy : MonoBehaviour
{
public void OnSpawn()
{
// 적이 활성화될 때 필요한 초기화 코드
Debug.Log("Enemy Spawned");
}
public void OnDespawn()
{
// 적이 비활성화되거나 풀로 돌아갈 때 필요한 정리 코드
Debug.Log("Enemy Despawned");
}
}
public class GameManager : MonoBehaviour
{
// ObjectPool을 선언하여 적 객체 풀링 관리
private ObjectPool<Enemy> enemyPool;
public Enemy enemyPrefab; // 적 프리팹 연결
void Start()
{
enemyPool = new ObjectPool<Enemy>(
createFunc: () => Instantiate(enemyPrefab), // 생성 함수
actionOnGet: (enemy) => enemy.OnSpawn(), // 풀에서 가져올 때 실행할 작업
actionOnRelease: (enemy) => enemy.OnDespawn(),// 풀로 반환할 때 실행할 작업
actionOnDestroy: (enemy) => Destroy(enemy) // 객체 파괴할 때 작업
);
}
public void SpawnEnemy(Vector3 position)
{
// PooledObject 사용하여 자동으로 풀에 반환되도록 함
using (var pooledEnemy = enemyPool.Get(out var enemy))
{
enemy.transform.position = position;
enemy.gameObject.SetActive(true);
// 적이 특정 시간 후 비활성화되며 자동으로 풀로 반환됨
Invoke(nameof(DeactivateEnemy), 3.0f);
}
}
// 적을 비활성화하는 메서드
private void DeactivateEnemy()
{
// 이 메서드에서는 적을 비활성화만 해도 자동으로 풀에 반환됨
}
}
Unity에서 제공하는 오브젝트 풀링 인터페이스
오브젝트 풀을 관리하는 데 필요한 기본적인 기능들을 정의한 인터페이스
이를 통해 오브젝트 풀링 시스템을 구현하거나 커스터마이징할 수 있음
주요 기능은 풀의 오브젝트를 요청하고 반환하는 과정에 대한 규칙을 설정하는 것
IObjectPool 인터페이스는 풀링 시스템을 표준화하여 다양한 종류의 오브젝트 풀링에 동일한 방식으로 접근할 수 있게 해줌
Get()
Release(T element)
Clear()
CountInactive
public class ObjectPool<T> : IDisposable, IObjectPool<T> where T : class
{
internal readonly List<T> m_List;
private readonly Func<T> m_CreateFunc;
private readonly Action<T> m_ActionOnGet;
private readonly Action<T> m_ActionOnRelease;
private readonly Action<T> m_ActionOnDestroy;
private readonly int m_MaxSize;
internal bool m_CollectionCheck;
public int CountAll { get; private set; }
public int CountActive => CountAll - CountInactive;
public int CountInactive => m_List.Count;
public ObjectPool(Func<T> createFunc, Action<T> actionOnGet = null, Action<T> actionOnRelease = null, Action<T> actionOnDestroy = null, bool collectionCheck = true, int defaultCapacity = 10, int maxSize = 10000)
{
if (createFunc == null)
{
throw new ArgumentNullException("createFunc");
}
if (maxSize <= 0)
{
throw new ArgumentException("Max Size must be greater than 0", "maxSize");
}
m_List = new List<T>(defaultCapacity);
m_CreateFunc = createFunc;
m_MaxSize = maxSize;
m_ActionOnGet = actionOnGet;
m_ActionOnRelease = actionOnRelease;
m_ActionOnDestroy = actionOnDestroy;
m_CollectionCheck = collectionCheck;
}
public T Get()
{
T val;
if (m_List.Count == 0)
{
val = m_CreateFunc();
CountAll++;
}
else
{
int index = m_List.Count - 1;
val = m_List[index];
m_List.RemoveAt(index);
}
m_ActionOnGet?.Invoke(val);
return val;
}
public PooledObject<T> Get(out T v)
{
return new PooledObject<T>(v = Get(), this);
}
public void Release(T element)
{
if (m_CollectionCheck && m_List.Count > 0)
{
for (int i = 0; i < m_List.Count; i++)
{
if (element == m_List[i])
{
throw new InvalidOperationException("Trying to release an object that has already been released to the pool.");
}
}
}
m_ActionOnRelease?.Invoke(element); // 이 이벤트와 연결된 객체의 Relase 관련 함수 실행
if (CountInactive < m_MaxSize)
{
m_List.Add(element);
}
else
{
m_ActionOnDestroy?.Invoke(element); // 이 이벤트와 연결된 객체의 Destroy 관련 함수 실행
}
}
public void Clear()
{
if (m_ActionOnDestroy != null)
{
foreach (T item in m_List)
{
m_ActionOnDestroy(item);
}
}
m_List.Clear();
CountAll = 0;
}
public void Dispose()
{
Clear();
}
}