본문 바로가기

종스크롤 슈팅게임(1942)

8. 오브젝트 풀링

지금까지는 필요한 오브젝트를 만들때 아래 두 함수를 사용했습니다.

Destroy(gameOjbect);
Instantiate(gameObject);

그러나 Destroy와 Instantiate는 오브젝트를 생성, 삭제하면서 가비지(Garbage : 쓰레기 값)를 만듭니다. 이 가비지가 쌓이면 유니티에서 가비지컬렉트(GC : 쌓인 조각난 메모리를 비우는 기술)를 실행하는데, 이때 게임이 끊기게됩니다. 이를 방지하기 위해 오브젝트 풀링(Object Pooling)이란 기술을 사용합니다.

 

* 오브젝트 풀링이란? : 미리 생성해둔 풀에서 활성화/비활성화로 사용

게임이 시작할때 미리 오브젝트들을 만들어놓고 SetActive로 활성화/비활성화만 조작해서 오브젝트를 재사용합니다. 기존에 한 오브젝트의 생명이 Instantiate(생성) -> Destroy(삭제)로 끝났다면, 오브젝트 풀링을 사용함으로써 생명주기가 생성 -> 활성화 -> 비활성화 -> 활성화 -> 비활성화.... 반복으로 바뀝니다. 쳇바퀴 구조라고 보면 됩니다.

 

먼저 OjbectManager 오브젝트와 스크립트를 생성합니다.

ObjectMnager 스크립트 & 오브젝트 생성

OjbectManager 스크립트에 코드 입력

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectManager : MonoBehaviour
{
    //--- Prefabs ---
    //Enemy
    public GameObject enemyLPrefab;
    public GameObject enemyMPrefab;
    public GameObject enemySPrefab;
    //Item
    public GameObject itemCoinPrefab;
    public GameObject itemPowerPrefab;
    public GameObject itemBoomPrefab;
    //Bullet
    public GameObject bulletPlayerAPrefab;
    public GameObject bulletPlayerBPrefab;
    public GameObject bulletEnemyAPrefab;
    public GameObject bulletEnemyBPrefab;

    //--- GameObjects ---
    //Enemy
    GameObject[] enemyL;
    GameObject[] enemyM;
    GameObject[] enemyS;
    //Item
    GameObject[] itemCoin;
    GameObject[] itemPower;
    GameObject[] itemBoom;
    //Bullet
    GameObject[] bulletPlayerA;
    GameObject[] bulletPlayerB;
    GameObject[] bulletEnemyA;
    GameObject[] bulletEnemyB;
    //GameObject for Copy 
    GameObject[] targetPool;
    void Awake()
    {
        //Enemy
        enemyL = new GameObject[20];
        enemyM = new GameObject[20];
        enemyS = new GameObject[20];
        //Item
        itemCoin = new GameObject[20];
        itemPower = new GameObject[20];
        itemBoom = new GameObject[20];
        //Bullet
        bulletPlayerA = new GameObject[20];
        bulletPlayerB = new GameObject[20];
        bulletEnemyA = new GameObject[20];
        bulletEnemyB = new GameObject[20];

        generate();
    
    }

    void generate() {
        //Enemy
        for (int i = 0; i < enemyL.Length; i++) {
            enemyL[i] = Instantiate(enemyLPrefab);
            enemyL[i].SetActive(false);
        }
        for (int i = 0; i < enemyM.Length; i++)
        {
            enemyM[i] = Instantiate(enemyMPrefab);
            enemyM[i].SetActive(false);
        }
        for (int i = 0; i < enemyS.Length; i++)
        {
            enemyS[i] = Instantiate(enemySPrefab);
            enemyS[i].SetActive(false);
        }
        //Item
        for (int i = 0; i < itemCoin.Length; i++)
        {
            itemCoin[i] = Instantiate(itemCoinPrefab);
            itemCoin[i].SetActive(false);
        }
        for (int i = 0; i < itemPower.Length; i++)
        {
            itemPower[i] = Instantiate(itemPowerPrefab);
            itemPower[i].SetActive(false);
        }
        for (int i = 0; i < itemBoom.Length; i++)
        {
            itemBoom[i] = Instantiate(itemBoomPrefab);
            itemBoom[i].SetActive(false);
        }
        //Bullet
        for (int i = 0; i < bulletPlayerA.Length; i++)
        {
            bulletPlayerA[i] = Instantiate(bulletPlayerAPrefab);
            bulletPlayerA[i].SetActive(false);
        }
        for (int i = 0; i < bulletPlayerB.Length; i++)
        {
            bulletPlayerB[i] = Instantiate(bulletPlayerBPrefab);
            bulletPlayerB[i].SetActive(false);
        }
        for (int i = 0; i < bulletEnemyA.Length; i++)
        {
            bulletEnemyA[i] = Instantiate(bulletEnemyAPrefab);
            bulletEnemyA[i].SetActive(false);
        }
        for (int i = 0; i < bulletEnemyB.Length; i++)
        {
            bulletEnemyB[i] = Instantiate(bulletEnemyBPrefab);
            bulletEnemyB[i].SetActive(false);
        }
    }

    public GameObject makeObj(string type) {
        switch (type) {
            case "EnemyL":
                targetPool = enemyL;
                break;
            case "EnemyM":
                targetPool = enemyM;
                break;
            case "EnemyS":
                targetPool = enemyS;
                break;
            case "ItemCoin":
                targetPool = itemCoin;
                break;
            case "ItemPower":
                targetPool = itemPower;
                break;
            case "ItemBoom":
                targetPool = itemBoom;
                break;
            case "BulletPlayerA":
                targetPool = bulletPlayerA;
                break;
            case "BulletPlayerB":
                targetPool = bulletPlayerB;
                break;
            case "BulletEnemyA":
                targetPool = bulletEnemyA;
                break;
            case "BulletEnemyB":
                targetPool = bulletEnemyB;
                break;
        }
        for (int i = 0; i < targetPool.Length; i++)
        {
            if (!targetPool[i].activeSelf)
            {
                targetPool[i].SetActive(true);
                return targetPool[i];
            }
        }
        return null;
    }

    public GameObject[] getPool(string type) {
        switch (type)
        {
            case "EnemyL":
                targetPool = enemyL;
                break;
            case "EnemyM":
                targetPool = enemyM;
                break;
            case "EnemyS":
                targetPool = enemyS;
                break;
            case "ItemCoin":
                targetPool = itemCoin;
                break;
            case "ItemPower":
                targetPool = itemPower;
                break;
            case "ItemBoom":
                targetPool = itemBoom;
                break;
            case "BulletPlayerA":
                targetPool = bulletPlayerA;
                break;
            case "BulletPlayerB":
                targetPool = bulletPlayerB;
                break;
            case "BulletEnemyA":
                targetPool = bulletEnemyA;
                break;
            case "BulletEnemyB":
                targetPool = bulletEnemyB;
                break;
        }
        return targetPool;
    }
}

