반응형

unity ObjectPool API 
(Version:2021)

이 글을 읽기 전에 자신이 쓰는 유니티 버전을 확인해보라

유니티가 지원하는 objectpool은 2021년 이후 버전부터 추가되었다.

 

1.ObjectPool 이란?

2.Unity ObjectPool

3. 글쓴이가 응용한 방식


 

1.ObjectPool

우리가 사용 하려는 오브젝트 풀은 간단하게 설명하면

오브젝트를 담아두고 재활용하기 위한 디자인 패턴이다.

 

어째서 재활용을 해야할까? 라는 생각을 할 수 있다.

이 물음에 답하자면

 

 생성과 파괴보다 재사용이 빠르고 가볍다.

 

생각해보라 우리가 오브젝트를 "생성" 한다는것은 그냥 그 자리에 나오는 것이 아닌

생성-> class에 맞는 데이터 크기만큼 메모리 할당-> 재생성시 재 대입해줘야 하는 데이터 값

그리고 파괴할 때 가비지 메모리 제거 등 많은 작업이 필요하다.

 

이러한 작업이 적을 경우에는 크게 상관이 없을 수 있지만 많은 데이터를 생성과 제거를 반복한다 생각하면 끔찍하다.

 

이러한 작업을 적게 할 방법이 ObjectPool이라는 방식이다.

 

ObjectPool은 오브젝트를 (생성-> 사용-> 제거)반복 에서  생성-> 사용-> (보관->재사용)반복 ->제거 식으로 사용하여

메모리를 아끼는 방식이다.


 

2.Unity ObjectPool

 

 

 

 

 

 

 

 

 

 

 

Pool 관련 클래스는 몇 가지가 있지만. 대부분 비슷한 사용법을 가진다.

그중에서 ObjectPool <T0>을 사용할 것이다.

namespace UnityEngine.Pool
{
    //
    // 요약:
    //     A stack based Pool.IObjectPool_1.
    public class ObjectPool<T> : IDisposable, IObjectPool<T> where T : class
    {
        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);

        public int CountAll { get; }
        public int CountActive { get; }
        public int CountInactive { get; }

        public void Clear();
        public void Dispose();
        public T Get();
        public PooledObject<T> Get(out T v);
        public void Release(T element);
    }
}

 

이게 내부 구성과 ObjectPool의 매개변수다.

ObjectPool(생성할 때 함수, 사용할 때 함수, 사용 끝났을 때 함수, 제거 함수, 충돌 체크, 기본적으로 생성해둘 양, 최대 양)

으로 생각하면 쉽다


 

총과 총알로 설명하겠다.

먼저 총알 prefab에 IobjectPool <GameObject> pool {get;set;}을 생 성해준 뒤 Update에 날아가는 코드를 작성해준다.

using UnityEngine.Pool;

public class Bullet : MonoBehaviour
{
    public IObjectPool<GameObject> Pool { get; set; }
    
    public virtual void ReleaseObject() => Pool.Release(this.gameObject);
    public virtual void OnTakeFromPool(GameObject obj) { }
    public virtual void OnReturnedToPool(GameObject obj) => obj.SetActive(false);
    public virtual void OnDestroyPoolObject(GameObject obj) => Destroy(obj);


}


public class Bullet_1 : BulletBase
{
    public float speed = 7;
    private void Update()
    {
        if (transform.position.y >= 10)
        {
            ReleaseObject();
        }
        transform.Translate(speed * Time.deltaTime * Vector3.up);
    }

    public override void OnTakeFromPool(GameObject obj)
    {
        obj.SetActive(true);
    }
}

 

그 후 BulletSpawner.cs를 만들어주어  ObjectPool <Gameobject>  Pool를 만들어준 뒤

ObjectPool내부에 생성할 Bullet의 함수들을 넣어둔다.

그리고 bullet 생성 함수에서 bullet 내부의 IObjcetPool pool에 spawn의  Pool을 넣어준다.

( bullet 내부에서 spawn의  Pool을  사용해야 하기 때문에) 

 

그리고 특정 상황마다 BulletSpawner 에서  Pool.Get();을 해준다.

 Pool.Get()은  Pool 내부 함수로 자동으로 현재 재사용 가능한 오브젝트가 있는지 없는지를 판단하여

생성해준다.

public class BulletSpawner : MonoBehaviour
{
 
    public BulletBase poolablePrefab;
    public IObjectPool<GameObject> Pool { get; private set; }
    

