zyeon's 작심삼일 코딩 공부
오브젝트 풀링 (오브젝트 풀 패턴) 본문
오브젝트 풀링은 객체 생성과 소멸에 따른 비용을 줄이기 위해, 객체들을 미리 생성한 후 사용하는 디자인 패턴이다.
나는 반복적으로 나오는 장애물과 아이템에 이 오브젝트 풀링을 사용하였다.
오브젝트 풀링은 코드의 유연성, 재사용성, 유지보수성을 높이기 위해 일반적으로 두 개 이상의 클래스를 사용하는 경우가 많다.
Poolable 클래스
풀에서 관리될 개별 오브젝트의 동작을 정의한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Poolable : MonoBehaviour
{
//자신이 속한 풀 참조, 상속을 통한 다양한 오브젝트 타입 구현 가능
protected ObjectPool m_pool;
//오브젝트 풀을 할당하고 오브젝트를 비활성화함
public virtual void Create(ObjectPool pool)
{
m_pool = pool;
gameObject.SetActive(false);
}
//오브젝트를 다시 풀로 반환
public virtual void Push()
{
m_pool.Push(this);
}
}
풀에서 사용할 수 있도록 초기화 및 반환 메서드를 구현해준다.
향후 풀에 들어갈 오브젝트들은 이 Poolable 클래스를 상속한다.
Create()와 Push() 기능은 virtual 키워드에서도 알 수 있듯이 오버라이딩을 통해 향후 기능 확장이 가능하다.
ObjectPool 클래스
오브젝트 풀의 상태와 동작을 관리한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/* 오브젝트 풀을 관리하는 클래스 */
public class ObjectPool : MonoBehaviour
{
//풀에 들어갈 오브젝트
public Poolable m_poolObj;
// 미리 할당할 오브젝트의 수
public int m_allocateCount;
public int allocateCount { get { return m_allocateCount; } set { m_allocateCount = value; } }
//오브젝트를 담고 있는 스택
Stack<Poolable> m_poolStack = new Stack<Poolable>();
void Start()
{
Allocate();
}
//오브젝트 생성
public void Allocate()
{
//count 만큼 생성 후 스택에 담음
for(int i = 0; i<m_allocateCount; i++)
{
Poolable allocateObj = Instantiate(m_poolObj, gameObject.transform);
allocateObj.Create(this);
m_poolStack.Push(allocateObj);
}
}
//스택에서 오브젝트를 활성화 시킨 후 반환
public GameObject Pop()
{
Poolable obj = m_poolStack.Pop();
obj.gameObject.SetActive(this);
return obj.gameObject;
}
//오브젝트를 다시 비활성화 한 후 풀에 반납
public void Push(Poolable obj)
{
obj.gameObject.SetActive(false);
m_poolStack.Push(obj);
}
}
Allocate()로 원하는 만큼 미리 오브젝트를 생성한 뒤 스택에 쌓아 둔다.
사용할 땐 Pop()으로 스택에서 오브젝트를 꺼내 활성화 시키면 되고,
사용이 끝난 후엔 Push()로 오브젝트를 다시 비활성화 한 뒤 다시 스택에 쌓아둔다.
오브젝트 풀링 사용시 설정 방법이다.
씬에 empty object 생성 후 Object Pool 스크립트 추가 후 원하는 프리팹과 생성 원하는 개수를 넣어준다.
이 때 프리팹에 Poolable을 상속한 코드가 포함되어 있어야한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Trap : Poolable // Poolable 상속
{
/* 코드 생략 ... */
void Update()
{
/* 코드 생략 ... */
if (this.transform.position.x <= -2.5f)
Push();
}
/* 코드 생략 ... */
}
Push()를 사용하여 오브젝트를 다시 반환해 줄 수 있다.
나는 방금 생성 된 empty object에 장애물을 생성해주는 코드도 추가해 주었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreatingObstacle : MonoBehaviour
{
// 장애물이 스폰될 위치
Vector3[] m_posIdx = { new Vector3(1.0f, 1.0f, 1.0f),
new Vector3(10.0f, 1.0f, 1.0f),
new Vector3(20.0f, 1.0f, 1.0f) };
Coroutine m_coroutine = null;
void Update()
{
if(게임 플레이 중인 조건) m_coroutine = StartCoroutine(SpawnRepeat());
}
private IEnumerator SpawnRepeat()
{
/* 랜덤 위치 장애물 생성 코드 생략 ... */
this.GetComponent<ObjectPool>().Pop().transform.position = m_posIdx[randPos1];
m_coroutine = null;
}
}
ObjectPool 에서 Pop()을 호출하여 오브젝트를 가져올 수 있다.
게임 실행 시 하이어라키에 오브젝트가 일괄 생성되는 것을 볼 수 있다.
생성된 오브젝트들은 사용할 땐 활성화 되고, 사용하지 않을 땐 비활성화 된다.
메모리에 오브젝트를 미리 할당해 놓기 때문에 사용되지 않는 오브젝트가 풀에 존재하는 경우 메모리 사용량이 증가할 수 있으며 초기 로딩 시간도 길어질 수 있다는 점도 유의하자.
'unity' 카테고리의 다른 글
플레이어 상태, 상태 머신 (0) | 2024.09.12 |
---|---|
2.5D 게임에서 캐릭터 이동 구현 (0) | 2024.08.28 |
싱글톤 패턴 (0) | 2024.08.22 |
WorldToScreenPoint, ScreenToWorldPoint (0) | 2024.08.20 |
Camera.main 오류 (CS1061) (0) | 2024.08.09 |