EasyCastleUNITY

드래곤 플라이트 모작 개발일지6 (드래곤 오브젝트 풀링 및 웨이브 만들기) 본문

2D 프로젝트 개발 일지(드래곤 플라이트 모작)

드래곤 플라이트 모작 개발일지6 (드래곤 오브젝트 풀링 및 웨이브 만들기)

EasyCastleT 2023. 9. 14. 17:43

여태까지는 드래곤을 미리 씬에 배치해두고, 처리를 하고 있었습니다.

드래곤도 계속 나와야 하는 만큼, 그때 그때, 생성하면 성능저하로 이어질 수 있습니다. 

그래서 드래곤도 총알처럼 오브젝트 풀링을 하려고 합니다. 

드래곤 오브젝트 풀링

총알을 오브젝트 풀링 했을 때와는 구조적으로 동일합니다.

DragonPoolManager

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

public class DragonPoolManager : MonoBehaviour
{
    //화이트 드래곤을 미리 생성해 저장할 리스트 
    [SerializeField] private List<GameObject> whitePool = new List<GameObject>();
    //골드 드래곤을 미리 생성해 저장할 리스트 
    [SerializeField] private List<GameObject> goldPool = new List<GameObject>();
    //오브젝트 풀에 생성할 드래곤의 최대 수 
    [SerializeField] private int maxDragons = 10;
    //싱글톤 인스턴스 선언
    public static DragonPoolManager instance=null;
    //드래곤 프리팹들 
    [SerializeField] private GameObject[] dragonPrefabs; //0이 화이트, 1이 골드 

    public List<GameObject> WhitePool { get => whitePool;  }
    public List<GameObject> GoldPool { get => goldPool;  }

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else if (instance != null)
        {
            Destroy(this.gameObject);
        }
        // 아직 게임 씬만 구성하지만
        // 추후에 새로운 씬도 추가할 수 있도록 사용 
        DontDestroyOnLoad(gameObject);
    }
    // Start is called before the first frame update
    void Start()
    {
        this.CreateDragonPool();
    }

    private void CreateDragonPool()
    {
        for(int i=0; i<maxDragons; i++)
        {
            GameObject whiteGo = Instantiate(this.dragonPrefabs[0]);
            whiteGo.SetActive(false);
            whiteGo.transform.SetParent(this.transform);
            whitePool.Add(whiteGo);

            GameObject goldGo = Instantiate(this.dragonPrefabs[1]);
            goldGo.SetActive(false);
            goldGo.transform.SetParent(this.transform);
            goldPool.Add(goldGo);
        }
    }
    //풀에서 드래곤 받아가기 
    public GameObject GetWhiteDragonInPool()
    {
        foreach(var white in this.whitePool)
        {
            if(white.activeSelf == false)
            {
                return white;
            }
        }
        return null;
    }

    public GameObject GetGoldDragonInPool()
    {
        foreach (var gold in this.goldPool)
        {
            if (gold.activeSelf == false)
            {
                return gold;
            }
        }
        return null;
    }
    //돌아오기 
    public void ReleaseDragon(GameObject dragonGo)
    {
        dragonGo.SetActive(false);
        dragonGo.transform.SetParent(this.transform);
    }
}

DragonGenerator

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

public class DragonGenerator : MonoBehaviour
{

    [SerializeField] private Transform[] dragonInitPoses; //드래곤 생성 위치

    private float elapsedTime = 0.0f; //경과시간
    [SerializeField] private float intervelTime = 0.3f;
    [SerializeField] private GameObject[] dragonPrefabs; //0이 화이트, 1이 골드 

    public void SummonDragon()
    {
        this.elapsedTime += Time.deltaTime;
        if(this.elapsedTime > intervelTime)
        {
            this.CallDragon();
            this.elapsedTime = 0.0f;
        }
       
    }

