[Unity] 강력한 기능의 오브젝트풀(ObjectPool) 추천
본문 바로가기
프로그래밍/Unity

[Unity] 강력한 기능의 오브젝트풀(ObjectPool) 추천

by [아마군] 2019. 9. 18.
반응형

1. 오브젝트풀(ObjectPool) 이란?

유니티 뿐만 아니라 모든 개발 플랫폼에서 인스턴스의 생성과 삭제는 매우 무거운 작업이다.

 

그리고 C# 기반의 유니티는 메모리 관리를 플랫폼이 담당하므로 가비지 컬렉션의 발생 타이밍을 직접 컨트롤 할 수 없기 때문에 예상치 못한 퍼포먼스 저하가 발생 할 수 있다.

 

이런 상황을 방지하기 위해 생성 가능한 인스턴스 갯수만큼의 메모리를 미리 할당해 풀에 넣어두고 필요할 때 꺼내고 사용이 끝나면 풀에 다시 반납해 나중에 재사용 할 수 있게 해주는 오브젝트 풀의 존재는 필수이다.

 

이런 오브젝트 풀을 통해 인스턴스 생성, 삭제 및 가비지 컬렉션 비용에서 매우 큰 이득을 얻을 수 있다.

 

또한 비슷한 뜻으로 오브젝트풀(ObjectPool) 을 메모리풀(MemoryPool) 이라고도 부르는데 엄밀히 따지면 두가지는 조금 다르다.

 

둘다 일정한 크기 만큼의 메모리 공간을 미리 생성해서 필요에 따라 꺼내거나 반납해 퍼포먼스를 최적화하는 기법이지만 오브젝트풀은 메모리를 예약할 때 오브젝트 단위로 생성을 하는 것이고 메모리풀은 특정 오브젝트에 상관없이 말 그대로 메모리 공간 자체의 크기를 예약 하는 것이다.

 

메모리풀의 경우 어떤 종류의 오브젝트든 필요한 만큼 메모리를 떼어내서 오브젝트로 캐스팅 한 후 사용하면 되지만 그만큼 구현에 더 신경을 써줘야 한다.

 

게임에서는 생성할 객체들이 정해져 있으므로 대체로 오브젝트풀을 사용하는게 더 편리하다.

 


2. 사용 준비

오픈소스로 공개되어 있는 뛰어난 기능과 안정성의 오브젝트풀인 AutoObjectPool 를 사용하려고 했는데 객체를 꺼내올 때 오브젝트 명이 아닌 원본 프리팹 인스턴스로만 가져올 수 있다거나 하는 등의 불편함이 있어서 좀더 편하게 사용할 수 있도록 수정해 보았다.

 

Download ObjectPool source code => https://github.com/skuld2000/ObjectPool

 

위의 깃허브 링크에서 ObjectPool 을 받은 후 유니티 프로젝트의 적당한 위치에 추가해 주자.

 

이것으로 모든 준비는 끝났다.

 

아, 잠깐... 소스코드 중 오브젝트풀의 메소드 인터페이스를 담당하는 매니저 클래스인 ObjectPoolManager 는 내가 사용중인 제네릭(Generic) 기반의 싱글톤(Singleton) 으로 되어 있다.

 

이 부분은 여러분이 사용 중인 싱글톤 방식이 다르다면 거기에 맞추거나 내가 일전에 올린 싱글톤 패턴의 제너릭 클래스 포스팅 의 Singleton.cs 를 가져다 사용하기 바란다.

 

https://skuld2000.tistory.com/26

 

[Unity] 싱글톤(Singleton) 패턴을 제너릭 클래스로 구현해서 범용적으로 사용하는 방법

싱글톤(Singleton) 디자인 패턴은 게임 개발 시 단 하나의 유일한 인스턴스만 존재해야 하는 Manager 클래스 들을 구현하기 위해 가장 많이 사용되는 디자인 패턴이다. 개발 언어, 플랫폼과 관련없이 싱글톤 패턴..

skuld2000.tistory.com


3. 오브젝트풀 생성

사용법도 간단하다.

 

우선 오브젝트풀에 풀링할 프리팹을 등록한다.

 

