오브젝트 풀링을 이용한 SpawnManager

devKyoun·2024년 10월 30일
0

Unity

목록 보기
20/27

🧐오브젝트 풀링

오브젝트 풀링.
간단히 설명하자면 Spawn을 이미 한 Object들을 재사용 하는 것이다.

왜 오브젝트 풀링을 사용해야 하는가?
오브젝트들을 Instantiate, Destroy 하는 작업들이 GC 발생을 일으키기 때문.

다수의 오브젝트들을 생성, 파괴를 한다면 그 GC의 작업량은 더욱 많이 생긴다.
그래서, 오브젝트 풀링을 사용해주면 좋다.


SpawnManager.cs

Queue 세팅

세팅 설명에 앞서서 게임 도중에 생성되는 오브젝트(장애물)들은
좀비, 나무, 바위, 통(폭발) 이 있다.

각각 id는 순번대로 0, 1, 2, 3이다.

해당 4개 타입의 오브젝트를 Queue 4개로 관리해주고, 이를 또 Dictionary를 이용해 효율적으로 관리를 해주려고 한다.

그래서 Dictionary QueueSet< Queue> 에다가 해당 타입 4개의 Queue를 연결해줬다.

private Dictionary<int, Queue<GameObject>> QueueSet = new Dictionary<int, Queue<GameObject>>();
  
void Start()
{
  	QueueSet.Add(0, jombies);
	QueueSet.Add(1, trees);
	QueueSet.Add(2, rocks);
	QueueSet.Add(3, barrels);
}

Spawn 조건

랜덤으로 오브젝트들을 Spawn 해주는데 Spawn이 되는 상황은 무조건 일정해야한다.
장애물에 부딪힐때 Player 속도는 늦어져서 시간으로 하면 다소 불합리한 게임상황이 생긴다.

그래서, Player의 이동거리가 설정한 거리 이상 움직였을때 Spawn을 해주게끔 코드를 짰다.

private void Update()
{
    // 저장된 van 위치로부터 일정 거리 벗어나면
    if (vanTransform != null && vanTransform.position.z - saveVanPosition.z >= spawnTriggerDistance)
    {
        spawnId = Random.Range(0, 20);
        Spawn(spawnId % 4);
        saveVanPosition = vanTransform.position;
    }
}

이렇게 하면 Player 속도가 늦어지든 빨라지든 일정 간격으로 장애물들이 생성된다.

일정 간격으로 생성하는 이유는 이러한 게임이기 때문.

Spawn Method

    public void Spawn(int id)
    {


        // 만들어진 오브젝트가 없거나 비었다면
        if (QueueSet.TryGetValue(id, out Queue<GameObject> queue))
        {
            if (queue.Count == 0)
            {
                // 오브젝트 생성
                GameObject obj = Instantiate(prefabs[id], SetObstaclePosition(), vanTransform.rotation);
                if(obj.TryGetComponent<ObstacleManager>(out ObstacleManager obstacleManager))
                {
                    obstacleManager.Initialization(this);
                }
                
            }
            else
            {
                Debug.Log("Dequeue");

                GameObject obj = queue.Dequeue();
                obj.SetActive(true);
                obj.transform.position = SetObstaclePosition();
            }
        }

    }

id를 전달을 받으면 QueueSet Dictionary 기능을 통해 어떠한 Queue 타입을 가져와야 하는지 찾아낸다.

그 후, 만약 해당 Queue에서 아무 오브젝트도 대기가 돼 있지 않다면은 Instantiate로 생성해주고
Queue에 비활성화 된 오브젝트 ( 전에 생성됐었고 어떠한 조건으로 비활성화 ) 가 있다면 DeQueue를 해주어서 다시 활성화 시키고 재사용한다.

비활성화 코드

    public void DeActivateObject(GameObject obj)
    {

        if (obj.TryGetComponent<ObstacleManager>(out ObstacleManager obstacleManager))
        {
            int id = obstacleManager.Id;

            if (QueueSet.TryGetValue(obstacleManager.Id, out Queue<GameObject> queue))
            {
                

                queue.Enqueue(obj);
            }
            obj.SetActive(false);
        }

    }

비활성화가 되는 기준은 활성화된 오브젝트 ( 좀비, 나무, 바위, 통 ) 에 Player가 부딪히거나 화면 바깥으로 벗어났을때이다.

해당 생성되는 오브젝트들은 생성됐을때 부터 ObstacleManager.cs 라는 스크립트가 부착 돼 있다.

장애물 오브젝트가 비활성화가 되기 위해서 ObstacleManager.cs 스크립트에서
SpawnManager.cs 의 Deactivate() 함수를 호출한다.
그러면 해당 오브젝트 타입의 Queue를 다시 찾아오고, 그 다음 해당 오브젝트를 비활성화 한 뒤 매칭이 된 Queue에 삽입해준다. ( 재사용을 할 준비가 된 것 )

ObstacleManager.cs

화면 바깥으로 벗어나는 경우

사실 여기서 Renderer 기능을 이용해 OnBecameInVisible() 내장 함수를 사용하려했으나,
오브젝트 풀링을 사용하다보니까 OnBecameInVisible이 재사용 됐을때 실행되지 않는다.
그래서, 성능에 영향이 안가는 최대한의 방법을 생각해봤는데
직진을 하는 게임이기때문에 Vector3.Distance를 사용 할 필요도 없이 z 값만 비교를 하면 되는 부분이였다.

    private void Update()
    {
        if (spawnManager.VanTransform != null && 
  				spawnManager.VanTransform.position.z - transform.position.z > disappearOffSetZ)
        {
            spawnManager.DeActivateObject(this.gameObject);
        }
    }

VanTransform(Player)의 z가 해당 Obstacle Postion z보다 크면 앞서나가있다는 것이다.
일정 거리가 지나가면 DeActivate를 해준다.

오브젝트가 Player와 충돌한 경우

    private void OnTriggerEnter(Collider other)
    {
        // Van과 충돌 시에
        if (other.gameObject.CompareTag("Player"))
        {

            if (other.TryGetComponent<VanController>(out VanController vanController))
            {
				// 파괴되는 오브젝트 ID와 ScriptableObject 시스템을 통해 효과 생성
                Instantiate(obstacleData.GetEffects(id), transform.position, transform.rotation);

                //obstacleData.SetEffectValue(id, ref sideChangeValue, ref straightChangeValue);
                vanController.HitObstacle(id , obstacleData);


            }

            spawnManager.DeActivateObject(this.gameObject);
        }
    }

비활성화 된 것들을 DeQueue 과정을 통해 재사용하는 모습

profile
Game Developer

0개의 댓글