Resources 폴더에서 프리팹이나 SO 등의 데이터를 가져오는 역할을 하는 매니저 입니다.
먼저 enum 형태로 리소스 폴더의 대분류, 소분류를 만들어주었습니다.
소분류의 경우 사용하지 않는 경우도 있기때문에 None 을 만들어주었습니다.
//파일 경로 (대분류)
public enum EMajorType
{
Data,
Prefab,
Sound
}
//파일 경로 (세부)
public enum ESubType
{
None,
UI
}
일단 임시로 만들어준 상태입니다. 리소스 폴더의 내용이 늘어날때, enum 의 종류도 늘려주면 됩니다.
싱글톤의 경우, 별도로 만들어주어야합니다.
ResourceManager
//리소스 파일에서 데이터를 가져오는 클래스
public class ResourceManager : PersistentSingleton<ResourceManager>
{
private Dictionary<string, object> resourcePool = new Dictionary<string, object>();
/// <summary>
/// 동기로 리소스 불러오기, key : 불러올 에셋 이름, majorType : 대분류, subType : 소분류 비어있을경우 None 으로 처리
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="type"></param>
/// <param name="categoryType"></param>
/// <returns></returns>
public T LoadResource<T> (string key, EMajorType majorType, ESubType subType = ESubType.None)
{
//리소스 폴더 경로 저장
StringBuilder stringBuilder = new StringBuilder();
//반환할 객체용
T returnAsset;
//경로 생성
stringBuilder.Append(majorType);
stringBuilder.Append((subType == ESubType.None) ? "" : $"/{subType}");
stringBuilder.Append($"/{key}");
//딕셔너리에서 같은 에셋이 있는지 검색
if(!resourcePool.ContainsKey(stringBuilder.ToString()))
{
var targetResource = Resources.Load(stringBuilder.ToString(), typeof(T));
//대상이 없을경우 처리
if(targetResource == null )
{
return default;
}
resourcePool.Add(stringBuilder.ToString(), targetResource);
}
//반환 에셋 지정
returnAsset = (T)resourcePool[stringBuilder.ToString()];
return returnAsset;
}
/// <summary>
/// 비동기로 리소스 불러오기, key : 불러올 에셋 이름, majorType : 대분류, subType : 소분류 비어있을경우 None 으로 처리
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="majorType"></param>
/// <param name="subType"></param>
/// <returns></returns>
public async Task<T> LoadResourceAsync<T>(string key, EMajorType majorType, ESubType subType = ESubType.None)
{
StringBuilder stringBuilder = new StringBuilder();
T returnAsset;
stringBuilder.Append(majorType);
stringBuilder.Append((subType == ESubType.None) ? "" : $"/{subType}");
stringBuilder.Append($"/{key}");
if (!resourcePool.ContainsKey(stringBuilder.ToString()))
{
//비동기로 불러오기
var op = Resources.LoadAsync(stringBuilder.ToString(), typeof(T));
//처리가 완료될때까지 대기
while(!op.isDone)
{
await Task.Yield();
}
var targetResource = op.asset;
if(targetResource == null )
{
return default;
}
resourcePool.Add(stringBuilder.ToString(), targetResource);
}
returnAsset = (T)resourcePool[stringBuilder.ToString()];
return returnAsset;
}
}
동기와 비동기로 리소스를 가져오는 메서드를 나누어주었습니다.
동기의 경우, 씬을 로드할 때 사용하여 필요한 내용을 미리 로드하기 위해 사용합니다.
비동기의 경우 게임 진행중에 로드가 필요할경우 사용하게 될 것 입니다.
또한 이미 로드한 리소스를 다시 로드하지 않기 위해, 딕셔너리로 캐싱하는 방식을 사용하였습니다.
private Dictionary<string, object> resourcePool = new Dictionary<string, object>();
물론, 캐싱한 데이터가 많아질경우의 문제도 존재하기 때문에 적절한 위치에서 데이터를 비워줄 필요가 있을것입니다.
해당 문제는 차후에 생각해볼 예정입니다.
동기 형태로 로드하는 메서드 입니다.
public T LoadResource<T> (string key, EMajorType majorType, ESubType subType = ESubType.None)
{
//리소스 폴더 경로 저장
StringBuilder stringBuilder = new StringBuilder();
//반환할 객체용
T returnAsset;
//경로 생성
stringBuilder.Append(majorType);
stringBuilder.Append((subType == ESubType.None) ? "" : $"/{subType}");
stringBuilder.Append($"/{key}");
//딕셔너리에서 같은 에셋이 있는지 검색
if(!resourcePool.ContainsKey(stringBuilder.ToString()))
{
var targetResource = Resources.Load(stringBuilder.ToString(), typeof(T));
//대상이 없을경우 처리
if(targetResource == null )
{
return default;
}
resourcePool.Add(stringBuilder.ToString(), targetResource);
}
//반환 에셋 지정
returnAsset = (T)resourcePool[stringBuilder.ToString()];
return returnAsset;
}
리소스 폴더의 경로는 string 값으로 지정되며, 이 값을 만들어주기 위해 StringBuilder 를 사용해주었습니다.
딕셔너리에 같은 리소스가 있는지 경로 이름으로 key를 검색하며, 없을경우 경로를 key로 사용하여 리소스를 저장합니다.
딕셔너리에 저장이 완료되거나, 경로가 딕셔너리에 있을경우 저장된 리소스를 반환합니다.
비동기 방식의 경우, LoadAsync 를 사용하는 내용이 추가되었을 뿐, 크게 다른 내용은없습니다.
public async Task<T> LoadResourceAsync<T>(string key, EMajorType majorType, ESubType subType = ESubType.None)
{
StringBuilder stringBuilder = new StringBuilder();
T returnAsset;
stringBuilder.Append(majorType);
stringBuilder.Append((subType == ESubType.None) ? "" : $"/{subType}");
stringBuilder.Append($"/{key}");
if (!resourcePool.ContainsKey(stringBuilder.ToString()))
{
//비동기로 불러오기
var op = Resources.LoadAsync(stringBuilder.ToString(), typeof(T));
//처리가 완료될때까지 대기
while(!op.isDone)
{
await Task.Yield();
}
var targetResource = op.asset;
if(targetResource == null )
{
return default;
}
resourcePool.Add(stringBuilder.ToString(), targetResource);
}
returnAsset = (T)resourcePool[stringBuilder.ToString()];
return returnAsset;
}
일단 기반 내용으로 이렇게 잡아주었습니다.
현재 완전한 기획이 잡힌 내용이 아니기 때문에, 이후 필요한 내용이 추가될 예정입니다.