ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 11주차 5일 ( 리소스 데이터 관리 )
    개발일지/스파르타 코딩클럽 부트캠프 2024. 6. 28. 12:12

    그냥 기본적으로 큐브를 프리팹으로 만들어서 대충 객체로써 생성한다고 생각해보자

     

    우리는 지금까지 큐브를 만들어서 프리팹으로 뺀 후 캔버스를 만들어서 판넬에 버튼을 만들고

    직렬화필드로 프리팹을 받아와서 버튼에 온클릭 형식으로 프리팹을 생성했을 것이다.

     

    만약에 여기서 빌드를 하게되면 여기있는 에셋 파일이 다 빌드가 되는 것이 아니다.

    빌드를 할 때는 씬에 연결되어있는 오브젝트들만 연결되어 빌드가 된다.

     

    직렬화필드로 인스펙터에서 프리팹을 연결해주면

    버튼을 눌러서 큐브를 생성하지 않아도

    큐브는 항상 메모리에 올라 가 있게된다. ( 게임의 처음부터 끝 까지 큐브의 메모리가 존재하는 샘 )

    당연히 최적화에 좋지 않을것이다.

     

    인스펙터에서 프리팹을 넣어주거나 하는 행위는 좋지 않다고 볼 수 있다.

    오늘은 조금 유연성 있게 데이터를 불러오는 방법을 알아보겠다

     

    동적 로드

    Resources

    Resources 폴더를 생성하면 폴더 안의 모든 것들은 빌드에 올라갈 수 있다.

    Resources 함수는 경로가 알아서 Resources라는 이름을 가진 폴더로 정해져있다.

    여기서 중요한 것은 빌드에 올라가는거랑 메모리에 해당되는 것은 다르다.

    인스펙터에 연결하는 것과 다르게 사용할 때만 불러오는 방식이다.

    public void OnClickCube()
    {
        var obj = Resources.Load<GameObject>("Cube");
        Instantiate(obj);
    }

     

    UI Image를 불러오는 방법

    [SerializeField] private Image img;
    
    public void OnClickImage()
    {
        var sprite = Resources.Load<Sprite>("Dog");
        img.sprite = sprite;     
    }

     

    그럼 리소스 폴더 안에 모든 데이터를 때려박느냐?

    리소스 폴더 안에서도 폴더링이 가능하다.

    경로만 잘 표시해준다면 가능하다.

    public void OnClickCube()
    {
        var obj = Resources.Load<GameObject>("Prefab/Cube");
        Instantiate(obj);
    }

     

    그럼 하나하나 이런 함수를 써야할까?

    이런 이유로 리소스 매니저를 만든다면 효율적으로 코딩할 수 있다.

     

    리소스 매니저

     

    리소스매니저에서 LoadAsset 함수를 만들어서 제네릭으로 여러 형식의 변수를 받아올 수 있다.

    Load는 오브젝트 형식만 받아오기 때문에 T는 오브젝트로 변환할 수 있는 것만 가져온다고 제한을 걸어두어야

    오류가 생기지 않는다.

    public class ResourceManager : MonoBehaviour
    {
        public static ResourceManager Instance;
      
        private void Awake()
        {
            if(Instance == null)
                Instance = this;
            else
                Destroy(gameObject);
        }
        
        public T LoadAsset<T>(string assetName) where T : Object  
    	{
        	var obj = Resources.Load<T>(assetName);
    
       	    return obj;
    	}
    }
    
    public class PopupPanel : MonoBehaviour
    {
    	public void OnClickCube()
        {
        	var obj = ResourceManager.Instance.LoadAsset<GameObject>("Prefab/Cube");
    		Instantiate(obj);
        }
    }

    이런 식으로 리소스매니저를 만들어 둔 후에

    다른 클래스에서 LoadAsset 스크립트를 사용하여 리소스 폴더 안의 객체들을 불러올 수 있다.

     

    그런데 여기서 string으로 불러오는 것을 최소화 하는 방법에는 뭐가 있을까?

    enum을 사용한다면 조금 더 좋은 방법이 될 것이다.

     

    public enum eAssetType
    {
        Prefab,
        Image
    }
    public class ResourceManager : MonoBehaviour
    {
        public static ResourceManager Instance;
    
        private void Awake()
        {
            if(Instance == null)
                Instance = this;
            else
                Destroy(gameObject);
        }
        
        public T LoadAsset<T>(eAssetType assetType ,string assetName) where T : Object
    	{
        	string path = assetType.ToString() + "/" + assetName;
    
        	var obj = Resources.Load<T>(assetName);
    
       	  	return obj;
    	}
    }
    
    public class PopUpPanel : MonoBehaviour
    {
    	public void OnClickCube()
        {
            var obj = ResourceManager.Instance.LoadAsset<GameObject>(eAssetType.Prefab ,"Cube");
            Instantiate(obj);
        }
    }

    이런 방식으로 enum에 폴더를 담아두고 사용하면 스트링을 사용하는 것을 최소화할 수 있다.

    스트링을 사용해서 데이터를 불러오는 것은 유지보수가 힘들고 오타에도 치명적이기 때문에 최소화 하는것이 좋다.

     

    리소스 데이터를 캐싱하는 법

     

    게임을 끝내고 나서 다시 메인 씬으로 돌아오면 오브젝트들이 파괴되고 게임을 다시 진행하면

    리소스 로드를 다시 시작하게 될 것이다. 같은 오브젝트를 한번 더 탐색하는것이 처음에 탐색하는거랑은 다르지만

    그래도 메모리적으로 좋지 않다.

    즉, 리소스 로드로 에셋을 중복으로 불러오면 불러올 수록 손해다.

    그럼 어떻게 해야할까?

    코드에서 리소스를 캐싱해줄 수 있다.

     

    public enum eAssetType
    {
        Prefab,
        Image
    }
    public class ResourceManager : MonoBehaviour
    {
        public static ResourceManager Instance;
    
        private Dictionary<string, Object> assetPool = new Dictionary<string, Object>();
    
        private void Awake()
        {
            if(Instance == null)
                Instance = this;
            else
                Destroy(gameObject);
        }
        
            public T LoadAsset<T>(eAssetType assetType, string assetName) where T : Object
        {
            string path = assetType.ToString() + "/" + assetName;
    
            if(assetPool.TryGetValue(path, out var obj))
            {
                Debug.Log(path + " 이미 있음");
                if(obj != null)
                    return (T)obj;
            }
            Debug.Log(path = "새로 로드");
    
    
            obj = Resources.Load<T>(assetName);
            assetPool.TryAdd(path, obj); // 키 값에 중복이 있어도 오류를 발생시켜주지 않기 위해
    
            return (T)obj;
        }
    }

     

Designed by Tistory.