Object Pool 방식의 디자인 패턴은 비단 Unity에서만 사용하는 패턴은 아니지만 Unity에서도 유용하게 쓸 수 있는 디자인 패턴이다
Object Pooling은 많은 GameObject들을 생성하고 파괴하는데 CPU를 최적화 시키는 방법으로 GameObject를 다 사용하고 난 후 GameObject를 파괴시키지 않고 비활성화 시킨 후 pool로 되돌리는 방식이다.
이는 GC(Garbage Collector)로 인한 버벅거리는 현상을 줄여주는 효과가 있다.
1. 간단한 Object Pooling 예시
[ObjectPool.cs]
public class ObjectPool : MonoBehaviour
{
[SerializeField] private uint initPoolSize;
[SerializeField] private PooledObject objectToPool;
// Store the pooled objects in a collection
private Stack<PooledObject> stack;
private void Start()
{
SetupPool();
}
private void SetupPool()
{
stack = new Stack<PooledObject>();
PooledObject instance = null;
for (int i = 0; i < initPoolSize; i++)
{
instance = Instantiate(objectToPool);
instance.Pool = this;
instance.gameObject.SetActive(false);
stack.Push(instance);
}
}
public PooledObject GetPooledObject()
{
// if the pool is not large enough, instantiate a new PooledObjects
if (stack.Count == 0)
{
PooledObject newInstance = Instantiate(objectToPool);
newInstance.Pool = this;
return newInstance;
}
// otherwise, just grab the next one from the stack
PooledObject nextInstance = stack.Pop();
nextInstance.gameObject.SetActive(true);
return nextInstance;
}
public void ReturnToPool(PooledObject pooledObject)
{
stack.Push(pooledObject);
pooledObject.gameObject.SetActive(false);
}
}
[PooledObject.cs]
public class PooledObject : MonoBehaviour
{
private ObjectPool pool;
public ObjectPool Pool { get => pool; set => pool = value; }
public void Release()
{
pool.ReturnToPool(this);
}
}
ObjectPool.cs 설명
- 처음에 PooledObject를 설정한 값(initPoolSize) 만큼 만들어 stack에 저장
- PooledObject가 필요할때마다 stack에서 꺼내어 사용(GetPooledObject 함수), stack에 초기 설정 값(initPoolSize)보다 더 많은 PooledObject가 필요하게 되면 새로이 생성하여 stack에 저장
- PooledObject의 사용이 끝나면 다시 stack에 저장(ReturnToPool() 함수)
2. Built-in Object Pool System
위에선 커스텀하게 Object Pooling System을 만들었지만 굳이 만들 필요 없이 Unity 2021 버전 이후부터는 object pooling system이 내장되어 있다. ==> UnityEngine.Pool
UnityEngine.Pool API는 Stack 기반의 object pool 패턴으로 object들을 tracking 해준다.
UnityEngine.Pool API
- 생성자
- Public 함수
UnityEngine.Pool - 사용예시
public class RevisedGun : MonoBehaviour
{
[SerializeField] private Transform _parentTransform;
[SerializeField] private RevisedProjectile projectilePrefab;
// Stack-based ObjectPool
private IObjectPool<RevisedProjectile> objectPool;
// throw an exception if we try to return an existing item, already in the pool
[SerializeField] private bool collectionCheck = true;
// extra options to control the pool capacity and maximum size
[SerializeField] private int defaultCapacity = 20;
[SerializeField] private int maxSize = 100;
private void Awake()
{
objectPool = new ObjectPool<RevisedProjectile>(CreateProjectile, OnGetFromPool, OnReleaseToPool, OnDestroyPooledObject, collectionCheck, defaultCapacity, maxSize);
}
private void Start()
{
// 미리 만들어 두기
for (int i = 0; i < 5; i++)
{
RevisedProjectile projectile = Instantiate(projectilePrefab);
projectile.ObjectPool = objectPool;
objectPool.Release(projectile);
}
}
public void Update()
{
if (Input.GetMouseButtonDown(0))
{
RevisedProjectile obj = objectPool.Get();
obj.transform.parent = _parentTransform;
}
}
// invoked when creating an item to populate the object pool
private RevisedProjectile CreateProjectile()
{
Debug.Log("CreateProjectile");
RevisedProjectile projectileInstance = Instantiate(projectilePrefab);
projectileInstance.ObjectPool = objectPool;
return projectileInstance;
}
// invoked when returning an item to the object pool
private void OnReleaseToPool(RevisedProjectile pooledObject)
{
Debug.Log("OnReleaseToPool");
pooledObject.gameObject.SetActive(false);
}
// invoked when retrieving the next item from the object pool
private void OnGetFromPool(RevisedProjectile pooledObject)
{
Debug.Log("OnGetFromPool");
pooledObject.gameObject.SetActive(true);
}
// invoked when we exceed the maximum number of pooled items (i.e. destroy the pooled object)
private void OnDestroyPooledObject(RevisedProjectile pooledObject)
{
Debug.Log("OnDestroyPooledObject");
Destroy(pooledObject.gameObject);
}
}
public class RevisedProjectile : MonoBehaviour
{
private IObjectPool<RevisedProjectile> objectPool;
// public property to give the projectile a reference to its Object Pool
public IObjectPool<RevisedProjectile> ObjectPool { set => objectPool = value; }
public void OnDisable()
{
objectPool.Release(this);
}
}
- RevisedGun.cs Script를 보면 Awake() 함수에서 new ObjectPool로 ObjectPool을 생성
- defaultCapacity는 pool 이 만들어질때 초기 pool사이즈 값
- maxSize는 pool의 최대사이즈를 설정한다
- CreateProjectile, OnReleaseToPool, OnGetFromPool, OnDestroyPooledObject는 pool의 상태에 따라 호출되는 콜백함수
- CreateProjectile : Pool에 Object가 없어 새로이 생성해야 할 때 호출되는 함수
- OnReleaseToPool : Object가 다시 Pool에 되돌아올때 호출되는 함수
- OnGetFromPool : Object가 Pool에서 나올때 호출되는 함수
- OnDestroyPooledObject : Pool의 maxsize를 넘게되면 호출되는 함수
RevisedGun.cs는 처음에 5개의 Object를 생성해 Pool에 넣고 그 후 마우스 클릭시 지정한 transform의 아래에 하나씩 생성되게 만든 script
'Unity' 카테고리의 다른 글
[Unity] Animator Component의 Root Motion 이란? (0) | 2024.07.10 |
---|---|
Unity 3D Object Drag & Drop (0) | 2024.06.04 |
유니티 3D Model 파괴 효과 만들기 (0) | 2024.02.26 |
Unity Android Plugin 만들기 (2) | 2024.01.28 |
Mac에서의 classes.jar 위치 (0) | 2021.10.31 |