private void DestroyAllEnemies()
    {
        ObjectManager.PoolType[] enemyTypes =
        {
            ObjectManager.PoolType.EnemyMini1, ObjectManager.PoolType.EnemyElite1, ObjectManager.PoolType.EnemyElite2
        }; // 적 종류
        
        foreach (var type in enemyTypes)
        { 
            GameObject[] enemies = ObjectManager.Instance.GetObjects(type); // 해당 타입에 해당되는 enemy 모두 가져옴

            foreach (var enemy in enemies) // 배열로 가져온 enemy 각 개체에 접근 
            {
                if (enemy.activeSelf)
                {
                    enemy.GetComponent<EnemyHealth>()?.TakeDamaged(int.MaxValue);
                }
            }
        }

 

아이템 사용(필살기) 시 GetActiveObjects 메서드를 통해 적 type을 가져오고

 

무조건 적이 제거될 수 있도록 int.MaxValue를 사용했다.

 

근데, 아이템을 사용해도 적이 제거되지 않는 문제가 발생

 

기존 오브젝트 풀링 매니저는 비활성화된 오브젝트만 저장하고 있어 제거가 되지 않았다.

 

 딕셔너리에 List<GameObject> ActiveObject를 추가하여 활성화된 오브젝트를 가져오는 방식으로 해결하였다.

public class ObjectManager : MonoBehaviour
{
    public static ObjectManager Instance { get; private set; }

    ... 생략

    private Dictionary<PoolType, Queue<GameObject>> PoolDictionary = new Dictionary<PoolType, Queue<GameObject>>();
    private Dictionary<PoolType, List<GameObject>> ActiveObjects = new Dictionary<PoolType, List<GameObject>>(); //  추가

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject);
        }

        foreach (var pool in PoolList)
        {
            Queue<GameObject> queue = new Queue<GameObject>();
            List<GameObject> activeList = new List<GameObject>(); //  추가

            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.prefab);
                obj.SetActive(false);
                queue.Enqueue(obj);
            }

            PoolDictionary.Add(pool.key, queue);
            ActiveObjects.Add(pool.key, activeList); //  추가
        }
    }

    public GameObject GetObject(PoolType key)
    {
        if (PoolDictionary.ContainsKey(key) && PoolDictionary[key].Count > 0)
        {
            GameObject obj = PoolDictionary[key].Dequeue();
            obj.SetActive(true);
            ActiveObjects[key].Add(obj); //  추가
            return obj;
        }

        return null;
    }

    public void ReturnObject(PoolType key, GameObject obj)
    {
        if (!PoolDictionary.ContainsKey(key)) return;

        obj.SetActive(false);
        PoolDictionary[key].Enqueue(obj);
        ActiveObjects[key].Remove(obj); //  추가
    }


    public GameObject[] GetObjects(PoolType key)
    {
        return PoolDictionary[key].ToArray();
    }

    // ✅ 활성화된 오브젝트만 가져오는 함수
    public GameObject[] GetActiveObjects(PoolType key)
    {
        return ActiveObjects[key].ToArray();
    }
}

 

private Dictionary<string, Object> assetPool = new Dictionary<string, Object>();

public T LoadAsset<T>(eAssetType eAssetType, string assetName) where T : Object // 첫번째 T 반환타입, 2번째 T 어떤 클래스로 받을건지
    {
        string path = $"{eAssetType}/{assetName}"; 
        // 로드후 assetPool에 넣음, 로드할때마다 로드할 에셋이 assetPool에 있는지 확인
        // 있으면 assetPool꺼내서 줌, 없으면 다시 로드 후 assetPool에 넣음
        // 키 값이 있는지 없는지 확인
        if (assetPool.TryGetValue(path, out Object obj))
        {
            Debug.Log($"{path}가 이미 있습니다.");
            if(obj != null)
                return (T)obj;
        }
        Debug.Log($"{path} 새로 로드");
        obj = Resources.Load<T>(path);
        assetPool.TryAdd(path, obj);
        return (T)obj;
    }

 

리소스 데이터 로드 시 중복 생성 방지

 

다양한 데이터 자료형에 대응하기 위해 제너릭 T를 사용하며 Object 제약 조건을 설정합니다.

