[Unity] 유니티 오브젝트풀 패턴 구현 1 ( ObjectPool Pattern )

TNT·2023년 11월 25일
0

유니티 디자인패턴

목록 보기
6/14
post-thumbnail

오브젝트 풀 패턴

게임 패턴 중에 유명한 패턴 중 하나이다.
게임들 하다 보면 오브젝트나 이펙트 같은 걸 로드하고 삭제하고 하는데
이때마다 시스템 적으로 메모리에서 삭제하거나 생성하는 것보단 잠시 숨겨두고 있다가 써야 할 때 보여주는 것이 좋다.

만약에 게임하고 있는데 이런 로드나 삭제하는 것 때문에 게임이 잠깐 멈추고 다시 움직이고 하면 불편하니까
그 부분을 최소화한다고 보면 될 거 같다.

이번 디자인 패턴은 유니티에서 공식으로 지원도 해주고 있기도 하다.
유니티에서 지원해 주는 오브젝트풀이 궁금하면
https://docs.unity3d.com/ScriptReference/Pool.ObjectPool_1.html
여기로 가서 확인해 보자.

개념

오브젝트 풀 패턴의 개념은 각각 필요한 오브젝트들은 초기화시킨 후 메모리에 미리 적재시켜 두고
필요할 때마다 불러와서 사용 후 사용이 종료되면 다시 넣어둔다라고 생각하면 된다.

(오브젝트 풀 이미지)

오프젝트풀 사용용도
그럼 이 오브젝트 풀을 왜 사용하냐면 필요한 오브젝트 들을 미리 불러둔 후 초기화해서 사용하고
다 사용하고 나면 다시 정리해 둔 뒤 필요할 때 다시 초기화해서 사용하기에 이 성능이 좋아서 사용하는 것이다.
그러면 어떨 때 사용하냐
예시로 보스 몬스터처럼 소환되어야 하는 경우는 손해이다. 왜냐하면 재사용될 가능성도 낮고 한번 나오고 끝이라서
대기하는 동안 메모리를 잡아먹고 있기 때문에 오히려 낭비만 하고 있고 스킬이나 이펙트 같은 것은 여러 번 나오고
꾸준하게 다시 사용해야 하기 때문에 사용하면 좋다.

오브젝트풀 패턴에서 나오는 가장 중요한 부분이 재사용성이다. 자주 사용해야 하면 써주는 게 좋다.

구현

기본적으로 오프젝트 풀은 재사용성이 높은 오브젝트를 사용하기에
이번엔 디펜스 같은 게임에 총알을 많이 사용하기에 이번엔 총알을 스폰하고 다시 돌려받는 오브젝트 풀을 만들겠다.

먼저 사용할 총알을 만들어주자.

Bullet.cs

using UnityEngine;

public class Bullet : MonoBehaviour
{
    private Vector3 direction;
    public void Shoot(Vector3 direction)
    {
        this.direction = direction;
        Invoke("DestroyBullet", 5f);
    }

    public void DestroyBullet()
    {
        ObjectPool.ReturnObject(this);
    }

    void Update()
    {
        transform.Translate(direction);
    }
}

소환하고 지정해 준 방향으로 날아가고 5초 뒤에 삭제하는 코드이다.
여기에서는 충돌 관련 작업은 따로 안 하겠습니다.

이제 오브젝트 풀을 작성해 보자.

ObjectPool.cs

using System.Collections.Generic;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
    public static ObjectPool Instance;

    [SerializeField]
    private GameObject poolingObj;

    Queue<Bullet> poolingObjectQueue = new Queue<Bullet>();

    private void Awake()
    {
        Instance = this;

        Initialize(10);
    }

    private void Initialize(int initCount)
    {
        for (int i = 0; i < initCount; i++)
        {
            poolingObjectQueue.Enqueue(CreateNewObject());
        }
    }

    private Bullet CreateNewObject()
    {
        var newObj = Instantiate(poolingObj).GetComponent<Bullet>();
        newObj.gameObject.SetActive(false);
        newObj.transform.SetParent(transform);
        return newObj;
    }

    public static Bullet GetObject()
    {
        if (Instance.poolingObjectQueue.Count > 0)
        {
            var obj = Instance.poolingObjectQueue.Dequeue();
            obj.transform.SetParent(null);
            obj.gameObject.SetActive(true);
            return obj;
        }
        else
        {
            var newObj = Instance.CreateNewObject();
            newObj.gameObject.SetActive(true);
            newObj.transform.SetParent(null);
            return newObj;
        }
    }

    public static void ReturnObject(Bullet obj)
    {
        obj.gameObject.SetActive(false);
        obj.transform.SetParent(Instance.transform);
        Instance.poolingObjectQueue.Enqueue(obj);
    }
}

Bullet 오브젝트를 생성하고 주고 반환받고 저장하는 코드이다.

이제 이걸 소환할 플레이어를 만들자.

TestPlayer.cs

using UnityEngine;

public class TestPlayer : MonoBehaviour
{
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var bullet = ObjectPool.GetObject(); 
            var direction = new Vector3(transform.position.x, transform.position.y, transform.position.z) - transform.forward;
            bullet.transform.position = direction.normalized;
            bullet.Shoot(direction.normalized);
        }
    }

}

오브젝트 풀에서 Get받고 그걸 그대로 앞에 방향으로 발사하는 코드이다.

그러면 이제 유니티로 가서

빈 오브젝트 하나 두고 TestPlayer.cs를 넣는다.

이후 새로 오브젝트 하나 만들고 여기엔 ObjectPool.cs를 넣는다
마지막으로 Sphere오브젝트하나 만들고 Bullet.cs를 넣고 프리펩으로 저장한다.
거기 안에 PoolingObj에 프리펩을 넣어준다.

실행 결과


마우스 누를 때마다 Sphere오브젝트를 오브젝트풀에서 받아와서 사용하고 다시 돌려주는 걸 확인할 수 있다.

근데 여기서 만약에 여러 개 오브젝트를 사용하고 싶다면 코드를 수정해야 하는데.
다음 포스트에서 여러 개 오브젝트를 나눠서 사용하는 거로 알아보자.

profile
개발

0개의 댓글