카테고리 없음

Unity - 오브젝트 풀링(미리 객체를 만들어두고 재사용하는 방법)

이더23 2024. 10. 18. 23:19

오브젝트 풀링

게임 개발에 널리 사용되는 테크닉으로, 게임의 성능을 개선하기 위해 사용됩니다.

- 오브젝트 풀링은 객체를 미리 생성해 두고 필요할 때 가져다 사용하고, 사용이 끝나면 다시 풀에 반납하는 방식을 말합니다.

- 오브젝트 풀링은 생성(Instantiate)과 소멸(Destroy)이라는 비용이 큰 작업을 최소화함으로써 성능을 향상시키는 데 중요한 역할을 합니다.

- 이는 특히 빈번하게 생성하고 파괴되는 객체(예: 총알, 입자 등)에 대해 중요하며, 이런 객체들을 풀에 저장해 놓고 재사용함으로써 메모리 할당과 가비지 컬렉션에 따른 성능 저하를 방지할 수 있습니다.

- 오브젝트 풀링은 적절히 사용하면 큰 성능 개선을 가져올 수 있지만, 불필요한 메모리 사용을 증가시킬 수 있으므로 사용 시에는 신중해야 합니다. 오브젝트 풀의 크기를 적절히 조절하는 것이 중요합니다.

ObjectPool 만들기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject prefab;
        public int size;
    }
    
    public List<Pool> pools;
    public Dictionary<string, Queue<GameObject>> poolDistionary;

    private void Awake()
    {
        poolDistionary = new Dictionary<string, Queue<GameObject>>();
        foreach (var pool in pools)
        {
            Queue<GameObject> objectPool = new Queue<GameObject>();
            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.prefab);
                obj.SetActive(false);
                objectPool.Enqueue(obj);
            }
            poolDistionary.Add(pool.tag, objectPool);
        }
    }
    

    public GameObject SpawnFromPool(string intag)
    {
        if (!poolDistionary.ContainsKey(intag))
        {
            return null;
        }
        Queue<GameObject> objectPool = poolDistionary[intag];

        int count = objectPool.Count;
        for (int i = 0; i < objectPool.Count; i++)
        {
            GameObject outObj = objectPool.Dequeue();

            if (!outObj.activeInHierarchy)
            {
                outObj.SetActive(true);
                objectPool.Enqueue(outObj);
                return outObj;
            }

            count++;
            objectPool.Enqueue(outObj);
        }

        if (count == objectPool.Count)
        {
            GameObject outNewObj = Instantiate(objectPool.Peek());
            outNewObj.SetActive(true);
            objectPool.Enqueue(outNewObj);

            return outNewObj;
        }

        return null;
    }
}

 

정보를 담을 Pool클래스를 만들고 Pool타입의 List를 하나생성하는데 그렇게 하는 이유는 다양한 프리펩을 담기 위해서다.(두번째 필드명이 GameObject인 이유다.) 그리고 저장할 공간을 new로 만들고 foreach를 통해 리스트에서 가져온 값을 저장할 Queue를 생성해서 objectPool을 추가하는데  Instantiate를 통해서 게임오브젝트를 만들고 SetActive(false) 를 해서 비활성화를 시킨다. 여기서 Queue는 

 

objectPool.Enqueue(obj);
GameObject outObj = objectPool.Dequeue();

위 코드처럼 사용을해서 순서대로 값을 가져올 수 있게 만드는거다.(만들어지면 제일 뒤에 넣은 다음에 꺼낼때에는 제일 빨리 만들어진애를 가져온다.) 

 

그다음에 추가를 다 했으니 poolDistionary 에 값을 넣어주면 세팅은 끝인거다. 그다음 SpawnFromPool 함수를 통해서 값을 가져오는데 키값을 매개변수로 받아와서 내가 사용할 프리팹을 가져온다. 그다음 순서대로 Enqueue을 사용해서 첫번째로 저장된 벨류값을 꺼내와서 사용을 하고 다시 Enqueue 를 사용해서 값을 맨뒤에 저장을 한다. 그렇게 해야지 프리펩들이 사라지지 않고 다시 사용할 수 있다.