Unity - 오브젝트 풀

수냉·2025년 11월 12일

Unity

목록 보기
5/10

오브젝트 풀(Object Pool)

객체가 생성될 때 메모리에는 각 객체가 필요한 메모리 공간이 할당된다. 주로 인스턴시에이트로 객체를 생성하는 방식으로 사용했다.
또한 객체를 파괴할 때는 GC(가비지 컬렉터)가 할당을 해제시켜준다. 이는 디스트로이로 객체를 파괴하는 방식으로 사용했다.
객체의 생성과 파괴는 무척이나 편리한 기능이지만 자주 사용되면 최적화에 좋지 않은 영향을 끼치는데,
생성과 파괴를 자주하게되면 메모리 단편화가 일어 날 수가 있다.

메모리 단편화란?

메모리 공간이 존재하긴 하나 할당이 불가능한 경우를 말한다. 주로 내부 단편화, 외부 단편화로 구분되어진다.

내부 단편화
  • 메모리를 할당 시 사용중인 작업량보다 더 크게 메모리가 할당되어 메모리 공간이 낭비되는 상태. 내부 단편화라고 한다.
외부 단편화
  • 메모리를 할당 및 해제할 때 중간 중간 비는 메모리가 생기는데, 중간에 있는 메모리 크기가 프로그램이 들어갈 메모리보다 작아 따로 메모리 힙 영역을 추가로 생성해야된다. 이를 외부 단편화라고 한다.

이렇듯 무분별한 생성과 파괴는 메모리에 부담을, GC가 자주 활성화되어 일시적인 성능저하, 프레임 드랍등 게임에 영향을 크게 미치는 최적화 악영향을 줄 수 있기 때문에 오브젝트 풀이라는 디자인 패턴으로 객체를 효율적으로 관리하는 기법을 사용한다.

오브젝트 풀이 뭐길래?

오브젝트 풀은 미리 할당된 공간에 여러 객체를 생성시키고 활성화, 비활성화 하면서 넣었다 뺐다 하듯이 재사용하는 디자인 패턴을 말한다.

예시

  1. 아주 큰 상자에 여러가지 도구를 담고, 도구가 필요할 때 마다 가져왔다가 필요 없으면 다시 상자로 넣기.

  2. FPS에서 총알을 발사하는데 굳이 총알을 만들고 삭제하고 할 필요 없이 총알을 미리 생성한 후 총을 발사하면 총알을 나타내고
    총알이 사라지면 총알을 비활성화해 불필요한 삭제를 방지 (오브젝트 객체 뿐만이 아닌 맵, 이펙트도 같은 방식으로 사용가능)

오브젝트 풀 사용 사례

ARPG

  • 다수의 적이 등장하고 사라지는 과정을 오브젝트 풀을 사용하여 관리
  • 이펙트 처리를 오브젝트 풀 활용

오픈월드

  • 심리스 오픈월드 시 맵을 오브젝트 풀로 관리하여 플레이어가 없는 곳에는 맵을 비활성화 하는 방식으로 성능 안정화

FPS

  • 유저가 사용하는 총알을 오브젝트 풀로 구현하여 관리
  • 총이 적이나 벽을 맞추면 나오는 이펙트를 오브젝트 풀로 관리

퍼즐 게임

  • 미사용 퍼즐들이나 생성되고 파괴되는 퍼즐들을 오브젝트 풀로 관리
  • 퍼즐을 파괴할때, 점수를 얻거나 특정 조건을 달성했을 때 애니메이션을 오브젝트 풀로 관리

유니티에서 오브젝트 풀 구현

Queue를 사용한 오브젝트 풀 코드

using UnityEngine;
using System.Collections.Generic;


public Class ObjPool : MonoBehavior
{
	[SerializeField] GameObject objPrefab;
    [SerializeField] int objCount = 5;
    
    private Queue<GameObject> myPool;
    
    private void Awake()
    {
    	//큐를 초기화
    	myPool = new Queue<GameObject>();
        
        for(int i = 0; i<objCount; i++)
        {
        	GameObject obj = Istantiate(objPrefab);
            //게임 오브젝트를 비활성화하여 큐에 추가
            obj.SetActive(false);
            myPool.Enqueue(obj);
        }
    }
    
    GameObject GetObj()
    {
    	//만약 큐에 오브젝트가 존재한다면
    	if(myPool.Count > 0)
        {
        	GameObject orb = myPool.Dequeue();
            orb.gameObject.SetActive(true);
        	return orb;
        }
        //만약 프리팹을 더 추가해야할 경우
        GameObject newOrb = Instantiate(objPrefab);
        return newOrb;
    }
    
    public void ReturnBullet(GameObject obj)
    {
    	//오브젝트를 비활성화
    	obj.SetActive(false);
        myPool.Enqueue(obj);
    }
}

유니티에서 제공하는 ObjectPool<>클래스를 활용한 코드

using UnityEngine;
using UnityEngine.Pool;

public Class ObjPool : MonoBehaviour
{

	//생성할 프리팹 지정
	[SerializeField] GameObject toInstantiate;
	private ObjectPool<GameObject> myPool;

	private void Start()
	{
    	//오브젝트 풀 초기화
		myPool = new ObjectPool<GameObject>
   	   (
       		//각 함수를 람다식으로 표현, 인자값에 명명된 인수를 반영
            
            // 오브젝트 생성 시 호출되는 함수
    		createFunc: () => Instantiate(toInstantiate),
            //오브젝트를 가져올 때 호출되는 함수
        	actionOnget: obj => obj.SetActive(true),
            //오브젝트를 반납될 때 호출되는 함수
        	actionOnRelease: obj => obj.SetActive(false),
            //오브젝트 파괴될 때 호출되는 함수
        	actionOnDestroy: obj => Destroy(obj.gameObject)
       );
	}
    //오브젝트 가져오기
   public GameObject GetObj()
   {
       return myPool.Get();
   }
   //오브젝트 반납하기
   public void ReleaseObj(GameObject obj)
   {
   	   myPool.Release(obj);
   }
}

오브젝트 풀의 장단점

장점

  • 오브젝트를 미리 생성하고 재사용함으로써 불필요한 메모리 할당 및 제거를 방지
  • GC의 발동 빈도를 감소시켜 CPU연산을 줄이고 안정적인 프레임을 유지할 수 있음
  • 프레임 드랍 등의 렉 현상이 줄어들어 쾌적한 플레이 경험

단점

  • 오브젝트 생성과 파괴도 메모리를 많이 소모하지만 활성화와 비활성화도 충분히 성능에 영향을 줄 수 있다.
  • 풀의 크기가 너무 크면 내부 단편화가 일어날 수 있다는 점은 동일 하기 때문에 적절한 풀의 크기를 제어해야한다.
  • 이전에 사용한 오브젝트의 상태(위치, 회전 등)을 초기화 시키지 않는다면 의도하지 않은 결과를 초래할 수 있다.

0개의 댓글