Development/Unity Engine

[Retro유니티] Object Pooling으로 발판생성기 만들기 & 음악넣고 빌드하기

사이바 미도리 2024. 11. 29. 02:09

발판을 무한 반복 생성하여 배치하는 발판 생성기를 구현하자.

가장 간단한 방법은 매번 새로운 발판을 생성하는 것인데,

이 방법은 발판 오브젝트를 kill하지 않기 때문에, 메모리 사용량이 계속 증가하는 문제가 있다.

따라서 Object Pooling 방식으로 발판을 생성/관리하도록 하자.

 

Object Pooling은 Object를 미리 만들어서, Pool에 쌓아두는 방식이다.

 

Pool에 Object를 생성해두고, 필요하면 생성 대신, Pool의 Object를 가져다 쓴다.

필요 없다면 파괴하는 대신, 비활성화하고 Pool에 반환한다.

- Instantiate() 또는 Destroy() 메서드는 오브젝트를 실시간으로 생성/파괴하는데, 성능을 많이 먹으며 GC를 유발하기 쉽다. 이것은 게임 중 Freeze를 겪게 만든다.

 

기존 Object를 재활용하는 방식을 통해, 간단한 발판생성기를 완성하는 것이 목표이다.

 

오브젝트 풀링을 통한 발판 무한배치

- 3개의 발판을 미리 생성하자.

- 이 3개의 발판을 계속 로테이션돌린다.

- 각 발판은, ScrollingObject Component를 보유하게 하여 항상 왼쪽으로 이동하게 한다.

PlatformSpawner.cs 스크립트 작성

Start 시점에 미리 Instantiate를 호출한다.

    void Start() {
        // 변수들을 초기화하고 사용할 발판들을 미리 생성
        platforms = new GameObject[count];
        for(int i = 0; i < count; i++)
        {
            platforms[i] = Instantiate(platformPrefab, poolPosition, Quaternion.identity);
        }
        lastSpawnTime = 0f;
        timeBetSpawn = 0f;
    }

 

void Update() {
    // 순서를 돌아가며 주기적으로 발판을 배치
    if(GameManager.instance.isGameover) return;
    if(Time.time >= lastSpawnTime + timeBetSpawn)
    {
        lastSpawnTime = Time.time;
        timeBetSpawn = Random.Range(timeBetSpawnMin, timeBetSpawnMax);
        float yPos = Random.Range(yMin, yMax);
        platforms[currentIndex].SetActive(false);
        platforms[currentIndex].SetActive(true);
        platforms[currentIndex].transform.position = new Vector2(xPos, yPos);
        currentIndex++;
        if(currentIndex >= count) currentIndex = 0; // 마지막 순번이라면 순번리셋
    }
}

 

전체 스크립트

using UnityEngine;

// 발판을 생성하고 주기적으로 재배치하는 스크립트
public class PlatformSpawner : MonoBehaviour {
    public GameObject platformPrefab; // 생성할 발판의 원본 프리팹
    public int count = 3; // 생성할 발판의 개수

    public float timeBetSpawnMin = 1.25f; // 다음 배치까지의 시간 간격 최솟값
    public float timeBetSpawnMax = 2.25f; // 다음 배치까지의 시간 간격 최댓값
    private float timeBetSpawn; // 다음 배치까지의 시간 간격

    public float yMin = -3.5f; // 배치할 위치의 최소 y값
    public float yMax = 1.5f; // 배치할 위치의 최대 y값
    private float xPos = 20f; // 배치할 위치의 x 값

    private GameObject[] platforms; // 미리 생성한 발판들
    private int currentIndex = 0; // 사용할 현재 순번의 발판

    private Vector2 poolPosition = new Vector2(0, -25); // 초반에 생성된 발판들을 화면 밖에 숨겨둘 위치
    private float lastSpawnTime; // 마지막 배치 시점


    void Start() {
        // 변수들을 초기화하고 사용할 발판들을 미리 생성
        platforms = new GameObject[count];
        for(int i = 0; i < count; i++)
        {
            platforms[i] = Instantiate(platformPrefab, poolPosition, Quaternion.identity);
        }
        lastSpawnTime = 0f;
        timeBetSpawn = 0f;
    }

    void Update() {
        // 순서를 돌아가며 주기적으로 발판을 배치
        if(GameManager.instance.isGameover) return;
        if(Time.time >= lastSpawnTime + timeBetSpawn)
        {
            lastSpawnTime = Time.time;
            timeBetSpawn = Random.Range(timeBetSpawnMin, timeBetSpawnMax);
            float yPos = Random.Range(yMin, yMax);

            // 발판 보드젝트를 껐다켜서, 해당 발판 게임 오브젝트의 상태를 리셋한다.
            // Platform 스크립트는 발판의 상태를 리셋하고 무작위로 장애물 오브젝트를 활성화한다.
            platforms[currentIndex].SetActive(false);
            platforms[currentIndex].SetActive(true); 

            platforms[currentIndex].transform.position = new Vector2(xPos, yPos);
            currentIndex++;
            if(currentIndex >= count) currentIndex = 0; // 마지막 순번이라면 순번리셋
        }
    }
}

 

일단 Hierarchy상에서, Platform Spawner를 만들고, Prefabs를 넣는다.

 

플레이해보면, 랜덤발판생성이 구현되었다는 것을 알 수 있다.

Scene창을 보면, 발판을 돌려쓰는 것을 확인할 수 있다.

 

 

 

음악넣기

별것없다. 드래그 딸깍 하자.

 

버그가 있는것같은데,

Deadzone을 아래로 잡았음에도, 갑자기 뭐랑 Collision된건지 죽는다.

이건 추후 디버깅해야할 이슈로 남겨놓자.

 

 

빌드하기

동일하다.

 

 

배운것

- OnEnable() 메서드는, Component 활성화마다 매번 실행된다.

- Object Pooling이라는 방법이 있다.