TryGetValue

딕셔너리에서 지정된 키, 연결된 값을 검색하는데 사용되며

키가 Dictionary에 있는지 여부를 나타내는 Bool 값을 반환(True, False)하고

키와 연결된 값이 있으면 출력합니다.

TryAdd

Dictionary에 추가할 때

Add와 다르게 해당 키가 이미 존재하는지 검사하고,

존재하지 않는 경우에만 항목을 추가한다.

 

public enum PoolType
    {
        Enemy,
        Item,
        Bullet
    }

public GameObject GetObject(PoolType key)
    {
        if (PoolDictionary.ContainsKey(key) && PoolDictionary[key].Count > 0)
        {
            GameObject obj = PoolDictionary[key].Dequeue();
            obj.SetActive(true);
            return obj;
        }

        return null;
    }

 

GetObject 메서드를 사용한다고 가정하자.

 

여기서 매개변수는 Pooltype(Enum)이다.

 

[SerializeField] private List<GameObject> enemies;

GameObject enemy = ObjectManager.Instance.GetObject(enemies[randomEnemy]);

 

그러나 여기서 GetObject를 불러와서 enemies[randomEnemy]를

 

매개변수 값에 넣는다면 오류가 발생한다.

 

타입이 다르기 때문이다.

 

enemies[randomEnemy]는 List<GameObject> 타입이고

 

ObjectManager.GetObject(PoolType)는 Enum 타입이기 때문에 직접 연결이 안 된다.

 

해결한 방법

[Header("Enemy Settings")]
    [SerializeField] private List<GameObject> enemies;
    [SerializeField] private List<ObjectManager.PoolType> enemiesTypes;

 

ObjectManager.PoolType의 리스트를 만들어 선언후 

 

ObjectManager.PoolType selectEnemiesType = enemiesTypes[randomEnemy];
GameObject enemy = ObjectManager.Instance.GetObject(selectEnemiesType);

 

리스트의 값을 selectEnemiesType 변수에 저장하여 맞춰준다.

 

 

단, 이 경우 동일한 인덱스로 접근하고 있기 때문에 

 

enemies 리스트와 enemyTypes 리스트의 순서를 일치시켜야 한다.

(Index는 하나인데, 리스트를 동시에 참조한다면)

만약 아이템마다 type이라는 변수를 string으로 사용한다면 오타위험도 크고 유지보수도 어려울 것이다.

 

Enum(열거형)을 사용하면 오타 방지 및 가독성 증가, 유지보수가 쉬워진다.

 

[SerializeField] private ItemType type;
    private Rigidbody2D _rigidbody2D;
    [SerializeField] private float itemSpeed = 3f;
    
    public enum ItemType
    {
        Coin
    }

    public ItemType GetItemType()
    {
        return type;
    }

직접 입력하는 것이 아닌 미리 설정된 값을 지정하는 것이기 때문에 오타날 일이 없다! (설정을 잘못한다면?)

SceneManager.GetActiveScene()

현재 활성화 되어있는 Scene을 반환한다.

 

SceneManager.GetActiveScene().name

> 활성화 된 Scene중 이름 반환

 

SceneManager.GetActiveScene().buildIndex

> 활성화 된 Scene중 인덱스 반환


AddListener

버튼의 경우 인스펙터 창에서 수동으로 할당 후 함수를 추가하는 방법이 있는데

메서드 이름이 바뀔경우 다시 추가해야 하는 불편함이 있다.

그럴경우 스크립트를 통해 AddListener 메서드를 사용하여 버튼 연결이 가능하다.

 

 private Button Restart;
 
    private void Start()
    {
        Restart = gameOverPanel.GetComponentInChildren<Button>();
        Restart.onClick.AddListener(GameRetry);
    }
    
    private void GameRetry()
    {
        Debug.Log("게임 재시작");
    }

 

게임오브젝트에 버튼이 달려있는 컴포넌트를 가져오고

onclick 이벤트를 통해 실행할 메서드를 지정해준다. 

 

인자값이 있는 경우 람다, delegate를 쓴다.

private void Start()
{
    Restart = gameOverPanel.GetComponentInChildren<Button>();
    Restart.onClick.AddListener(() => GameRetry());
}

 

+ Recent posts