본문 바로가기
게임 개발 일지/내일배움캠프 TIL

[Unity] Object Pooling

by 빛하_ 2024. 2. 29.

오브젝트 풀링 (Object Pooling)

최적화의 꽃, 오브젝트 풀링은 성능 향상을 위해 게임 오브젝트를 미리 생성하고 필요할 때마다 재활용하는 기술이다.

게임에서 동적으로 생성 및 삭제되는 오브젝트의 비용이 큰 경우에 특히 유용하다.

일반적으로 게임에서 오브젝트를 동적으로 생성하고 삭제하는 것은 리소스 소모가 크기 때문에 성능에 부정적인 영향을 미칠 수 있다. 오브젝트 풀링은 이런 비용을 최소화하기 위해 미리 여러 개의 오브젝트를 생성하고, 필요할 때마다 활성화 및 비활성화하여 재사용한다.

생성할 수 있는 오브젝트를 미리 생성해 놓고, 필요할 때만 꺼내어 쓰고 다시 필요없어지면 집어넣는 방식.

오브젝트 파괴 시 작동하는 가비지 콜렉터가 메모리를 좌지우지하는 주요 멤버인데, 이것이 과도하게 활성화되면 게임 중 프레임 드랍이 발생할 수 있다. 그래서 메모리에 과부하가 걸리지 않도록 하는 방법으로 오브젝트 풀이 등장했다.

 

오브젝트 풀링의 주요 단계는 다음과 같다.

  1. 초기화 : 미리 설정된 개수의 오브젝트를 생성하고 초기 상태로 설정
  2. 활성화 및 비활성화 : 필요할 때 오브젝트를 활성화하여 사용하고, 사용이 끝난 후에는 비활성화
  3. 재활용 : 비활성화된 오브젝트를 필요한 곳에서 다시 활성화하여 재사용

오브젝트 풀링은 주로 총알 (투사체), 파티클 이펙트, 적 캐릭터 등과 같이 게임에서 반복적으로 생성되고 삭제되는 오브젝트에 많이 사용된다. 오브젝트가 빠른 주기로 생길수록, 많은 양이 필요할수록, 빨리 사라질수록 이를 효율적으로 관리할 필요가 있다. 오브젝트 풀링을 통해 게임의 성능을 향상시키고 메모리 사용을 최적화할 수 있다.

 

오브젝트 풀링의 장점

오브젝트 풀링의 장점은 객체를 새롭게 초기화하기 위한 비용을 절감할 수 있는 것이다.

따라서 메모리 사용량을 예측하기 쉽고, 성능 향상을 기대할 수 있다.

 

(1) 성능 향상

오브젝트 생성 및 삭제는 비용(메모리 할당/해제로 인한 가비지 콜렉터 사용)이 크기 때문에 

오브젝트 풀을 이용하면 해당 오브젝트를 재활용함으로써 프로그램 성능을 향상시킬 수 있다.

 

(2) 메모리 관리 

오브젝트 풀 사용 시 프로그램에 사용될 메모리를 예측하기 쉬워지므로 메모리를 효율적으로 관리할 수 있다.

 

오브젝트 풀링의 특징

단점으로는 객체의 상태를 예측하기 어렵다는 것이다. 사용자가 의도한대로 객체가 풀에서 나왔다가 다시 들어가는지 확인하기가 어렵다.

 

사용 시 주의사항으로는, 기본적으로 오브젝트를 담을 풀 자체를 크게 잡기 때문에 만약 오브젝트의 양이 많다면 그만큼 로딩하는 시간이 오래걸릴 수 있다는 것이다.

 

생성과 파괴가 자주 일어나지 않는 소량의 오브젝트의 경우 굳이 오브젝트 풀링을 사용하지 않아도 된다.

 

오브젝트를 담을 컨테이너로 리스트, 스택, 큐 등을 활용할 수 있다.

Get() 메서드를 사용해 객체를 가져오고, Release() 메서드를 사용해 객체를 반환한다.

 

오브젝트 풀링 예시 코드

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

public class ObjectPool : MonoBehaviour
{
    public GameObject prefab;  // 풀링할 프리팹
    public int poolSize = 10;   // 풀 사이즈

    private List<GameObject> objectPool;

    void Start()
    {
        InitializeObjectPool();
    }

    void InitializeObjectPool()
    {
        objectPool = new List<GameObject>();

        for (int i = 0; i < poolSize; i++)
        {
            GameObject obj = Instantiate(prefab);
            obj.SetActive(false);
            objectPool.Add(obj);
        }
    }

    public GameObject GetObjectFromPool()
    {
        // 비활성화된 오브젝트 찾기
        foreach (GameObject obj in objectPool)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }

        // 모든 오브젝트가 활성화되어 있으면 새로운 오브젝트 생성
        GameObject newObj = Instantiate(prefab);
        objectPool.Add(newObj);
        return newObj;
    }

    public void ReturnObjectToPool(GameObject obj)
    {
        // 오브젝트를 비활성화하고 풀에 반환
        obj.SetActive(false);
    }
}

 

ObjectPool 클래스는

특정 프리팹의 오브젝트 풀을 생성하고, 필요할 때 오브젝트를 가져오고, 사용이 끝나면 다시 풀에 반환한다.

 

public class Use : MonoBehaviour
{
    private ObjectPool objectPool;

    void Start()
    {
        objectPool = GetComponent<ObjectPool>();
        SpawnObjects();
    }

    void SpawnObjects()
    {
        for (int i = 0; i < 5; i++)
        {
            GameObject obj = objectPool.GetObjectFromPool();
            obj.transform.position = new Vector3(i * 2, 0, 0);
        }
    }
}

 

ObjectPool 스크립트를 게임 오브젝트에 추가하고,

Use 스크립트에서 GetObjectFromPool메서드를 호출하여 오브젝트를 생성하고 배치한다.

필요한 경우 ReturnObjectToPool 메서드를 사용하여 오브젝트를 풀에 반환할 수 있다.

 

댓글