스파르타 게임개발종합반(Unity)/TIL - 본캠프 매일 공부 기록

2024.05.14 TIL - Queue를 활용한 오브젝트 풀(ObjectPool)

테크러너 2024. 5. 14.

오브젝트 풀링

  • 객체를 미리 생성해 두고 필요할 때 가져다 사용하고, 사용이 끝나면 다시 풀에 반납하는 방식
  • 오브젝트 풀링은 생성(Instantiate)과 소멸(Destroy)이라는 비용이 큰 작업을 최소화함으로써 성능을 향상시키는 데 중요한 역할을 한다.
  • 빈번하게 생성하고 파괴되는 객체(예: 총알, 입자 등)에 대해 중요하며, 이런 객체들을 풀에 저장해 놓고 재사용함으로써 메모리 할당과 가비지 컬렉션에 따른 성능 저하를 방지할 수 있다.
  • 오브젝트 풀링은 적절히 사용하면 큰 성능 개선을 가져올 수 있지만, 불필요한 메모리 사용을 증가시킬 수 있으므로 사용 시에는 신중해야 합니다. 오브젝트 풀의 크기를 적절히 조절하는 것이 중요하다.

 

사실 객체를 미리 생성 해두는 방식도 있지만 미리 생성하지 않는 방식도 있다.

위에서 말한 방식은 미리 오브젝트를 만들어 놓고, 달라고 하면 준다음에 다시 반납하는걸 반복한다.

달라고 한 것보다 더 필요하면 그만큼 더 생성한다.

근데 이걸 또 파괴하는 방식이 있고, 파괴안하는 방식이 있다.

 

두 번째 방식은 미리 안만들어둔다. 근데 만들어달라고 하면 그때 만들어준다.

반납하면 다시 넣어둔다.

 

모자라면 쓰고 있는걸 다시 재활용하는 방식도 있다.(아주 특별한 케이스)

 

 

오브젝트 풀 만들기

    // 오브젝트 풀 데이터를 정의할 데이터 모음 정의
    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject prefab;
        public int size;
    }

정해둔 size만큼 생성한다. → 생성을 하는 방식

 

    public List<Pool> Pools;
    public Dictionary<string, Queue<GameObject>> PoolDictionary;

오브젝트풀 만드는 방식에는 여러가지가 있는데 여기서는 `Queue`를 활용한다.

큐로 저장하여 제일 먼저 온 애를 제일 먼저 사용하는 방식이다.

→ `FIFO(First in First Out)`, `FCFS(First Come First Served)`

 

Object 이름에 따라서 Object들을 Pool에다가 등록해서 리스트를 만들어 놓으면 그거에 따라서 Pool `Dictionary`를 만들고, Pool Dictionary에는 어떤 Object가 어떤 Pool에 있는지 바로 매칭관계로 만든것.

이유는 List를 해버리면 Pool을 찾는 데 자체가 `O(n)`이 든다. 근데 Dictionary를 사용하면 `O(1)`이 드니까 훨씬 더 빠르게 접근할 수 있다.

 

    private void Awake()
    {
        // 인스펙터창의 Pools를 바탕으로 오브젝트풀을 만들 것. 
        // 오브젝트풀은 오브젝트마다 따로이며, pool개수를 넘어가면 강제로 끄고 새로운 오브젝트에게 할당.
        PoolDictionary = new Dictionary<string, Queue<GameObject>>();
        foreach (var pool in Pools)
        {
            // 큐는 FIFO(First-in First-out) 구조로서, 줄을 서는 것처럼 가장 오래 줄 선(enqueue) 객체가 가장 먼저 빠져 나올(dequeue) 수 있는 구조
            Queue<GameObject> objectPool = new Queue<GameObject>();
            for (int i = 0; i < pool.size; i++)
            {
                // Awake하는 순간 오브젝트풀에 들어갈 Instantitate 일어나기 때문에 터무니없는 사이즈 조심
                GameObject obj = Instantiate(pool.prefab);
                obj.SetActive(false);
                // 줄의 가장 마지막에 세움.
                objectPool.Enqueue(obj);
            }
            // 접근이 편한 Dictionary에 등록
            PoolDictionary.Add(pool.tag, objectPool);
        }
    }

Awke에서 오브젝트 풀 등록

 

    public GameObject SpawnFromPool(string tag)
    {
        // 애초에 Pool이 존재하지 않는 경우
        if (!PoolDictionary.ContainsKey(tag))
            return null;

        // 제일 오래된 객체를 재활용
        GameObject obj = PoolDictionary[tag].Dequeue();
        PoolDictionary[tag].Enqueue(obj);
        obj.SetActive(true);
        return obj;
    }

위의 코드대로 오브젝트풀을 사용하게 되면 이 과정에서 오브젝트가 살아있으면 문제가 된다.

살아있는 애를 없애고, 만드는 코드이기 때문이다.

 

예를 들면 1초에 화살이 50개가 나가게 한다고 하면 20개밖에 안만들게 세팅했을 때 쓰고 바로 재활용이 되기 시작하니까 화살이 나가다가 사라지는 현상이 나타날 수 있다.

그래서 오브젝트 풀을 만들 때 큐를 사용하게 되면 이러한 문제가 발생할 수 있다.

아직 사라지지도 않는데 빼서 쓰려고 하니까 문제이다.

이러한 문제를 해결하려면 오브젝트가 아직 쓰고 있는지를 먼저 확인해야한다.

반응형

댓글