새소식

💻 Programming (프로그래밍)/Unity | C#

[Unity][C#] 오브젝트 풀링(Object Pooling) 최적화 기법

  • -

🔔 유튜브 크리에이터 베르의 게임 개발 유튜브의 "오브젝트 풀링 구현하기 | 유니티" 를 보고 공부하여 작성한 게시글입니다! 🔔

 

오브젝트 풀링이란?

오브젝트가 많이 쌓이고, 제거가 된다면 가비지 컬렉터가 지나갈 때 많은 리소스 손실이 있을 수 있습니다.

따라서 리소스를 줄이기 위해, 오브젝트를 담아 놓을 수 있는 그릇을 만드는 최적화 기법입니다.

 

오브젝트 풀링을 만들면, 그 곳에서 오브젝트들을 꺼내고 쓰고를 반복하여 더욱 좋은 성능의 게임을 만들 수 있습니다.

 

이번에는 Bullet이라는 프리팹을 이용하는 경우만 만들어 보고, 이 후에는 다른 오브젝트도 넣을 수 있는 멀티 방법도 적용할 줄 알아야합니다.


🧷 1. 오브젝트 풀링

- Object Pool 스크립트

public class ObjectPool : MonoBehaviour
{
    public static ObjectPool Instance;

    [SerializeField]
    private GameObject poolingObjectPrefab;

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

    void Awake()
    {
        Instance = this;
        Initialize(10);
    }

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

    private void Initialize(int count)
    {
        for(int i = 0; i < count; i++)
        {
            poolingObjectQueue.Enqueue(CreateNewObject());
        }
    }
     
    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.transform.SetParent(null);
            newObj.gameObject.SetActive(true);
            return newObj;
        }
    }

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

}

public static ObjectPool 이라는 한가지 기능만 사용 하는 싱글톤 기법을 사용합니다.

 

이 후 컨테이너는 Queue를 이용해 줍니다.

 

Initialize() : count만큼 오브젝트를 미리 생성 해 넣어 줍니다.

GetObject() : Queue에서 가져와 주는 것, 있으면 그대로 가져오고, 없으면 새로 생성하여 가져옵니다.

ReturnObject() : 오브젝트를 Queue 다시 넣어줍니다.

CreateNewObject() : 새로운 오브젝트를 생성 해줍니다.


🧷 2. 적용 해 보기

- Player 스크립트

public class Shooter : MonoBehaviour
{
    [SerializeField]
    private GameObject bulletPrefab;
    private Camera mainCam;


    void Start()
    {
        mainCam = Camera.main;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            RaycastHit hitResult;
            if(Physics.Raycast(mainCam.ScreenPointToRay(Input.mousePosition), out hitResult))
            {
                var direction = new Vector3(hitResult.point.x, transform.position.y, hitResult.point.z) - transform.position;
                var bullet = ObjectPool.GetObject();// Instantiate(bulletPrefab, transform.position + direction.normalized, Quaternion.identity).GetComponent<Bullet>();
                bullet.transform.position = transform.position + direction.normalized;
                bullet.Shoot(direction.normalized);
            }
        }
    }
}

 

기존의 Bullet 생성 시의 Instantiate 함수 대신, ObjectPool.GetObject()를 통하여, 총알을 나눠주는 모습입니다.

 

- Bullet 스크립트

public class Bullet : MonoBehaviour
{
    private Vector3 direction;
    
    public void Shoot(Vector3 dir)
    {
        direction = dir;
        //Destroy(gameObject, 5f);
        Invoke("DestoryBullet", 5f);

    }

    private void DestoryBullet()
    {
        ObjectPool.ReturnObject(this);
    }
    void Update()
    {
        transform.Translate(direction);
    }
}

기존의 오브젝트 파괴인 Destory() 기능 대신, ReturnObject를 통하여 회수하는 모습을 보여줍니다.


출처: 베르의 게임 개발님 유튜브

https://www.youtube.com/watch?v=xiojw0lHzro 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.