Unity - Object Pool

2024. 5. 23. 20:45Unity/Unity 학습정리

  게임을 진행하면서 많은 오브젝트들을 생성하고 파괴하는 과정을 거친다. 특히 반복적으로 이 행동을 해야하는 경우가 있는데 발사체를 쏠때 발사체를 여러개 생성하고 또 범위 밖으로 나가면 생성되어있던것을 파괴해야하는 경우가 있다.  오브젝트를 만들때 Instantiate(), 파괴할때 Destroy()를 자주 사용하는데 이 메서드들은 비용을 굉장히 많이 먹는 작업이다. 메모리를 새로 할당하고 초기화하는 과정 및 파괴 후 가비지 컬렉션같은 과정들을 거친다.

  위 과정을 더 효율적으로 만들어주는것이 Object Pool이다. 과거버전 유니티에서는 오브젝트 풀을 공식적으로 지원하지 않아, 개발자가 직접 코드로 구현하여 사용했다고 한다. 지금은 공식적으로 지원하고 있고 직접 만든 오브젝트 풀과 유니티의 오브젝트 풀 둘 다 알아보았다.

 

목차

  1. Object Pool 원리
  2. 직접구현

     


    Object Pooling 원리

      생성과 파괴가 빈번하게 발생하는 오브젝트에 대해서 매번 Instantiate, Destroy를 할 수 없다. 이는 게임 성능에 영향을 미친다. 그래서 미리 다수의 오브젝트들을 만들어놓고 필요할 때 가져다쓰고, 사용이 끝나면 반납하는 방식으로 구현된다.

    • 생성(Instantiate)와 소멸(Destroy)이라는 큰 비용을 드는 작업을 최소화하여 성능향상을 할 수 있게 한다.
    • 특히 빈번하게 생성되고 소멸되는 오브젝트에 대해서 유용하다.
    • 개발자가 스스로 오브젝트의 양을 예측해서 사용해야한다. 실제로는 20~30개 정도 사용되는데 오브젝트를 미리 몇 백개나, 몇 천개를 만들어 넣어놓으면 메모리 사용이 많다. 이는 결국 성능 저하로 이어진다. 그래서 오브젝트 풀의 크기를 적절하게 조절해야 한다.

     

    직접구현

     

    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject prefab;
        public int size;
    }

     

    필드

    • tag : 프리팹의 태그
    • prefab : Object Pool을 이용해 미리 생성해둘 오브젝트
    • size : 오브젝트 풀의 크기

    해당 pool 클래스를 생성하고 게임 오브젝트를 Pool클래스로 만든다.

     

    public List<Pool> pools = new List<Pool>();
    public Dictionary<string, Queue<GameObject>> PoolDictionary;

    리스트

    리스트에 들어가있는 pool들은 모두 오브젝트 풀링의 대상 객체들이 된다. 나중에 foreach를 이용해 모두 탐색하면서 PoolDcitionary에 등록한다.

     

    딕셔너리

    • key : pool.tag 오브젝트풀의 태그를 키값으로 둔다.
    • Value : pool에 들어있는 GameObject 프리팹으로 생성한 게임오브젝트들의 Queue를 Value로 넣어준다.

    이렇게 구현해놓으면 우린 Key값(tag)로 오브젝트 풀의 Queue를 사용할 수 있다. 

    private void Awake()
    {
        PoolDictionary = new Dictionary<string, Queue<GameObject>>();
    
        foreach (var pool in pools)
        {
            Queue<GameObject> queue = new Queue<GameObject>();
            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.prefab,transform);
                obj.SetActive(false);
                queue.Enqueue(obj);
            }
    
            PoolDictionary.Add(pool.tag, queue);
        }   
    }
    • 리스트안에있는 pool후보들을 탐색한다.
    • 해당 pool후보 size만큼 Instantiate(생성)하고 비활성화를 시켜 queue에 집어넣어준다.
    • 해당 pool 태그와 queue를 딕셔너리에 등록해주면서 Object Pool을 완성시킨다.
    public GameObject SpawnFromPool(string tag)
    {
        if (!PoolDictionary.ContainsKey(tag))
        {
            return null;
        }
    
        GameObject obj = PoolDictionary[tag].Dequeue();
        PoolDictionary[tag].Enqueue(obj);
    
        obj.SetActive(true);
        return obj;
    }

    사용방법

     

    private void CrateProjectile(RangedAttackSO rangedAttackSO, float angle)
    {
        GameObject obj = GameManager.Instance.ObjectPool.SpawnFromPool(rangedAttackSO.bulletNameTag);
        obj.transform.position = projectileSpawnPosition.position;
        ProjectileController attackController = obj.GetComponent<ProjectileController>();
        attackController.InitializeAttack(RoatateVector2(aimDirection,angle),rangedAttackSO);
    }

    실제로 SpawnFromPool을 하여 오브젝트를 활성화 시키고 해당 오브젝트의 위치를 지정하고 발사까지 하는 코드이다.

     

     

    'Unity > Unity 학습정리' 카테고리의 다른 글

    Unity - 슬라이더바로 사운드 조절하기  (1) 2024.06.05
    Unity - Scriptable Object  (0) 2024.05.27
    Unity - 싱글톤 패턴  (0) 2024.05.21
    Unity - Vector2.Dot으로 시야각 구하기  (0) 2024.05.17
    Unity - 각도 구하기  (0) 2024.05.16