    private void CallDragon()
    {
        //5번 부름 
        for(int i=0; i<dragonInitPoses.Length; i++)
        {
            GameObject go;
            int rnd = Random.Range(1, 11);
            if (rnd > 7)
            {
                go = DragonPoolManager.instance.GetGoldDragonInPool();
                if (go == null) //이미 만들어둔 드래곤을 다 사용하면 
                {
                    go = Instantiate(this.dragonPrefabs[1]);
                    DragonPoolManager.instance.GoldPool.Add(go);
                    go.transform.position = dragonInitPoses[i].position;
                }
                else if (go != null)
                {
                    go.transform.SetParent(null); //매니저 밖으로 나옴
                    go.transform.position = dragonInitPoses[i].position;
                    go.SetActive(true); // false였으니 true로 변환
                }
            }
            else
            {
                go = DragonPoolManager.instance.GetWhiteDragonInPool();
                if (go == null) //이미 만들어둔 드래곤을 다 사용하면 
                {
                    go = Instantiate(this.dragonPrefabs[0]);
                    DragonPoolManager.instance.WhitePool.Add(go);
                    go.transform.position = dragonInitPoses[i].position;
                }
                else if (go != null)
                {
                    go.transform.SetParent(null); //매니저 밖으로 나옴
                    go.transform.position = dragonInitPoses[i].position;
                    go.SetActive(true); // false였으니 true로 변환
                }
            }
            
        }
        
        

       
    }
}

PoolManager에서 생성하고, Generator에서 생성된 드래곤들을 받아와 소환합니다. 

Generator의 CallDragon에서 소환위치를 순차적으로 불러와 드래곤을 소환하는데, 

랜덤을 통해, 한마리가 소환될때 마다 70프로의 확률로 흰 드래곤이 소환되고, 30프로의 확률로 금색 드래곤이 소환되게 만들었습니다. 

웨이브 만들기

드래곤 소환위치

소환위치를 이렇게 미리 씬에 만들어서 등록해두었습니다. 

그리고 Generator의 CallDragon에서 for문을 통해 드래곤들의 소환위치를 지정해 줍니다.

private void CallDragon()
    {
        //5번 부름 
        for(int i=0; i<dragonInitPoses.Length; i++)
        {
            GameObject go;
            int rnd = Random.Range(1, 11);
            if (rnd > 7)
            {
                go = DragonPoolManager.instance.GetGoldDragonInPool();
                if (go == null) //이미 만들어둔 드래곤을 다 사용하면 
                {
                    go = Instantiate(this.dragonPrefabs[1]);
                    DragonPoolManager.instance.GoldPool.Add(go);
                    go.transform.position = dragonInitPoses[i].position;
                }
                else if (go != null)
                {
                    go.transform.SetParent(null); //매니저 밖으로 나옴
                    go.transform.position = dragonInitPoses[i].position;
                    go.SetActive(true); // false였으니 true로 변환
                }
            }
            else
            {
                go = DragonPoolManager.instance.GetWhiteDragonInPool();
                if (go == null) //이미 만들어둔 드래곤을 다 사용하면 
                {
                    go = Instantiate(this.dragonPrefabs[0]);
                    DragonPoolManager.instance.WhitePool.Add(go);
                    go.transform.position = dragonInitPoses[i].position;
                }
                else if (go != null)
                {
                    go.transform.SetParent(null); //매니저 밖으로 나옴
                    go.transform.position = dragonInitPoses[i].position;
                    go.SetActive(true); // false였으니 true로 변환
                }
            }
        } 
    }

그래서 위의 영상처럼 드래곤들이 일렬로 내려오게 됩니다. 

 

그런데 문제점이 있습니다. 

현재 드래곤이 죽으면, 그 대리자를 Main으로 넘겨 그에 걸맞는 이펙트들이 나오게 하고 있습니다. 

하지만, 이 대리자가 등록된 곳이 Main에 Start이기에 Main에 Start가 실행되는 시점에, 

Main 드래곤 리스트에 들어 있는 드래곤 개체에게만 대리자가 연결이 된다는 점입니다. 

 

즉, 미리 만들어둔 드래곤이 부족하여, 새로 만드는 경우 대리자가 연결이 되지 않아

NullReferenceException 오류가 발생하게 됩니다. 

오류가 발생하는 모습

이 오류를 해결해보려고 하겠습니다.