Game Development, 게임개발

Object Pooling, 오브젝트 풀링, 디자인 패턴 [Game Development]

게임이 더 좋아 2021. 3. 26. 04:16
반응형
728x170

 

게임을 조금 더 개발자의 입장에서 용이하게 만들어주는 디자인 패턴인

오브젝트 풀링에 대해서 알아보자

 

**딱히 어떠한 게임엔진에 국한된 패턴은 아니다. 그냥 ~한 개념이다.

 


 

우선 오브젝트 풀링이란 것이 무엇인가?

 

오브젝트 풀링이란 오브젝트를 재활용하기 위한 디자인 패턴을 의미한다.

**여기서 재활용이란 정말 다시 활용하는 것을 의미한다.

 

예를 들자면 총알, bullet이 가장 대표적이다.

 

우리가 흔히 총을 쏠 때 버튼을 누르면 총알이 생기고 날아가게끔 만든다.

근데 미니건 쏜다고 생각해보자.

 

1분에 600발이었나 K-2가?  우습지

미니건 쏘면 1분에 몇천 발 쓸건데.. 그렇게 되면 몇천개의 총알을 만들어야 한다.

 

여기서 생각한 것이 ... 좀 아까운데..? 

아니 쏠 때마다 오브젝트 만들면... 몇천 발이 게임 안에 존재하는 상황이면 메모리 공간을 너무차지하는데..?

이게 맞는건가???

 

**또한 작은 크기의 메모리할당이 빈번이 할당되면 파편화(Framgentation)이 일어나서 메모리에 좋지 않다.

 

 

그래서 나온 것이 오브젝트 풀링이다.

 


 

어떻게 쓰느냐..?

 

총알을 오브젝트 풀링한다고 해보자

 

1. 총알을 미리 게임 안에다 미리 많이 만들어놓는다. 

** pool of bullet 을 만든다고 한다.

 

2. 총을 쏠 때마다 그 pool에서 가져온다.

 

3. pool에서 가져온 총알이 날라간다.

 

4. 총알이 무엇인가 맞췄다면 Destroy 대신 다시 pool에 집어넣는다.

 

 

다시 말하자면 

미리 만들어놓은 것을 쓰고

사용한 후에 것들도 다시 쓰자.

 

 

여기서 특징이 나온다.

 

**만약 pool의 크기가 작으면 총알이 날라가고 있는데.. 더 나갈 총알이 없어진다.

**만약 pool의 크기가 너무 커도 메모리 낭비가 된다.

 

이런 것은 경험에 의해서.. 잘 해결하거나

혹은 반대로 생각하면 제한을 걸 수도 있다는 것이다라고 생각하면 된다.

 

 

 


 

그렇다면 왜 쓸까???

 

정말 메모리만을 위해서 오브젝트 풀링을 쓰는 것일까?

 

90%가 맞다고 보면 된다.

제 1 목표는 효율성이다. 

게임 실행 중에, runtime 중에 수십개의 다른 오브젝트를 만드는 것은 정말 성능을 요하는 작업이다.

그래서 오브젝트 풀링으로 런타임에서는 그러한 불필요한 메모리 낭비를 하지 않도록 한다.

 

다만 오브젝트 풀링의 1번처럼 미리 pool을 만들어놓는 작업을 해야하기에

우리가 흔히 알고 있는 Loading 이라는 시간이 든다.

스테이지를 넘어가거나 화면이 전환되면 조금은 딜레이가 생기는데??

그게 로딩이고

그것이 바로 오브젝트 풀링을 하는 것이다.

 

그래서 오브젝트 풀링에 시간이 걸리더라도 

풀을 일단 만들어놓으면 우리는 풀에 있는 오브젝트들을 따로 만들지 않고도

큰 연산 없이도 바로바로 쓸 수 있기에 오브젝트 풀링을 한다.

 


 

유니티에서 직접 실행을 한다면...?

 

 

1. Create a pool of objects

 

우리는 에디터에서 재활용할 pool을 생성해야 한다.

 

 

보면 알겠지만 

게임오브젝트들이 엄청나게 만들어져 있다. 바로 재활용할 오브젝트들이다.

 

 

2. Object pooling logic

 

pool을 만들었으면 우리는 3가지 작업을 하게 만들어야 한다.

1. pool에서 꺼내는 작업

 

2. 꺼낸 것을 Scene에 나타내는 작업

 

3. 다시 pool에 넣어서 재활용할 수 있게하는 작업

 

using UnityEngine;
using System.Collections.Generic;
using System.Collections;
 
public class ObjectPoolingManager : MonoBehaviour {
 
    #region FIELDS
    public List objectToRecycle = new List(); // 게임에서 쓰일 오브젝트를 관리, 풀을 관리하는 리스트
    public Transform originlPoolPosition; // 우리가 쓴 오브젝트를 다시 pool에 넣을 때, 위치 초기화 총알은 총에서 나가야지?
    #endregion
     
    #region MONOBHEAVIOR
    void Start() {
        InvokeRepeating("Shot", 1.0f, 2f); //2초마다 총알을 쏨
    }
 
    private void Shot()
    {
        SetObjectFromPools(); // 오브젝트꺼내와서
        _selectedObject.transform.position = Vector3.zero; // 오브젝트 불러옴
        StartCoroutine(ResetTheObject()); // 다시 넣어
    }
 
    //풀에서 오브젝트를 가져오고 풀에서 꺼냈으니 풀에선 사라져야함.
    private void SetObjectFromPools()
    {
        _selectedObject = this.objectToRecycle[this.objectToRecycle.Count - 1]; // stack같이 pop을 한다. -> 꺼낸다.
        this.objectToRecycle.Remove(this.selectedObject); // pop은 꺼내는 것이지만 위에선 참조만 했으므로 실제로 값 제거.
    }
 
    // 재활용을 위한 pool에 다시 넣는 작업. 코루틴으로 작동함
    private IEnumerator ResetTheObject()
    {
        yield return new WaitForSeconds(1);
        _selectedObject.transform.position = originlPoolPosition.position; //넣기 전 위치를 초기화시킨다.(총알은 총에서 나가야지)
        this.objectToRecycle.Add(this.selectedObject); // pool에 넣는다. push를 한다.
    }
    #endregion
}

 

pool을 만든다면 inspector 창에서는 이렇게 보이겠지???

 

 

 

 이렇게 되면 쓸 수 있게 된다.

 

총알 말고도 다른 것에 모두 쓰일 수 있으니 유용하다고 할만 하다.

더 복잡해지는 것도 마찬가지다. 위의 개념 하에 조금 더 신경써야할 것만 늘어날 뿐이다.

게임을 조금 더 렉없이 할 수 있는 방법. 오브젝트 풀링이었다.

 

 


참고링크

 

gamedevelopertips.com/object-pooling-design-pattern-in-unity-c/

반응형
그리드형