using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;

/// <summary>
/// ๊ฐ„๋‹จํ•œ ๋ฒ”์šฉ ์˜ค๋ธŒ์ ํŠธ ํ’€
/// </summary>
/// <typeparam name="T">ํ’€์—์„œ ๋ฐ”๋กœ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•  ์ปดํฌ๋„ŒํŠธ ํƒ€์ž…</typeparam>
public class SimpleObjectPool<T> where T : MonoBehaviour
{
private readonly Transform objHolder;
private readonly T targetPrefab;
private readonly List<T> usingPool = new List<T>();
private readonly List<T> sleepingPool = new List<T>();

public SimpleObjectPool(T objToUse, Transform objHolder, int preWarmCount = 5)
{
targetPrefab = objToUse;
this.objHolder = objHolder;
PreWarm(preWarmCount);
}

/// <summary>
/// ํ’€์˜ ์ƒํƒœ๊ฐ€ ์ •์ƒ์ ์ธ์ง€ ์ฒดํฌํ•จ
/// ๋งŒ์•ฝ ๋น„์ •์ƒ์ ์ด๋ผ๋ฉด ํ’€์„ ์ •๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ๋„ ๊ฒธํ•จ
/// </summary>
/// <returns>true : ์ •์ƒ</returns>
public bool CheckPoolStatus()
{
var ret = objHolder && targetPrefab;
if (ret) return true; //์ •์ƒ์ ์ด์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ •๋ฆฌํ•จ
usingPool.Clear();
sleepingPool.Clear();

return false;
}

/// <summary>
/// ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฏธ๋ฆฌ ์ถ”๊ฐ€๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” method
/// </summary>
/// <param name="countToGenerate">๋ฏธ๋ฆฌ ์ถ”๊ฐ€๋กœ ์ƒ์„ฑํ•  ์˜ค๋ธŒ์ ํŠธ ์ˆ˜</param>
public void PreWarm(int countToGenerate)
{
GenerateObjects(countToGenerate);
}

/// <summary>
/// ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ’€์— ๋ฐ˜ํ™˜ํ•จ
/// </summary>
/// <param name="obj">sleeping pool์— ๋ฐ˜ํ™˜ํ•  ์˜ค๋ธŒ์ ํŠธ</param>
public void ReturnObject(T obj)
{
obj.gameObject.SetActive(false);
if (!usingPool.Remove(obj)) //ํ’€์— ์˜ํ•ด ์ œ์–ด๋˜์ง€ ์•Š๊ณ  ์žˆ๋Š” ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฐ˜ํ™˜ ์‹œ๋„ํ•˜๋Š” ๊ฒฝ์šฐ ์˜ˆ์™ธ ๋ฐœ์ƒ
throw new InvalidDataException();
sleepingPool.Add(obj);
}

/// <summary>
/// ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•จ
/// </summary>
/// <param name="count"> ์ƒ์„ฑํ•  ์˜ค๋ธŒ์ ํŠธ ์ˆ˜ </param>
private void GenerateObjects(int count = 5)
{
for (var i = 0; i < count; i++)
{
var newObj = Object.Instantiate(targetPrefab.gameObject, objHolder).GetComponent<T>();
newObj.gameObject.SetActive(false);
sleepingPool.Add(newObj);
}
}

/// <summary>
/// ๋ฏธ์‚ฌ์šฉ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์™€ ํ™œ์„ฑํ™”ํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•จ
/// </summary>
/// <param name="getNonActivated"> ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์€ ์ฒด๋กœ ๋ฐ˜ํ™˜ </param>
/// <returns> ํ™œ์„ฑํ™”๋œ ์˜ค๋ธŒ์ ํŠธ </returns>
public T GetObj(bool getNonActivated = false)
{
if (sleepingPool.Count == 0)
{
if (!ClearUsingPool())
GenerateObjects();
}

var retObj = sleepingPool[0];
sleepingPool.Remove(retObj);
usingPool.Add(retObj);
if (!getNonActivated)
retObj.gameObject.SetActive(true);

return retObj;
}

private readonly List<T> tempList = new List<T>();

/// <summary>
/// ํ’€์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋ฏธ์‚ฌ์šฉ ์ค‘์ธ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์‚ฌ์šฉ ์ค‘ ํ’€์— ์žˆ๋Š” ๊ฒฝ์šฐ ์ •๋ฆฌํ•จ
/// </summary>
/// <returns> ์ฒญ์†Œ๋œ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ๋Š”์ง€ (์—†์œผ๋ฉด false, ์žˆ์œผ๋ฉด true) </returns>
private bool ClearUsingPool()
{
tempList.Clear();
tempList.AddRange(usingPool.Where(obj => !obj.gameObject.activeSelf));
sleepingPool.AddRange(tempList);
usingPool.RemoveAll(tempList.Contains);
return tempList.Count != 0;
}
}


๋ฒ”์šฉ์œผ๋กœ ๋งŒ๋“ค์–ด๋‘๊ณ  ์ž˜ ์“ฐ๊ณ  ์žˆ๊ธด ํ•˜๊ฑฐ๋“ 

๊ทผ๋ฐ ์˜ค๋ธŒ์ ํŠธ ํ’€๋ง ํŠน์„ฑ ์ƒ ์•ˆ๋“ค์–ด๊ฐ€๋Š” ํ”„๋กœ์ ํŠธ๊ฐ€ ๋“œ๋ฌผ๋‹ค ๋ณด๋‹ˆ ๋” ๊ดœ์ฐฎ๊ฒŒ ๋‹ค๋“ฌ์–ด๋ณด๊ณ  ์‹ถ์–ด์„œ

์˜๊ฒฌ ๋ฌผ์–ด๋ณด๋Ÿฌ ์™”์Œ


์ถ”๊ฐ€ ๊ธฐ๋Šฅ, ์„ฑ๋Šฅ ์ƒ ๊ฐœ์„ ์  ์˜๊ฒฌ ๋ฐ›์Œ


์ธ๋ถ•์ด๋“ค์€ ๋” ์ž˜ ๋งŒ๋“ค์–ด์„œ ์“ฐ๊ณ  ์žˆ๊ฒ ์ง€๋งŒ ์“ฐ๊ณ  ์‹ถ์€ ์‚ฌ๋žŒ์€ ๊ฐ€์ ธ๋‹ค ์จ๋ผ