등록 과정에서 최종적으로 필요한 인자는 총 7개 이다.

  1. prefab : 풀링할 프리팹 인스턴스
  2. addPool : 오브젝트가 minPool 갯수 이상일 경우 풀 확장 시 증가시킬 단위 (1.0f 이하면 현재 풀 크기의 백분율, 이상이면 근접한 정수 단위로 증가.)
  3. minPool : 오브젝트풀 생성 시 풀에 미리 할당해 둘 최소 갯수
  4. emptyBehaviour : 풀이 비어 있는 상태에서 객체 요청이 왔을 때 추가 객체를 생성할지 말지 또는 가장 오래전 활성화 된 오브젝트를 다시 재사용 할 지 여부
  5. maxBehaviour : 풀이 비어 있고 최대 크기에 도달했을 때 추가 객체 생성을 실패하고 끝낼 지, 또는 가장 오래전 활성화 된 오브젝트를 다시 재사용 할 지 여부
  6. modBehaviour : emptyBehaviour 와 maxBehaviour 설정 값을 해당 풀의 블록에 저장할 지 여부

일반적인 경우 Prefab, addPool, minPool 세가지 값을 인자로 갖는 메소드를 사용하면 된다.

 

그리고 꺼내올 때는 해당 Prefab 의 이름을 사용하므로 등록에 사용한 Prefab 인스턴스는 별도로 저장해두지 않아도 된다.

 


3. 오브젝트 스폰(Spawn)

이제 등록한 오브젝트풀에서 새 오브젝트를 꺼내올(Spawn) 차례다.

 

Spawn 함수가 요구하는 인자들을 살펴보자

 

  1. name : 오브젝트풀을 생성할 때 사용했던 Prefab 의 이름
  2. parent : 오브젝트풀에서 꺼내온 해당 오브젝트 인스턴스가 추가될 부모 오브젝트의 transform
  3. child : 지정한 인덱스의 자식을 활성화(SetActive) 
  4. pos : 좌표
  5. rot : 회전값
  6. usePosRot : 4,5 번 인자에 넘겨준 값을 쓸지, 자체 transform 의 값을 쓸지 여부

일반적으로는 name 과 parent 를 인자로 갖는 메소드를 사용하면 된다.

 


4. 오브젝트 반납(Despawn)

사용이 끝난 오브젝트는 다시 풀로 반납(Despawn) 한다.

 

Despawn 은 GameObject 로 반납하는 메소드와 AP_Reference 로 반납하는 두가지 타입이 있다.

 

대부분의 경우 GameObject 로 사용할 것이므로 이 메소드를 사용하게 될것이다.

 

반납할 오브젝트와 지연 삭제를 위해 반납할 시간을 입력하는 두가지 메소드를 필요에 따라 적당히 사용하면 된다.

 

그 외에도 씬 리셋등의 경우를 위해 모든 오브젝트를 반납하는 DespawnAll 이나 풀 자체를 삭제하는 RemovePool, RemoveAll 등의 메소드가 제공된다. 


5. 사용 예

실제 사용 샘플은 아래와 같다.

 

내 샘플 프로젝트에서 사용한 실제 예를 보자

 

내가 사용한 Singleton 클래스는 DontDetroyOnLoad 에 attach 되는 구조이므로 ObjectPoolManager 역시 DontDestoryOnLoad 하위에 추가된다.

 

그리고 InitializeSpawn 메소드로 생성한 오브젝트풀은 현재 Card_magician_magic_gear_hand 와 Magician_Back, Character, Warrior_Front, ButtonSelectCard 이렇게 5종류이며 각 풀 별로 아직 사용되지 않은 비활성 오브젝트들이 자식으로 들어있다.

 

각 풀을 선택해 Instpector 창을 보면 AP_Pool 컴포넌트가 추가 되어 있으며 이 풀의 프리팹과 현재 사이즈, 최대 사이즈 및 갯수 초과 요청 시 어떻게 할 것인지를 런타임 중에 수정할 수 있게 되어 있다.

 

그리고 디버그 옵션에는 현재 이 풀의 로그를 콘솔창에 출력하는 기능과

 

강제로 풀의 오브젝트를 Spawn 시키는 기능이 있다.

 

사용하기도 쉽고 다양한 상황에 맞춰 커스터마이징 할 수 있는 다양한 기능을 제공하므로 쓸만한 오브젝트풀을 찾고 있다면 한번쯤 써보길 권한다.

 

반응형

댓글