pooling은 어디에나 필요하므로. 프레임워크 내에 기본적으로 가져가려고 내가 편한 형태로 작성
- 전체 풀링 오브젝트 관리해줄 매니저 스크립트
필요한 기능 생각나면 추가해 사용하자
<PoolingManager.cs>
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using FrameWork.ExtensionMethods;
using System;
using System.Threading.Tasks;
namespace FrameWork
{
public class PoolingManager : Singleton<PoolingManager>
{
#region Internal_Class
/// <summary>
/// PoolingObject동작 관리 시스템
/// </summary>
public class PoolingSystem
{
#region Structs
public struct Option
{
public bool useResize; // 자동할당사용 여부
public int resizedCount; // 자동할당 시 추가 될 수
public int totalLmit; // 자동할당 시 전체 수 제한 -1 is Unlimited
public int initialCount; // 초기 확보 수
public Option(bool useResize, int resizedCount, int totalLmit, int initialCount)
{
this.useResize = useResize;
this.resizedCount = resizedCount;
this.totalLmit = totalLmit;
this.initialCount = initialCount;
}
/// <summary>
/// Pooling옵션.
/// </summary>
/// <param name="useResize">자동할당기능</param>
/// <param name="resizedCount">자동할당 시 생성 될 수</param>
/// <param name="totalLmit">전체 수 제한(-1이면 제한없음)</param>
/// <param name="initialCount">초기 할당 수</param>
/// <returns></returns>
public static Option Create(bool useResize, int resizedCount, int totalLmit, int initialCount)
{
return new Option(useResize, resizedCount, totalLmit, initialCount);
}
public static Option Defalut()
{
return new Option(true, 10, -1, 20);
}
}
#endregion
#region Fields
Queue<PoolingObject> m_ReadyForUseObjs; // 사용 준비중인 Object큐
Option m_Option;
GameObject m_OriginPrefab; // 원본 Prefab
int
m_TotalPoolingObjCnt,
m_UsedPoolingObjCnt;
Action<GameObject>
m_AcPoolingObjRevive, // 풀링객체 사용될 떄 호출될 콜백
m_AcPoolingObjReturn; // 풀링객체 회수될 때 호출될 콜백
Transform m_PoolHolder; // Pooling객체 잡아둘 홀더
#endregion
#region Properties
public int ReadyForUseCount { get => m_ReadyForUseObjs.Count; } // 사용가능 대기중인 수
public int TotalPoolingObjCnt { get => m_TotalPoolingObjCnt; } // 전체 풀링객체 수
public int UsedPoolingObjCnt { get => m_UsedPoolingObjCnt; } // 사용되고 있는 수
#endregion
#region Methods_Private
private void Init()
{
m_ReadyForUseObjs.Clear();
for (int i = 0; i < m_Option.initialCount; ++i)
{
CreatePoolingObject(m_PoolHolder, m_AcPoolingObjRevive, m_AcPoolingObjReturn);
}
}
private void CreatePoolingObject(Transform parent_, Action<GameObject> whenRevive_, Action<GameObject> whenReturn_)
{
GameObject _go = UnityEngine.Object.Instantiate(m_OriginPrefab);
_go.name = m_OriginPrefab.name;
_go.SetActive(false);
PoolingObject _poolingObj = _go.GetComponent<PoolingObject>();
if (whenRevive_ != null) _poolingObj.AddEventWhenRevive(whenRevive_);
if (whenReturn_ != null) _poolingObj.AddEventWhenReturn(whenReturn_);
_poolingObj.transform.SetParent(parent_);
m_ReadyForUseObjs.Enqueue(_poolingObj);
++m_TotalPoolingObjCnt;
}
private bool Resize()
{
// 사이즈제한 있는 경우
if (m_Option.totalLmit != -1)
{
// 사이즈제한 넘으면 false
if (m_TotalPoolingObjCnt > m_Option.totalLmit)
return false;
// 사이즈제한 고려해 오브젝트 생성
int _permitCount = m_Option.totalLmit - m_TotalPoolingObjCnt;
if (_permitCount > m_Option.resizedCount)
{
for (int i = 0; i < m_Option.resizedCount; ++i)
{
CreatePoolingObject(m_PoolHolder, m_AcPoolingObjRevive, m_AcPoolingObjReturn);
}
}
else
{
for (int i = 0; i < _permitCount; ++i)
{
CreatePoolingObject(m_PoolHolder, m_AcPoolingObjRevive, m_AcPoolingObjReturn);
}
}
return true;
}
else
{
for (int i = 0; i < m_Option.resizedCount; ++i)
{
CreatePoolingObject(m_PoolHolder, m_AcPoolingObjRevive, m_AcPoolingObjReturn);
}
return true;
}
}
#endregion
#region Methods_Public
public PoolingSystem(PoolingObject poolingObj_, Option option_, Transform parent_, Action<GameObject> whenObjRevive_ = null, Action<GameObject> whenObjReturn_ = null)
{
m_OriginPrefab = poolingObj_.gameObject;
m_Option = option_;
m_ReadyForUseObjs = new Queue<PoolingObject>(m_Option.initialCount);
m_AcPoolingObjRevive = whenObjRevive_;
m_AcPoolingObjReturn = whenObjReturn_;
m_PoolHolder = parent_;
Init();
}
public static PoolingSystem Create(PoolingObject poolingObj_, Option option_, Transform parent_, Action<GameObject> whenObjRevive_ = null, Action<GameObject> whenObjReturn_ = null)
{
return new PoolingSystem(poolingObj_, option_, parent_, whenObjRevive_, whenObjReturn_);
}
/// <summary>
/// 풀링객체 반환. 만약 resize가 안되서 반환할게 없다면 null반환한다.
/// </summary>
/// <returns></returns>
public PoolingObject GetPoolingObject()
{
PoolingObject _poolingObj = null;
if (m_ReadyForUseObjs.Count == 0)
{
if (m_Option.useResize)
{
if (Resize())
{
_poolingObj = m_ReadyForUseObjs.Dequeue();
}
}
}
else
{
_poolingObj = m_ReadyForUseObjs.Dequeue();
}
if (_poolingObj != null) ++m_UsedPoolingObjCnt;
return _poolingObj;
}
public void ReturnPoolingObject(PoolingObject po_)
{
--m_UsedPoolingObjCnt;
m_ReadyForUseObjs.Enqueue(po_);
}
#endregion
}
#endregion
#region Fields
[SerializeField] Dictionary<string, PoolingSystem> m_DicPooling;
#endregion
#region Properties
public Dictionary<string, PoolingSystem> DicPooling { get => m_DicPooling; }
#endregion
#region Methods_Protected
protected override void Init()
{
m_DicPooling = new Dictionary<string, PoolingSystem>();
}
#endregion
#region Methods_Public
/// <summary>
/// 풀링할 오브젝트들을 미리 등록한다.
/// </summary>
/// <param name="prefab_">등록할 Prefab</param>
/// <param name="option_">PoolingSystem에 사용될 Option값</param>
/// <param name="whenObjRevive_">풀링될객체가 사용될 때 호출될 콜백함수 - POInstantiate로 생성될 떄 호출</param>
/// <param name="whenObjReturn_">풀링될객체가 회수될 때 호출될 콜백함수 - PODestroy로 회수될 댸 호출</param>
public static void RegisterPrefab(GameObject prefab_, PoolingSystem.Option option_, Action<GameObject> whenObjRevive_ = null, Action<GameObject> whenObjReturn_ = null)
{
if (PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError("aleady Register" + prefab_.name);
return;
}
// PoolingObject컴포넌트 설정
PoolingObject _poolingObj = prefab_.GetComponent<PoolingObject>();
if (_poolingObj == null) _poolingObj = prefab_.AddComponent<PoolingObject>();
GameObject _poolingObHandler = new GameObject("Pool_" + prefab_.name);
_poolingObHandler.transform.SetParent(PoolingManager.Instance.transform);
// Dic에 등록
PoolingManager.Instance.m_DicPooling.Add(prefab_.name, PoolingSystem.Create(_poolingObj, option_, _poolingObHandler .transform, whenObjRevive_, whenObjReturn_));
}
/// <summary>
/// 사용하기에 앞서 RegisterPrefab에 등록된 프리팹인지 확인.
/// <para>PODestroy로 풀로 돌려줄 떄는 PoolingObject인자로 호출하는게 더 최적화된다. => 미리 PoolingObject캐싱해 놓자.</para>
/// </summary>
/// <param name="prefab_"></param>
/// <returns></returns>
public static PoolingObject POInstantiate(GameObject prefab_)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError("Please RegisterPrefab before Call POInstantiate");
return null;
}
PoolingObject _po = PoolingManager.Instance.m_DicPooling[prefab_.name].GetPoolingObject();
_po.gameObject.SetActive(true);
return _po;
}
public static PoolingObject POInstantiate(GameObject prefab_, Transform parent_)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError("Please RegisterPrefab before Call POInstantiate");
return null;
}
PoolingObject _po = PoolingManager.Instance.m_DicPooling[prefab_.name].GetPoolingObject();
_po.gameObject.SetActive(true);
_po.transform.SetParent(parent_);
return _po;
}
public static PoolingObject POInstantiate(GameObject prefab_, Transform parent_, bool worldPositionStays)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError("Please RegisterPrefab before Call POInstantiate");
return null;
}
PoolingObject _po = PoolingManager.Instance.m_DicPooling[prefab_.name].GetPoolingObject();
_po.gameObject.SetActive(true);
_po.transform.SetParent(parent_, worldPositionStays);
return _po;
}
public static PoolingObject POInstantiate(GameObject prefab_, Vector3 position_, Quaternion rotation)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError("Please RegisterPrefab before Call POInstantiate");
return null;
}
PoolingObject _po = PoolingManager.Instance.m_DicPooling[prefab_.name].GetPoolingObject();
_po.gameObject.SetActive(true);
_po.transform.SetPositionAndRotation(position_, rotation);
return _po;
}
public static PoolingObject POInstantiate(GameObject prefab_, Vector3 position_, Quaternion rotation, Transform parent_)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError("Please RegisterPrefab before Call POInstantiate");
return null;
}
PoolingObject _po = PoolingManager.Instance.m_DicPooling[prefab_.name].GetPoolingObject();
_po.gameObject.SetActive(true);
_po.transform.SetPositionAndRotation(position_, rotation);
_po.transform.SetParent(parent_);
return _po;
}
public static void PODestroy(GameObject prefab_)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(prefab_.name))
{
Debug.LogError(prefab_.name + "is not poolingObject");
}
prefab_.SetActive(false);
PoolingManager.Instance.m_DicPooling[prefab_.name].ReturnPoolingObject(prefab_.GetComponent<PoolingObject>());
}
public static void PODestroy(PoolingObject po_)
{
if (!PoolingManager.Instance.m_DicPooling.ContainsKey(po_.name))
{
Debug.LogError(po_.name + "is not poolingObject");
}
po_.gameObject.SetActive(false);
PoolingManager.Instance.m_DicPooling[po_.name].ReturnPoolingObject(po_);
}
public static void PODestroy(GameObject prefab_, float t_)
{
AppTools.WaitThenCallback(PoolingManager.Instance, t_, () => { PoolingManager.PODestroy(prefab_); });
}
public static void PODestroy(PoolingObject po_, float t_)
{
AppTools.WaitThenCallback(PoolingManager.Instance, t_, () => { PoolingManager.PODestroy(po_); });
}
#endregion
}
}
<PoolingObject.cs>
- 풀링 프리팹이 등록되면 자동적으로 붙을 컴포넌트
- 각 개별적으로 풀링 호출 밑 회수 때 이벤트 부여하기 위해 만듬
더 추가할 이벤트 있으면 추가로 작성하면 된다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using System;
public class PoolingObject : MonoBehaviour
{
#region Fields
Action<GameObject> m_AcRevive;
Action<GameObject> m_AcReturn;
#endregion
#region Properties
#endregion
#region UnityEvents
private void Awake() { }
private void OnEnable() => m_AcRevive?.Invoke(gameObject);
private void OnDisable() => m_AcReturn?.Invoke(gameObject);
#endregion
#region Methods_Public
public void AddEventWhenRevive(Action<GameObject> ac_) => m_AcRevive += ac_;
public void AddEventWhenReturn(Action<GameObject> ac_) => m_AcReturn += ac_;
#endregion
}
—사용법
-
등록할 프리팹 변수 선언
pbulic Gameobject _prefab;
-
PoolingManager에 pooling할 오브젝트 등록
// 사용할 프리팹 등록 PoolingManager.RegisterPrefab(_prefab, PoolingManager.PoolingSystem.Option.Create(true, 5, -1, 5), (_go) => { // 생성 될 때 호출 Debug.Log(_prefab.name + "is Revive."); // PoolingManager.PODestroy(PoolingObject, 2f); 인자 값으로 미리 캐싱해놓은 PoolingObject사용하는게 더 최적화된다. // 여기서는 간단하게 확인상 생성과 동시에 제거하느라 캐싱을 못했지만 POInstantiate사용시 PoolingObject컴포넌트 반환하므로 // 미리 캐싱해놓고 사용하자.(Gameobject로 넘길시 내부에서 GetComponent<PoolingObject>호출함) PoolingManager.PODestroy(_go, 2f); //2초뒤에 회수되도록 }, (_go) => { // 회수 될 때 호출 Debug.Log(_prefab.name + "is Return to Pooling."); });
-
생성하고 싶은 곳에서 생성
// 사용할 곳에 Instantiate와 동일하게 호출 PoolingObject _po = PoolingManager.POInstantiate(_prefab, Vector3.zero, Quaternion.identity);