Pool과 Prefab을 연동하여 구현하는 것을 오브젝트 풀이라고 한다.

오브젝트 풀 구현 방법에는 여러 가지가 있는데 오늘은 그중 유니티에 내장되어있는 오브젝트 풀에서 공부했다.

 

일단 일반적인 방법으로 Prefab 하나에 오브젝트 풀 스크립트를 붙여주는 것인데 이 방법의 단점은 Prefab을 하나 만들 때마다 오브젝트 풀 클래스를 만들어줘야하는 것이다. 그래서 유니티에서 지원하는 풀링을 이용해볼 것이다.

 

먼저 유니티에서 제공하는 Pool을 만들려면 OnCreate, OnGet, OnRelease, OnDestroy 콜백 함수 네 가지가 필요하다. 이 함수들을 IObjectPool 인터페이스를 상속하는 ObjectPool 클래스에 넣어준다. 제네릭으로 GameObject라는 자료형으로 제한한다.

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

public class Pool {
    private GameObject _prefab; // 어떤 프리팹과 엮일 것인가.
    private IObjectPool<GameObject> _pool; // 프리팹들을 담을 풀.
    private Transform _root; // 오브젝트가 생성될 위치.
	
    // 읽기 속성 프로퍼티 - 오브젝트가 비어있다면 생성해서 반환.
    private Transform Root {
        get {
            if (_root == null) {
                GameObject obj = new() { name = $"[Pool_Root] {_prefab.name}" };
                _root = obj.transform;
            }
            return _root;
        }
    }

	// 생성자 - MonoBehaviour를 상속받고 있지 않기 때문에 생성자로부터 초기화.
    public Pool(GameObject prefab) {
        this._prefab = prefab;
        this._pool = new ObjectPool<GameObject>(OnCreate, OnGet, OnRelease, OnDestroy);
    }

    public GameObject Pop() { // 스택, 선입후출/후입선출 형식의 자료 꺼내기.
        return _pool.Get(); // OnGet 호출.
    }

    public void Push(GameObject obj) { // 자료 들여보내기.
        _pool.Release(obj); // OnRelease 호출.
    }

	// 유니티에서 제공하는 Pool을 사용하기 위한 콜백 함수.
    #region Callbacks

    private GameObject OnCreate() {
        GameObject obj = GameObject.Instantiate(_prefab); // 오브젝트 생성.
        obj.transform.SetParent(Root); // 생성될 위치 등록.
        obj.name = _prefab.name; // 프리팹 이름 등록.
		// List.Add(obj);
        return obj;
    }
    private void OnGet(GameObject obj) { // Pool이 이미 있는 경우.
        obj.SetActive(true);
    }

    private void OnRelease(GameObject obj) { // Pool을 사용하지 않을 때.
        obj.SetActive(false);
    }

    private void OnDestroy(GameObject obj) { // Pool이 너무 많은 경우 정리.
        GameObject.Destroy(obj);
    }

    #endregion

}

 

이제 Manager를 통해 Pool을 관리해보자.

public class PoolManager {
	
    // 딕셔너리 생성.
    private Dictionary<string, Pool> _pools = new();

    public GameObject Pop(GameObject prefab) {
        // #1. 풀이 없으면 새로 만든다.
        if (_pools.ContainsKey(prefab.name) == false) {
            CreatePool(prefab);
        }

        // #2. 해당 풀에서 하나 가져온다.
        return _pools[prefab.name].Pop();
    }

    public bool Push(GameObject obj) {
        // #1. 풀이 있는지 확인한다.
        if (_pools.ContainsKey(obj.name) == false) return false;

        // #2. 풀에 게임 오브젝트를 넣는다.
        _pools[obj.name].Push(obj);

        return true;
    }

    private void CreatePool(GameObject prefab) { // 유지보수를 위해 Pool 생성을 함수로 만든다.
        Pool pool = new(prefab);
        _pools.Add(prefab.name, pool);
    }

	// 씬에 변화가 있을 때 비워주기 위한 함수.
    public void Clear() {
        _pools.Clear();
    }
}

 

테스트로 프리팹을 넣어 오브젝트 풀을 사용해보자.

public class BaseScene : MonoBehaviour {

    private bool _initialized;

    void Start() {
        Initialize();
    }

    protected virtual bool Initialize() {
        if (_initialized) return false;
        // 각종 초기화 함수.
        // DataManager 초기화
        // GameManager 초기화

        _initialized = true;
        return true;
    }

}

 

public class GameScene : BaseScene {

    public GameObject prefab1;
    public GameObject prefab2;

    protected override bool Initialize() {
        if (!base.Initialize()) return false;

        GameObject obj1 = Main.Pool.Pop(prefab1);
        GameObject obj2 = Main.Pool.Pop(prefab1);
        GameObject obj3 = Main.Pool.Pop(prefab1);
        GameObject obj4 = Main.Pool.Pop(prefab1);
        GameObject obj5 = Main.Pool.Pop(prefab1);

        Main.Pool.Push(obj2);
        Main.Pool.Push(obj4);

        return true;
    }

}

 

 

유니티 ObjectPool은 캡슐화가 잘 되어있다. 내부 로직을 몰라도 CallBack 함수 네 가지만 사용해도 ObjectPool을 이용할 수 있기 때문에 협업에 유용하다.

Manager로 관리하는 경우에도 마찬가지이다. Pool 클래스를 몰라도 생성자와 Pop, Push 함수만 알아도 손쉽게 사용할 수 있다.

추가로 그렇기 때문에 협업에서 중요한 점은 내부 로직을 몰라도 함수명만 봐도 어떤 함수인지 알 수 있도록 쉽게 이용할 수 있게 명확히 표기하는 것이 좋다.

'부트캠프 > Study' 카테고리의 다른 글

Asset  (0) 2024.01.22
<Unity> Item 관리  (0) 2024.01.12
enable과 SetActive의 차이점  (0) 2023.12.28
<프로젝트> Unity 게임 개발 심화 개인과제  (1) 2023.12.27
<Unity> 프로젝트 빌드 하는 방법(Building)  (1) 2023.12.26

+ Recent posts