-
[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; } }
'개발일지 > 스파르타 코딩클럽 부트캠프' 카테고리의 다른 글
[TIL] 12주차 2일 기술 면접 대비 (0) 2024.07.03 [TIL] 12주차 1일 ( FSM, 유한 상태 머신 ) (0) 2024.07.01 [TIL] 11주차 4일 최종 프로젝트 기획 (0) 2024.06.27 [TIL] 11주차 3일 ( 유니티 커스텀툴 만드는 방법 ) (0) 2024.06.26 [TIL] 11주차 2일 (0) 2024.06.25