생성-> class에 맞는 데이터 크기만큼 메모리 할당-> 재생성시 재 대입해줘야 하는 데이터 값
그리고 파괴할 때 가비지 메모리 제거 등 많은 작업이 필요하다.
이러한 작업이 적을 경우에는 크게 상관이 없을 수 있지만 많은 데이터를 생성과 제거를 반복한다 생각하면 끔찍하다.
이러한 작업을 적게 할 방법이 ObjectPool이라는 방식이다.
ObjectPool은 오브젝트를 (생성-> 사용-> 제거)반복 에서 생성-> 사용-> (보관->재사용)반복 ->제거 식으로 사용하여
메모리를 아끼는 방식이다.
2.Unity ObjectPool
Pool 관련 클래스는 몇 가지가 있지만. 대부분 비슷한 사용법을 가진다.
그중에서 ObjectPool <T0>을 사용할 것이다.
namespace UnityEngine.Pool
{
//
// 요약:
// A stack based Pool.IObjectPool_1.
public class ObjectPool<T> : IDisposable, IObjectPool<T> where T : class
{
public ObjectPool(Func<T> createFunc, Action<T> actionOnGet = null, Action<T> actionOnRelease = null, Action<T> actionOnDestroy = null, bool collectionCheck = true, int defaultCapacity = 10, int maxSize = 10000);
public int CountAll { get; }
public int CountActive { get; }
public int CountInactive { get; }
public void Clear();
public void Dispose();
public T Get();
public PooledObject<T> Get(out T v);
public void Release(T element);
}
}
이게 내부 구성과 ObjectPool의 매개변수다.
ObjectPool(생성할 때 함수, 사용할 때 함수, 사용 끝났을 때 함수, 제거 함수, 충돌 체크, 기본적으로 생성해둘 양, 최대 양)
으로 생각하면 쉽다
총과 총알로 설명하겠다.
먼저 총알 prefab에 IobjectPool <GameObject>pool {get;set;}을 생 성해준 뒤 Update에 날아가는 코드를 작성해준다.
using UnityEngine.Pool;
public class Bullet : MonoBehaviour
{
public IObjectPool<GameObject> Pool { get; set; }
public virtual void ReleaseObject() => Pool.Release(this.gameObject);
public virtual void OnTakeFromPool(GameObject obj) { }
public virtual void OnReturnedToPool(GameObject obj) => obj.SetActive(false);
public virtual void OnDestroyPoolObject(GameObject obj) => Destroy(obj);
}
public class Bullet_1 : BulletBase
{
public float speed = 7;
private void Update()
{
if (transform.position.y >= 10)
{
ReleaseObject();
}
transform.Translate(speed * Time.deltaTime * Vector3.up);
}
public override void OnTakeFromPool(GameObject obj)
{
obj.SetActive(true);
}
}
그 후 BulletSpawner.cs를 만들어주어 ObjectPool <Gameobject> Pool를 만들어준 뒤
ObjectPool내부에 생성할 Bullet의 함수들을 넣어둔다.
그리고 bullet 생성 함수에서 bullet 내부의 IObjcetPoolpool에 spawn의 Pool을 넣어준다.
( bullet 내부에서 spawn의 Pool을 사용해야 하기 때문에)
그리고 특정 상황마다 BulletSpawner 에서 Pool.Get();을 해준다.
Pool.Get()은 Pool 내부 함수로 자동으로 현재 재사용 가능한 오브젝트가 있는지 없는지를 판단하여
생성해준다.
public class BulletSpawner : MonoBehaviour
{
public BulletBase poolablePrefab;
public IObjectPool<GameObject> Pool { get; private set; }
public void Init()
{
Pool = new ObjectPool<GameObject>(
delegate {
// 오브젝트 생성함수
// 오브젝트 생성
GameObject obj = Instantiate(poolablePrefab.gameObject);
//생성한 오브젝트의 IObjectPool Pool 에 Spawner의 ObjectPool을 넣어준다.
obj.GetComponent<BulletBase>().Pool = Pool;
return obj;
},
// Bullet에 구현된 사용할 함수들 입력
poolablePrefab.OnTakeFromPool, poolablePrefab.OnReturnedToPool,
poolablePrefab.OnDestroyPoolObject,
true, poolablePrefab.defaultCapacity, 50));
}
public void Shoot()
{
var a= poolablePrefab.Pool.Get();
a.transform.position = this.gameObject.transform.position;
}
private void Start()
{
Init();
InvokeRepeating("Shoot", 0f,5f);
}
}
using UnityEngine.Pool;
// This component returns the particle system to the pool when the OnParticleSystemStopped event is received.
[RequireComponent(typeof(ParticleSystem))]
public class ReturnToPool : MonoBehaviour
{
public ParticleSystem system;
public IObjectPool<ParticleSystem> pool;
void Start()
{
system = GetComponent<ParticleSystem>();
var main = system.main;
main.stopAction = ParticleSystemStopAction.Callback;
}
void OnParticleSystemStopped()
{
// Return to the pool
pool.Release(system);
}
}
// This example spans a random number of ParticleSystems using a pool so that old systems can be reused.
public class PoolExample : MonoBehaviour
{
public enum PoolType
{
Stack,
LinkedList
}
public PoolType poolType;
// Collection checks will throw errors if we try to release an item that is already in the pool.
public bool collectionChecks = true;
public int maxPoolSize = 10;
IObjectPool<ParticleSystem> m_Pool;
public IObjectPool<ParticleSystem> Pool
{
get
{
if (m_Pool == null)
{
if (poolType == PoolType.Stack)
m_Pool = new ObjectPool<ParticleSystem>(CreatePooledItem, OnTakeFromPool, OnReturnedToPool, OnDestroyPoolObject, collectionChecks, 10, maxPoolSize);
else
m_Pool = new LinkedPool<ParticleSystem>(CreatePooledItem, OnTakeFromPool, OnReturnedToPool, OnDestroyPoolObject, collectionChecks, maxPoolSize);
}
return m_Pool;
}
}
ParticleSystem CreatePooledItem()
{
var go = new GameObject("Pooled Particle System");
var ps = go.AddComponent<ParticleSystem>();
ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
var main = ps.main;
main.duration = 1;
main.startLifetime = 1;
main.loop = false;
// This is used to return ParticleSystems to the pool when they have stopped.
var returnToPool = go.AddComponent<ReturnToPool>();
returnToPool.pool = Pool;
return ps;
}
// Called when an item is returned to the pool using Release
void OnReturnedToPool(ParticleSystem system)
{
system.gameObject.SetActive(false);
}
// Called when an item is taken from the pool using Get
void OnTakeFromPool(ParticleSystem system)
{
system.gameObject.SetActive(true);
}
// If the pool capacity is reached then any items returned will be destroyed.
// We can control what the destroy behavior does, here we destroy the GameObject.
void OnDestroyPoolObject(ParticleSystem system)
{
Destroy(system.gameObject);
}
void OnGUI()
{
GUILayout.Label("Pool size: " + Pool.CountInactive);
if (GUILayout.Button("Create Particles"))
{
var amount = Random.Range(1, 10);
for (int i = 0; i < amount; ++i)
{
var ps = Pool.Get();
ps.transform.position = Random.insideUnitSphere * 10;
ps.Play();
}
}
}
3.
내가 구현한 방식이다.
글쓴이는 싱글톤으로 PoolManager을 만들어서 같은 총알은 서로 공유하여 사용하게 만들어보았다.
이런식으로 만들경우 Spawn컴퍼넌트를 오브젝트에 붙여주고 원하는 BulletPrefab을 넣어주면 완성이다.