AssetBase/Assets/FirstVillain/ObjectPool/PoolManager.cs

191 lines
5.8 KiB
C#

using FirstVillain.Singleton;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Pool;
namespace FirstVillain.ObjectPool
{
//GameObject Pooling
public class PoolManager : UnitySingleton<PoolManager>
{
[SerializeField] private int _capacity = 10;
[SerializeField] private int _maxSize = 1000;
[SerializeField] private bool _collectionCheck = true;
//음... 클래스 타입별로 풀을 만드는건?
private Dictionary<string, PoolItem> _poolDict = new();
public GameObject Spawn(string path, Transform parent = null)
{
string name = Path.GetFileName(path);
if(!_poolDict.ContainsKey(name))
{
CreatePoolItem(path);
}
var pooledObj = _poolDict[name].Pool.Get();
if (parent != null)
{
pooledObj.transform.Reset(parent);
}
return pooledObj;
}
public T Spawn<T>(string path, Transform parent = null)
{
return Spawn(path, parent).GetComponent<T>();
}
public void ReleaseGameObject(GameObject obj)
{
if (_poolDict.ContainsKey(obj.name))
{
try
{
obj.transform.Reset();
try
{
_poolDict[obj.name].Pool.Release(obj);
}
catch
{
obj.SetActive(false);
}
}
catch (Exception ex)
{
Debug.LogError($"Exception has occured!!! Object : {obj.name} === {ex.Message}");
}
}
else
{
Debug.LogWarning($"Object[{obj.name}] does not exist in pool dictionary. But it's been destroyed anyway :D");
Destroy(obj);
}
}
#region Create Pool
//현재 Resources.Load 기반으로 구현...
public void CreatePoolItem(string path)
{
string resourceName = Path.GetFileName(path);
if(!_poolDict.ContainsKey(resourceName))
{
var resource = LoadAssetOnResources<GameObject>(path);
var pool = new PoolItem(path, CreateNewObjectPool(resource, resourceName));
_poolDict.Add(resourceName, pool);
}
}
private ObjectPool<GameObject> CreateNewObjectPool(GameObject prefab, string resourceName)
{
return new ObjectPool<GameObject>(
() =>
{
GameObject obj = Instantiate(prefab);
obj.name = resourceName;
return obj;
},
OnGetItem,
OnReleaseItem,
OnDestroyItem,
_collectionCheck /* Collection checks will throw errors if we try to release an item that is already in the pool.*/,
_capacity/*defalut capacity*/,
_maxSize
);
}
private void OnGetItem(GameObject obj)
{
obj.SetActive(true);
}
private void OnReleaseItem(GameObject obj)
{
obj.SetActive(false);
}
private void OnDestroyItem(GameObject obj)
{
Destroy(obj);
}
#endregion Create Pool
#region Resource Load
//리소스 관리로 이전 예정
public T LoadAssetOnResources<T>(string resourcePath) where T : UnityEngine.Object
{
var resourceObj = Resources.Load<T>(resourcePath);
if (resourceObj == null)
{
throw new ResourceCriticalException($"Cannot Load Resources Object [{resourcePath}]");
}
return resourceObj;
}
#endregion Resource Load
#region AssetBundle Refresh Shader
private void RefreshShader<T>(UnityEngine.Object obj)
{
//AssetBundle은 custom shader를 포함하지 않으므로 불러올 시점에 shader를 재설정해준다.
if (typeof(GameObject) == typeof(T))
{
GameObject go = obj as GameObject;
if (go.GetComponents<Renderer>() != null)
{
RefreshShader(go.GetComponents<Renderer>());
}
if (go.GetComponentsInChildren<Renderer>(true) != null)
{
RefreshShader(go.GetComponentsInChildren<Renderer>(true));
}
}
else if (typeof(Material) == typeof(T))
{
RefreshShader(obj as Material);
}
}
private void RefreshShader(Material material)
{
Shader shader = Shader.Find(material.shader.name);
if (shader != null)
{
material.shader = shader;
}
else
{
Debug.LogWarning(string.Format("unable to refresh shader: [{0}] in material [{1}]", material.shader.name, material.name));
}
}
private void RefreshShader(Renderer[] renderers)
{
if (null == renderers)
{
return;
}
for (int i = 0; i < renderers.Length; i++)
{
Material[] materials = renderers[i].sharedMaterials;
for (int j = 0; j < materials.Length; j++)
{
if (materials[j] == null) { continue; }
RefreshShader(materials[j]);
}
}
}
#endregion AssetBundle Refresh Shader
}
}