    public void Init()
    {
        
              Pool = new ObjectPool<GameObject>(
              delegate {
        // 오브젝트 생성함수
        // 오브젝트 생성
        GameObject obj = Instantiate(poolablePrefab.gameObject);
        //생성한 오브젝트의 IObjectPool Pool 에 Spawner의 ObjectPool을 넣어준다.
        obj.GetComponent<BulletBase>().Pool = Pool;
            return obj;
        },
        // Bullet에 구현된 사용할 함수들 입력
        poolablePrefab.OnTakeFromPool, poolablePrefab.OnReturnedToPool,
        poolablePrefab.OnDestroyPoolObject,
        true, poolablePrefab.defaultCapacity, 50));

    }
    
    public void Shoot()
    {
       var a= poolablePrefab.Pool.Get();
        a.transform.position = this.gameObject.transform.position;
    }
    private void Start()
    {
        Init();
        InvokeRepeating("Shoot", 0f,5f);

    }
}

 

 

 

 

 

아래는 유니티에서 제공한 예제다

더보기
using UnityEngine.Pool;

// This component returns the particle system to the pool when the OnParticleSystemStopped event is received.
[RequireComponent(typeof(ParticleSystem))]
public class ReturnToPool : MonoBehaviour
{
    public ParticleSystem system;
    public IObjectPool<ParticleSystem> pool;

    void Start()
    {
        system = GetComponent<ParticleSystem>();
        var main = system.main;
        main.stopAction = ParticleSystemStopAction.Callback;
    }

    void OnParticleSystemStopped()
    {
        // Return to the pool
        pool.Release(system);
    }
}

// This example spans a random number of ParticleSystems using a pool so that old systems can be reused.
public class PoolExample : MonoBehaviour
{
    public enum PoolType
    {
        Stack,
        LinkedList
    }

    public PoolType poolType;

    // Collection checks will throw errors if we try to release an item that is already in the pool.
    public bool collectionChecks = true;
    public int maxPoolSize = 10;

    IObjectPool<ParticleSystem> m_Pool;

    public IObjectPool<ParticleSystem> Pool
    {
        get
        {
            if (m_Pool == null)
            {
                if (poolType == PoolType.Stack)
                    m_Pool = new ObjectPool<ParticleSystem>(CreatePooledItem, OnTakeFromPool, OnReturnedToPool, OnDestroyPoolObject, collectionChecks, 10, maxPoolSize);
                else
                    m_Pool = new LinkedPool<ParticleSystem>(CreatePooledItem, OnTakeFromPool, OnReturnedToPool, OnDestroyPoolObject, collectionChecks, maxPoolSize);
            }
            return m_Pool;
        }
    }

    ParticleSystem CreatePooledItem()
    {
        var go = new GameObject("Pooled Particle System");
        var ps = go.AddComponent<ParticleSystem>();
        ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);

        var main = ps.main;
        main.duration = 1;
        main.startLifetime = 1;
        main.loop = false;

        // This is used to return ParticleSystems to the pool when they have stopped.
        var returnToPool = go.AddComponent<ReturnToPool>();
        returnToPool.pool = Pool;

        return ps;
    }

    // Called when an item is returned to the pool using Release
    void OnReturnedToPool(ParticleSystem system)
    {
        system.gameObject.SetActive(false);
    }

    // Called when an item is taken from the pool using Get
    void OnTakeFromPool(ParticleSystem system)
    {
        system.gameObject.SetActive(true);
    }

    // If the pool capacity is reached then any items returned will be destroyed.
    // We can control what the destroy behavior does, here we destroy the GameObject.
    void OnDestroyPoolObject(ParticleSystem system)
    {
        Destroy(system.gameObject);
    }

    void OnGUI()
    {
        GUILayout.Label("Pool size: " + Pool.CountInactive);
        if (GUILayout.Button("Create Particles"))
        {
            var amount = Random.Range(1, 10);
            for (int i = 0; i < amount; ++i)
            {
                var ps = Pool.Get();
                ps.transform.position = Random.insideUnitSphere * 10;
                ps.Play();
            }
        }
    }

 

 

3.

내가 구현한 방식이다.

 

 

Spawn에도 IObjectPool로 PoolManager에 BulletPool을 받아서 사용한다.
Manager 에서 새로운 오브젝트가 아닐경우 서로 공유해서 사용하게 한다.

 

글쓴이는 싱글톤으로 PoolManager을 만들어서 같은 총알은 서로 공유하여 사용하게 만들어보았다.

이런식으로 만들경우 Spawn컴퍼넌트를 오브젝트에 붙여주고 원하는 BulletPrefab을 넣어주면 완성이다.

 

참고자료 : https://docs.unity3d.com/ScriptReference/Pool.ObjectPool_1.html 

 

Unity - Scripting API: ObjectPool<T0>

Object Pooling is a way to optimize your projects and lower the burden that is placed on the CPU when having to rapidly create and destroy new objects. It is a good practice and design pattern to keep in mind to help relieve the processing power of the CPU

docs.unity3d.com

외 여러 영상들과 블로그

'엔진 > 유니티' 카테고리의 다른 글

Unity Spline 기능 추가!  (0) 2023.06.01
Unity Simulator  (0) 2023.01.20
unity Redis  (0) 2022.07.13
unity _Prefab Variants  (0) 2022.03.17
Unity Inspector 창 정리  (2) 2022.02.24

+ Recent posts