본문 바로가기
유니티/개념 정리

ObjectPool

by fore4022 2024. 10. 23.

ObjectPool은 미리 생성해 놓은 재사용 가능한 GameObject들을 필요에 의해서 ObjectPool에서 가져와서 활성화하여서 사용하는 것이다. 또한 사용하지 않을 때에는 해당 GameObject를 비활성화 시켜서 ObjectPool에 돌려준다.


장점

  • 게임의 실시간 성능을 개선할 수 있다.
    • 사용하여서 무조건 성능이 빨라지는 것이 사용의 목적이 아니다. 물론 성능이 빨라지는 것 또한 기대할 수 있는 부분이긴 하지만, 게임 진행 도중에 성능이 안정적일 수 있도록 해주는 것이 주된 사용 목적이라 생각한다.

단점

  • 사용하지 않는 객체도 항상 메모리를 차지한다.
  • 게임의 초기 실행 성능이 낮아진다.

ObjectPool 분석

"위의 장단점이 어떻게 나타나고, 왜 ObjectPool이라는 것을 사용하는 것일까?"

‘게임의 실시간 성능을 개선할 수 있다.’라고 하였는데, 그렇다면 아래와 같은 의문이 생길 것이다.

"필요할 때마다 GameObject의 instance를 생성하고, Destroy하는 것과 어떤 부분에서 성능의 차이가 생기는가?"

성능의 차이가 생기는 이유를 알기 위해서는, GameObject를 생성하고 파괴하는 과정 그리고 그로 인해서 GC(Garbage Collector)의 동작에 대해서 알아야 한다.

  • GameObject의 생성과 파괴 이 부분에서 성능 차이가 발생하는 이유는 되게 명확하다. 이유는 그저 단순히 생성하고 파괴하는 작업이 오래 걸리기 때문이다.
  • GC(Garbage Collector)의 동작하지만 GC는 빈 공간의 메모리가 생길 때마다 동작하는 것이 아닌, 일정 이상 메모리가 쌓이면 동작하게 된다. 이 작업이 게임의 순간적인 성능 저하를 일으킨다.
  • 물론 GC를 직접 구현하여 메모리가 생길 때마다 처리해 주어서, 성능 저하를 예방할 수 있다.
  • 파괴된 객체로부터 생겨난, 흩어져 있는 빈 공간의 메모리들을 사용할 수 있는 메모리 공간으로 모아주는 작업이다.

주의할 점

  • ObjectPool을 사용한다 하여서, 무조건적인 성능 개선이 일어나는 것이 아니다.
    • 초마다 파괴되고 생성되는 객체가 많지 않다면, 오히려 메모리만 차지하고 큰 성능에 개선이 없을 수 있다. 활성화와 비활성화 하는 작업이 성능을 적게 사용하지 않기 때문이다.
  • ObjectPool이 너무 많은 메모리를 차지하지 않도록 조절해주어야 한다.
    • 객체를 많이 생성하고, 사용하지 않는다면 그만큼 메모리를 낭비하게 된다.

using System.Collections.Generic;
using UnityEngine;
public class ObjectPool
{
    public Dictionary<GameObject, List<GameObject>> objectPool = new();

    public Transform Transform
    {
        get
        {
            GameObject go = GameObject.Find("ObjectPool");

            if(go == null)
            {
                go = new GameObject { name = "ObjectPool" };
            }

            return go.transform;
        }
    }
    public void CreateInstance(GameObject targetObject, int count = 10)
    {
        if(objectPool.TryGetValue(targetObject, out List<GameObject> list))
        {
            for(int i = 0; i < count; i++)
            {
                GameObject go = Object.Instantiate(targetObject, Transform);

                list.Add(go);

                go.SetActive(false);
            }
        }
        else
        {
            list = new();

            for (int i = 0; i < count; i++)
            {
                GameObject go = Object.Instantiate(targetObject, Transform);

                list.Add(go);

                go.SetActive(false);
            }

            objectPool.Add(targetObject, list);
        }
    }
    public GameObject GetInstance(GameObject targetObject)
    {
        if(objectPool.TryGetValue(targetObject, out List<GameObject> list))
        {
            foreach(GameObject go in list)
            {
                if(!go.activeSelf)
                {
                    go.SetActive(true);

                    return go;
                }
            }
        }

        return default;
    }
    public List<GameObject> GetInstance(GameObject targetObject, int count)
    {
        if(objectPool.TryGetValue(targetObject, out List<GameObject> list))
        {
            List<GameObject> returnObjects = new();

            foreach(GameObject go in list)
            {
                if(!go.activeSelf)
                {
                    go.SetActive(true);

                    returnObjects.Add(go);

                    if(returnObjects.Count == count)
                    {
                        break;
                    }
                }
            }

            return returnObjects;
        }

        return default;
    }
}

'유니티 > 개념 정리' 카테고리의 다른 글

Static(정적)  (0) 2024.11.04
Interface  (0) 2024.10.30
실행 결과 보장  (0) 2024.10.21
Non-MonoBehaviour  (0) 2024.10.14
GetComponentsInChildren  (0) 2024.10.11