Unity의 ObjectPool
Coddy Unity 여정의 Unity 게임 아키텍처 섹션에 포함된 레슨. 64개 중 48번째.
Unity에는 UnityEngine.Pool의 풀인 ObjectPool<T>이 포함되어 있습니다. 네 개의 람다로 각 단계에서 무엇을 할지 지정하면 풀이 여분 객체와 개수를 관리합니다. 이 굴뚝은 0.2초마다 연기를 내뿜고, 연기 한 조각은 1초 뒤에 돌아갑니다:
using UnityEngine;
using UnityEngine.Pool;
public class Chimney : MonoBehaviour
{
public Puff puffPrefab;
private ObjectPool<Puff> pool;
private float timer;
void Awake()
{
pool = new ObjectPool<Puff>(
createFunc: () => Instantiate(puffPrefab),
actionOnGet: puff => puff.gameObject.SetActive(true),
actionOnRelease: puff => puff.gameObject.SetActive(false),
actionOnDestroy: puff => Destroy(puff.gameObject),
collectionCheck: true,
defaultCapacity: 10,
maxSize: 20);
}
void Update()
{
timer += Time.deltaTime;
if (timer >= 0.2f)
{
timer = 0f;
Puff puff = pool.Get();
puff.transform.position = transform.position + Vector3.up;
puff.Launch(pool);
}
}
}// Puff.cs
private IObjectPool<Puff> pool;
private float age;
public void Launch(IObjectPool<Puff> owner)
{
pool = owner;
age = 0f;
}
void Update()
{
transform.Translate(0f, Time.deltaTime, 0f);
age += Time.deltaTime;
if (age >= 1f) pool.Release(this);
}createFunc는 Get을 호출했을 때 풀이 비어 있으면 실행됩니다. actionOnGet은 Get을 호출할 때마다, actionOnRelease는 Release를 호출할 때마다 실행됩니다. 여분 객체가 이미 maxSize개 대기 중일 때 연기 객체를 반환하면 풀은 그 객체를 보관하지 않고 actionOnDestroy로 삭제합니다. defaultCapacity는 풀 목록의 초기 용량일 뿐이므로 미리 생성되는 객체는 없습니다. CountAll은 풀이 생성해 여전히 보유하고 있는 연기 객체의 수를, CountActive는 게임에서 사용 중인 객체의 수를, CountInactive는 여분 객체의 수를 셉니다. Get은 굴뚝이 연기 객체의 위치를 설정하기 전에 객체를 활성화하는데, 여기서는 Puff에 위치를 읽는 OnEnable이 없으므로 괜찮습니다. 연기 객체는 모든 Unity 풀이 구현하는 인터페이스인 IObjectPool<Puff>로 풀을 보유하며, Launch는 사용할 때마다 객체의 나이를 초기화합니다.
collectionCheck를 켜면 이미 풀에 들어 있는 연기 객체를 반환할 때 InvalidOperationException이 발생합니다: Trying to release an object that has already been released to the pool. 이 검사는 Release가 호출될 때마다 여분 객체를 검색하기 때문에 Editor에서만 실행됩니다. 빌드에서는 연기 객체가 풀에 두 번 들어가고, Get 두 번으로 같은 객체가 전달될 수 있습니다. 흔한 원인은 반환된 뒤에도 계속 실행되어 다시 자신을 반환하는 객체이며, 그래서 actionOnRelease가 객체를 비활성화합니다.
챌린지
중급Unity의 풀에서 불꽃을 터뜨려 보세요.
Launcher의Awake에서ObjectPool<Spark>을 만드세요.Instantiate(sparkPrefab)으로 생성하고, 가져올 때 활성화하고, 반환할 때 비활성화하고, 놓을 때 객체를 파괴하며,maxSize: 5로 설정하세요.Update에서Instantiate대신pool.Get()으로 각 불꽃을 가져오세요.Return에서Destroy대신pool.Release(spark)를 호출하세요.
‘플레이’를 눌러 불꽃을 몇 번 터뜨려 보세요. 그런 다음 ‘확인’을 누르세요.
직접 해보기
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;
public class Launcher : MonoBehaviour
{
public Spark sparkPrefab;
public int sparks = 8;
void Awake()
{
// 풀을 만듭니다: spark를 생성하고, 가져오고, 반환하고, 파괴하며, 여분의 spark는 최대 5개로 유지합니다
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
for (int i = 0; i < sparks; i++)
{
Spark spark = Instantiate(sparkPrefab);
spark.transform.position = transform.position + Vector3.up;
spark.Launch(Direction(i), this);
}
}
}
// 수명이 다한 spark
public void Return(Spark spark)
{
Destroy(spark.gameObject);
}
// launcher를 중심으로 바깥쪽과 위쪽을 향해 고르게 퍼지도록 합니다
Vector3 Direction(int i)
{
return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
}
}
Unity 게임 아키텍처의 모든 레슨
직접 연습해 보세요: Unity 플레이그라운드