본문 바로가기
유니티/오류 코드와 잘못된 개념 정리(기록용)

어드레서블 에셋을 이용한 오브젝트 풀 구현

by fore4022 2024. 7. 31.

  아래의 코드는 빌드 해보기 이전까지 사용했었습니다. 하지만, 어드레서블로 불러온 에셋의 인스턴스를 만들고 Release 하는 동작이 빌드 환경에서 오류가 발생한다는 것을 알았습니다.

 에디터와 달리 빌드 환경에서는 에셋이 Relase 되면, missing이 되어버립니다.


 어드레서블 에셋을 이용해서 오브젝트 풀을 구현해보았다. 이전 글의 Util 클래스의 어드레서블 에셋을 불러오는 기능을 사용하여서 구현되었다. Util 클래스는 이전 글에 올려두었다.
 어드레서블 에셋을 써야할 상황마다 불러와서 생성해주는 과정은, 어드레서블 에셋을 비동기로 불러온다는 것을 생각하면, 매우 비효율적이다. 그렇기에 오브젝트 풀을 하게해주는 기능을 구현해 보았다.

 본인이 이해한 내용을 바탕으로 만들었기에 성능 최적화나, 모든 경우에 대한 오류의 대응이 미흡할 수 있다. 본인도 사용하면서 수정하거나, 조금이라도 더 최적화를 한다면, 여기의 내용도 그때 그때 수정할 것이다.


using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using System.Text;
public class ObjectPool
{
    private Dictionary<string, ScriptableObject> scriptableObjects = new();
    private Dictionary<string, List<GameObject>> poolingObjects = new();

    private Transform root;

    private const string so = "SO";

    public ObjectPool()
    {
        GameObject go = GameObject.Find("@ObjectPool");

        if (go == null)
        {
            Managers.Scene.loadScene -= ClearPool;
            Managers.Scene.loadScene += ClearPool;

            go = new GameObject { name = "@ObjectPool" };

            root = go.transform;
        }
    }
    public int ScriptableObjectsCount { get { return scriptableObjects.Count; } }
    public int PoolingObjectsCount { get { return poolingObjects.Count; } }
    public void ClearPool()
    {
        if(scriptableObjects != null)
        {
            scriptableObjects = new();
        }

        if(poolingObjects != null)
        {
            poolingObjects = new();
        }

        Managers.Scene.loadScene -= ClearPool;
    }
    public void ActiveObject(string prefabName)
    {
        GameObject prefab = GetActiveGameObject(prefabName);
        
        prefab.SetActive(true);
    }
    public void DisableObject(GameObject prefab)
    {
        prefab.SetActive(false);
    }
    public GameObject GetActiveGameObject(string prefabName)
    {
        foreach(GameObject instance in poolingObjects[prefabName])
        {
            if(!instance.activeSelf)
            {
                return instance;
            }
        }

        return null;
    }
    public T GetScriptableObject<T>(string type) where T : ScriptableObject
    {
        if(scriptableObjects.ContainsKey(type))
        {
            return (T)scriptableObjects[type];
        }

        return null;
    }
    public void CreateObjects(List<GameObject> prefabs, int count)
    {
        string key;

        foreach (GameObject prefab in prefabs)
        {
            key = prefab.name;

            SetInstance(Instantiate(prefab, count), key);
        }
    }
    private async Task<ScriptableObject> CreateScriptableObject(string key)
    {
        if (!scriptableObjects.ContainsKey(key))
        {
            StringBuilder path = new StringBuilder(key);

            path.Append(so);

            ScriptableObject scriptableObject = await Util.LoadingToPath<ScriptableObject>(path.ToString());

            scriptableObjects.Add(key, scriptableObject);

            return scriptableObject;
        }

        return scriptableObjects[key];
    }
    private List<GameObject> Instantiate(GameObject prefab, int count)
    {
        List<GameObject> queue = new();

        GameObject parent = GameObject.Find(prefab.name);

        if (parent == null)
        {
            parent = new GameObject { name = prefab.name };

            parent.transform.parent = root;
        }

        for (int i = 0; i < count; i++)
        {
            GameObject instance = Object.Instantiate(prefab, parent.transform);

            instance.SetActive(false);

            queue.Add(instance);
        }

        return queue;
    }
    private async Task SetInstance(List<GameObject> prefabs, string key)
    {
        ScriptableObject so = await CreateScriptableObject(key);

        foreach (GameObject instance in prefabs)
        {
            IScriptableData scriptableData = instance.GetComponent<IScriptableData>();

            scriptableData.SetScriptableObject = so;
        }

        if (poolingObjects.ContainsKey(key))
        {
            poolingObjects[key].AddRange(prefabs);
        }
        else
        {
            poolingObjects.Add(key, prefabs);
        }
    }
}
using UnityEngine;
public class Test : MonoBehaviour
{
	private string prefabName = "test";
    
    private void Start()
    {
        ObjectPool.CreateToPath("basicAttack", 1);
    }
    private void Update()
    {
        ObjectPool.GetOrActiveObject("prefabName");
    }
}//실행 결과 : 오브젝트 풀로 오브젝트가 생성되기 전까지는 오류가 나오다가, 생성된 후 오브젝트가 활성화 된다. 1개만 생성했기에, 이후에는 오류가 발생한다.

아직 많이 부족한 코드이니, 틀린 점이나 개선 여지가 있는 것을 지적해주세요.

 오늘도 방문해 주셔서 감사합니다.

 

 최근 수정 날짜 : 2024.11.5