ObjectManager 오브젝트에 스크립트를 할당하고 맵핑합니다.

프리펩 드래그 앤 드랍

실행하면 아래처럼 오브젝트들이 만들어져 비활성화로 게임을 시작하게 됩니다. 

비활성화 된 상태로 대기중인 오브젝트들

이제 이 만들어놓은 오브젝트를 이용하기 위해선, 기존에 Instantiate, Destroy 코드를 바꿔야합니다.

Instantiate는 maekObj, Destroy는 SetActive(false)를 사용합니다.

 

Destroy 예)

Item 스크립트

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "BorderBullet") {
            gameObject.SetActive(false);
            //Destroy(gameObject);
        }
    }

 

Instantiate 예)

Player 스크립트

    void fire() {
        if (!Input.GetButton("Fire1")) {
            return;
        }
        if (curShotDelay < maxShotDelay) {
            return;
        }
        switch (power) {
            case 1:
                GameObject bullet = objectManager.makeObj("BulletPlayerA");
                bullet.transform.position = transform.position + new Vector3(0, 0.7f, 0);
                //Instantiate(bulletObjA, transform.position + new Vector3(0, 0.7f, 0), transform.rotation);
				...
            case 2:
                GameObject bulletR = objectManager.makeObj("BulletPlayerA");
                bulletR.transform.position = transform.position + new Vector3(0.1f, 0.7f, 0);
                //(bulletObjA, transform.position + new Vector3(0.1f, 0.7f, 0), transform.rotation);

                GameObject bulletL = objectManager.makeObj("BulletPlayerA");
                bulletL.transform.position = transform.position + new Vector3(-0.1f, 0.7f, 0);
                //Instantiate(bulletObjA, transform.position + new Vector3(-0.1f, 0.7f, 0), transform.rotation);
				...
            case 3:
                GameObject bulletRR = objectManager.makeObj("BulletPlayerA");
                bulletRR.transform.position = transform.position + new Vector3(0.3f, 0.7f, 0);
                //Instantiate(bulletObjA, transform.position + new Vector3(0.3f, 0.7f, 0), transform.rotation);

                GameObject bulletLL = objectManager.makeObj("BulletPlayerA");
                bulletLL.transform.position = transform.position + new Vector3(-0.3f, 0.7f, 0);
                //Instantiate(bulletObjA, transform.position + new Vector3(-0.3f, 0.7f, 0), transform.rotation);

                GameObject bulletCC = objectManager.makeObj("BulletPlayerB");
                bulletCC.transform.position = transform.position + new Vector3(0, 0.7f, 0);
                //Instantiate(bulletObjB, transform.position + new Vector3(0, 0.7f, 0), transform.rotation);
				...
        }

        curShotDelay = 0;
    }