Unity

Unity Design Pattern - Object Pool

CommitGuy 2024. 6. 3. 00:37

Object Pool 방식의 디자인 패턴은 비단 Unity에서만 사용하는 패턴은 아니지만 Unity에서도 유용하게 쓸 수 있는 디자인 패턴이다

 

Object Pooling은 많은 GameObject들을 생성하고 파괴하는데 CPU를 최적화 시키는 방법으로 GameObject를 다 사용하고 난 후 GameObject를 파괴시키지 않고 비활성화 시킨 후 pool로 되돌리는 방식이다.

 

이는 GC(Garbage Collector)로 인한 버벅거리는 현상을 줄여주는 효과가 있다.

 

1. 간단한 Object Pooling 예시

Simple Object Pool System Example

 

[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

- 생성자

UnityEngine.Pool Constructor

 

- Public 함수

UnityEngine.Pool Public Method

 

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