UNITY/Script

[UNITY C#] ObjectPool 구현

HYEOKJUN 2022. 2. 4. 18:00
반응형

ObjectManager.cs

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

/// <summary>
/// ObjectManager에서 관리할 오브젝트 리스트를 나타냅니다.
/// </summary>
public enum ObjectList {
    Object1, Object2
}

/// <summary>
/// ObjectPool을 관리하는 클래스입니다.
/// </summary>
public class ObjectManager : MonoBehaviour
{
    static ObjectManager instance = null; 
    public static ObjectManager Instance { // ObjectManager 싱글톤을 구현합니다. ObjectManager.Instance. 형식으로 접근합니다.
        get {
            if(instance == null){
                instance = FindObjectOfType<ObjectManager>();
            }
            return instance;
        }
    }

    [Header("Prefab")]
    [SerializeField] GameObject object1_prefab = null;
    Queue<GameObject> object1_queue = new Queue<GameObject>();
    [SerializeField] GameObject object2_prefab = null;
    Queue<GameObject> object2_queue = new Queue<GameObject>();
    // 새로운 Prefab을 등록하는 단위
    // [SerializeField] GameObject (오브젝트 이름)_prefab = null;
    // Queue<GameObject> (오브젝트 이름)_queue = new Queue<GameObject>();
    // ...

    /// <summary>
    /// 필요한 GameObject들을 미리 생성해 ObjectPool에 저장합니다.
    /// </summary>
    void Start() { 
        CreateObject(ObjectList.Object1, 5);
        CreateObject(ObjectList.Object2, 10);
    }

    /// <summary>
    /// Queue를 불러옵니다.
    /// </summary>
    /// <param name="object_target">불러올 오브젝트 Queue의 ObjectList입니다.</param>
    Queue<GameObject> GetQueue(ObjectList object_target) { 
        try {
            switch(object_target) {
                case ObjectList.Object1 : return object1_queue;
                case ObjectList.Object2 : return object2_queue;
                default : throw new Exception("Queue가 존재하지 않습니다.");
            }
        } catch {
            return null;
        }
    }
    /// <summary>
    /// Prefab을 불러옵니다.
    /// </summary>
    /// <param name="object_target">불러올 오브젝트 Prefab의 ObjectList입니다.</param>
    GameObject GetPrefab(ObjectList object_target) { 
        try {
            switch(object_target) {
                case ObjectList.Object1 : return object1_prefab;
                case ObjectList.Object2 : return object2_prefab;
                default : throw new Exception("Prefab이 존재하지 않습니다.");
            }
        } catch {
            return null;
        }
    }

    /// <summary>
    /// <para>GameObject를 생성합니다.</para>
    /// <para>(처음 실행하거나 Queue에 가져올 GameObject가 없을 경우 이 메서드를 통해 새로운 GameObject를 생성합니다)</para>
    /// </summary>
    /// <param name="object_target">생성할 오브젝트의 ObjectList입니다.</param>
    /// <param name="repeat">생성할 오브젝트의 개수입니다.</param>
    void CreateObject(ObjectList object_target, byte repeat = 1) { 
        GameObject prefab_target = GetPrefab(object_target);
        Queue<GameObject> queue_target = GetQueue(object_target);

        if(prefab_target != null && queue_target != null) {
            while(repeat-- > 0) {
                GameObject object_new = Instantiate(prefab_target);
                object_new.transform.SetParent(transform);
                object_new.SetActive(false);

                queue_target.Enqueue(object_new);
            }
        }
    }

    /// <summary>
    /// <para>오브젝트를 불러옵니다.</para>
    /// </summary>
    /// <param name="object_target">불러올 오브젝트의 ObjectList입니다.</param>
    /// <param name="setActive">불러올 오브젝트의 SetActive옵션입니다.</param>
    public GameObject GetObject(ObjectList object_target, bool setActive = true) { 
        Queue<GameObject> queue_target = GetQueue(object_target);

        if(queue_target != null) {
            if(queue_target.Count <= 0) {
                CreateObject(object_target);
            }
            GameObject object_return = queue_target.Dequeue();

            object_return.transform.SetParent(null);
            object_return.SetActive(setActive);

            return object_return;
        } else {
            return null;
        }
    }

    /// <summary>
    /// <para>오브젝트를 반환합니다.</para>
    /// </summary>
    /// <param name="object_target">반환할 오브젝트의 ObjectList입니다.</param>
    /// <param name="object_return">반환할 GameObject입니다.</param> 
    public void ReturnObject(ObjectList object_target, GameObject object_return) {
        if(object_return) {
            object_return.transform.SetParent(transform);
            object_return.SetActive(false);

            GetQueue(object_target).Enqueue(object_return);
        } else {
            throw new ArgumentNullException("반환하는 GameObject가 지정되지 않았습니다.");
        }
    }
}

 

다음과 같은 오브젝트 풀을 관리하는 스크립트를 작성합니다.

오브젝트 풀로 사용할 GameObject에 해당 스크립트를 추가하고 각 Prefab을 할당합니다.

Play 모드에 진입하면 오브젝트 풀이 활성화됩니다.

(오브젝트 풀에 있는 오브젝트들은 순환하면서 사용됩니다.)

 

Object 불러오기

GameObject object1 = ObjectManager.Instance.GetObject(ObjectList.Object1);

GetObject Instantiate대신 사용하는 GameObject 로드 메서드입니다.

(GameObject를 불러오고 나서 부모 오브젝트, 스케일, 위치 등은 직접적인 수정이 필요합니다.)
(UI(RectTransform) GameObject를 불러올때 반드시 transform.SetParent(... , false), transform.localScale = new Vector3(1f, 1f, 1f)를 해주어야 합니다.)

 

Object 반환하기

ObjectManager.Instance.ReturnObject(ObjectList.Object1, gameObject);

ReturnObjectDestroy대신 사용하는 GameObject 삭제 메서드입니다.

(위의 경우 GameObject를 반환할때 gameObject의 ObjectList를 정확하게 지정해 주어야합니다.)

 

반응형