[TIL] Unity - Resource Manager

MINO·2024년 10월 24일
0

2024-10-24


직접 참조 , 드래그 앤 드롭 방식

스크립트에서 public 또는 [SerializeField] 로 변수를 선언하고,
Unity 의 인스펙터 창에서 해당 변수에 필요한 게임 오브젝트, 프리팹, 리소스 등을 수동으로 연결해주는 작업.


단점 (Resource Manager 의 필요성)

  • 수동 설정의 번거로움 : 프로젝트의 규모가 커질수록, 오브젝트를 하나씩 연결하기 힘들어진다.
  • 실행 중 객체 참조 : 런타임 중에 오브젝트를 동적으로 할당하기 어렵다.
  • 유지보수 : 씬 내 오브젝트의 구조가 변경되거나 프리팹이 변경될 경우, 기존에 설정한 참조가 깨질 수도 있다.
  • 의존성 문제 : 코드에서 참조를 변경하거나 관리하기 어렵다.

이러한 이유로, Addressable Asset System 이나 Scriptable Object 같은 시스템을 이용하거나,
코드에서 동적으로 참조를 설정하는 방식 으로 전환하는 것이 좋다고 한다.



Resources.Load

Unity 에서 게임 실행 중에 리소스를 동적으로 로드할 수 있게 해주는 메서드
주로 프리팹, 오디오 클립 등을 동적으로 로드하거나 메모리 관리를 최적화할 때 사용된다.

보통 게임의 리소스 (Ex : 텍스쳐, 오디오, 프리팹 등) 는 프로젝트 빌드 시에 메모리에 로드되지만,
Resources.Load 를 사용하면 런타임에 특정 리소스를 메모리에 로드하여 필요한 시점에 사용할 수 있다.

Resources 폴더에 있는 파일만 Resource.Load 를 통해 로드할 수 있다.
Assets/Resources/ 뒤의 경로를 입력 받는다.


PrefabTest.cs

  1. Resources 폴더 - Prefabs 폴더 - Tank 를 동적으로 로드
  2. 해당 프리팹이 존재하면 Instantiate 를 통해 씬에 인스턴스화하여 새로운 오브젝트를 생성한다.
  3. 3초 뒤에 해당 프리팹을 소멸시킨다.
public class PrefabTest : MonoBehaviour
{
		GameObject prefab;
		GameObject tank;
		
		void Start()
		{
				prefab = Resources.Load<GameObject>("Prefabs/Tank"); // 1
				
				if(prefab != null)
				{
						tank = Instantiate(prefab); // 2
						Destroy(tank, 3.0f); // 3
				}
		}
}

주의점 및 단점

  • 퍼포먼스 문제 : Resources.Load 는 동기 방식으로 동작한다.
    → 큰 파일을 로드할 때 프레임 드랍이 발생할 수 있다.
  • 메모리 관리의 복잡성 : Resources 에서 로드된 오브젝트는 수동으로 메모리에서 해제해야 한다.
    Resources.UnloadUnUsedAssets() 또는 Destroy()와 같은 메서드를 통해 메모리를 해제해준다.
  • 코드 관리의 복잡성 : 필요할 때마다 InstantiateDestroy 를 직접 호출하여 오브젝트들을 생성, 소멸시킬 수 있지만, 해당 코드들이 여기저기서 호출된다면 누가 무엇을 언제 만드는지 추적하기 어려워진다.
    Resource 를 관리하는 매니저를 만들어서 관리를 하도록 하자.


Resource Manager

리소스를 효율적으로 관리하고 메모리 사용을 최적화 하기 위해서 구현.
리소스 Load / UnLoad 를 한 곳에 모아두어 추적이 용이해진다.

  • 리소스 로드 : Resource.Load 로 리소스를 동적으로 로드한다.
  • 리소스 캐싱 : 동일한 리소스를 여러 번 로드하는 것을 방지할 수 있다.
    → 이미 로드된 리소스를 캐시하여 재사용할 수 있도록 한다.
  • 리소스 언로드 : 사용하지 않는 리소스를 적절한 시점에 메모리에서 해제한다.
  • 생성 및 소멸 : InstantiateDestroy 를 중앙 집중화 한다.

ResourceManager.cs

제네릭(T) 타입으로 구현되어 있어서 다양한 에셋을 불러올 수 있다.

public class ResourceManager
{
		public T Load<T>(string path) where T : Object
		{
				return Resources.Load<T>(path);
		}
		
		public GameObject Instantiate(string path, Transform parent = null)
		{
				GameObject prefab = Load<GameObject>($"Prefabs/{path}"); // 상황에 따라 경로 맞춰주기
				if(prefab == null)
				{
						Debug.Log($"Failed to load prefab : {path}");
						return null;
				}
				
				return Object.Instantiate(prefab, parent);
		}
		
		public void Destroy(GameObject go)
		{
				if(go == null)
						return;
				
				Object.Destroy(go);
		}
}

Manager.cs

public class Managers : MonoBehaviour
{
		static Managers s_instance; // 유일성 보장
		static Managers Instance { get { Init(); return s_instance; } } // 유일한 매니저를 가져온다.
		
		InputManager _input = new InputManager();
		ResourceManager _resource = new ResourceManager();
		
		public static InputManager Input { get { return Instance._input; } }
		public static ResourceManager Resource { get { return Instance._resource; } }
		
		void Start()
		{
				Init();
		}
		
		void Update()
		{
				_input.OnUpdate();
		}
		
		static void Init()
		{
				if(s_instance == null)
				{
						GameObject go = GameObject.Find("@Managers");
						
						if(go == null)
						{
								go = new GameObject { name = "@Managers" };
								go.AddComponent<Managers>();
						}
						
						DontDestoryOnLoad(go); // 씬이 변경되어도 삭제되지 않고 유지하도록 설정
						s_instance = go.GetComponent<Managers>();
				}
		}
}

PrefabTest.cs

public class PrefabTest : MonoBehaviour
{
		GameObject prefab;
		GameObject tank;
		
		void Start()
		{
				tank = Managers.Resources.Instantiate("Tank"); // ResourceManager 를 통한 1 + 2
				Managers.Resources.Destory(tank); // ResourceManager 를 통한 3
				
				
				// 기존의 PrefabTest 코드
				// 1. prefab = Resources.Load<GameObject>("Prefabs/Tank");
				// 2. tank = Instantiate(prefab);
				// 3. Destroy(tank, 3.0f); 
		}
}

profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글