[Unity] Object Pool Pattern

AsiaticRicecake·2025년 4월 23일

이번에는 디자인 패턴 중에 Object Pool Pattern에 대해서 정리해보려고 합니다.

저번에 3가지 디자인 패턴을 알아보았는데 이번 패턴도 게임에서 상당히 많이 사용됩니다.

1. 📖 Object Pool Pattern

Object Pool Pattern은 자주 사용하는 오브젝트를 미리 생성해두고,
필요할 때 꺼내 쓰고 다시 반납하는 방식의 패턴입니다.

즉, 사용한 오브젝트를 재활용하는 기법이라고 생각하시면 됩니다.

오브젝트의 생성과 파괴가 반복적으로 일어날 경우에는
프레임 드랍이나 GC에 인한 메모리 단편화 증상이 생길 수 있다고 했었죠?

Object Pool Pattern은 단순한 활성화와 비활성화 작업이기 때문에
생성과 파괴보다 속도가 빠르고 메모리 단편화 증상을 예방할 수 있습니다.

하지만 미사용중에도 메모리 공간을 차지하고 있어서
필요한 크기와 양을 설정해서 사용하는 것 중요합니다.

1-1 🔖 Object Pool Pattern 예시

Object Pool Pattern이 상당히 많이 사용되는 부분은 총입니다.
FPS 게임의 경우 많은 탄환을 사용하기 때문에 프레임 드랍을 피하려면
Object Pool을 필수로 구현할 수 밖에 없습니다.

1-1-1 ✔️ Bullet

ReturnToPool을 만들어 반납하는 즉시 비활성화 시킵니다.

using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float speed = 10f;

    void Update()
    {
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }

    public void Init(Vector3 position)
    {
        transform.position = position;
        gameObject.SetActive(true);
    }

    public void ReturnToPool() //  총알을 다시 풀에 반납할 때 비활성화함.
    {
        gameObject.SetActive(false);
    }
}

1-1-2 ✔️ BulletPool

Object Pool Pattern은 보통 먼저 만들어진 오브젝트를 먼저 꺼내 쓰고,
사용 후 다시 뒤에 넣는 방식인 큐를 많이 사용합니다.
선입선출 방식이라 직관적이고 쉽기도 하고요.

물론 스택, 리스트, 배열로도 구현할 수 있습니다!

스택의 경우는 후입선출 구조로 사용한 오브젝트를 바로 다시 꺼낼 수 있어
짧은 시간 간격으로 재사용되는 경우에 좋겠죠.
아무래도 가장 최근에 반납한 총알을 다시 쓰는 게 빠르고 효율적이기 때문입니다.
예를 들면 Dodge 게임에서 스택을 사용하면 유리하겠죠

일단 밑에는 큐를 사용해서 구현한 코드입니다.

using System.Collections.Generic;
using UnityEngine;

public class BulletPool : MonoBehaviour
{
    public static BulletPool Instance; // 싱글톤으로 전역 접근이 가능
    public GameObject bulletPrefab;
    public int poolSize = 10; // 처음에 몇 개 생성할지 정하는 것

    private Queue<GameObject> pool = new Queue<GameObject>(); // 총알들을 보관(큐 사용)

    void Awake() // 시작하면 총알 프리팹을 poolSize만큼 미리 생성하고 비활성화한 뒤, 큐에 저장
    {
        Instance = this;

        for (int i = 0; i < poolSize; i++)
        {
            GameObject bullet = Instantiate(bulletPrefab);
            bullet.SetActive(false);
            pool.Enqueue(bullet);
        }
    }

    public GameObject GetBullet(Vector3 position)
    {
    	// 큐에 남아있는 게 있다면 꺼냄 (Dequeue()).
        GameObject bullet = pool.Count > 0 ? pool.Dequeue() : Instantiate(bulletPrefab);
        bullet.GetComponent<Bullet>().Init(position);
        return bullet;
    }

    public void ReturnBullet(GameObject bullet) // 총알을 다시 풀에 반납
    {
    	// ReturnToPool()로 비활성화
        bullet.GetComponent<Bullet>().ReturnToPool();
        // 큐에 다시 넣기 (Enqueue()).
        pool.Enqueue(bullet);
    }
}

1-1-3 ✔️ Gun

Gun은 총알을 발사하고 되돌리는 부분입니다.
코루틴을 시작하여 2초 기다린 후, 총알을 풀로 돌려보내는 거죠.

using UnityEngine;

public class Gun : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            GameObject bullet = BulletPool.Instance.GetBullet(transform.position);
            // StartCoroutine()으로 2초 뒤에 총알을 다시 풀로 돌려보냄.
            StartCoroutine(ReturnBulletAfterTime(bullet, 2f));
        }
    }

    private IEnumerator ReturnBulletAfterTime(GameObject bullet, float delay)
    {
    	// 코루틴으로 2초 기다린 후, 총알을 풀로 돌려보냄.
        yield return new WaitForSeconds(delay);
        BulletPool.Instance.ReturnBullet(bullet);
    }
}

0개의 댓글