#nullable enable using Cysharp.Threading.Tasks; using System; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Networking; namespace UVC.Util { /// /// Unity 리소스를 관리하고 비동기적으로 로드하는 기능을 제공합니다. /// /// 클래스는 로드된 리소스의 내부 캐시를 유지하여 /// 후속 액세스를 최적화합니다. 리소스는 키별로 비동기적으로 로드하거나 키 목록을 사용하여 대량으로 로드할 수 있습니다. /// public static class ResourceManager { private static Dictionary _resources = new(); /// /// 문자열 키로 매핑된 리소스가 포함된 읽기 전용 사전을 가져옵니다. /// public static IReadOnlyDictionary get => _resources; /// /// 지정된 경로에서 지정된 유형의 리소스를 비동기적으로 로드합니다. /// /// 이 메서드는 Unity의 를 사용하여 /// 리소스를 비동기적으로 로드합니다. 지정된 리소스가 지정된 경로에 있는지 확인하여 /// 을 반환하지 않도록 합니다. /// 로드할 리소스의 유형입니다. 에서 파생되어야 합니다. /// 리소스 폴더 내 리소스 경로입니다. 이 경로는 리소스 폴더를 기준으로 하며 /// 파일 확장자를 포함하지 않아야 합니다. /// 비동기 작업을 나타내는 작업입니다. 결과에는 로드된 리소스 유형이 포함됩니다. /// (리소스가 발견되면); 그렇지 않으면 . public static async UniTask LoadOnlyAsync(string path) where T : UnityEngine.Object { var request = Resources.LoadAsync(path); await request; return request.asset as T; } /// /// Unity 리소스 시스템에서 지정된 유형의 리소스를 비동기적으로 로드합니다. /// /// 리소스가 이미 캐시된 경우, 새로운 로드 작업을 수행하지 않고 즉시 반환됩니다. /// 그렇지 않은 경우, 리소스는 비동기적으로 로드되어 나중에 사용할 수 있도록 캐시에 추가됩니다. /// /// 로드할 리소스의 유형입니다. 의 하위 클래스여야 합니다. /// Unity 리소스 시스템에서 리소스를 찾는 데 사용되는 키 또는 경로입니다. /// 로드된 리소스와 함께 호출할 선택적 콜백입니다. 콜백은 로드된 리소스를 /// 또는 (리소스를 로드할 수 없는 경우) 유형으로 받습니다. /// 비동기 작업을 나타내는 작업입니다. 작업 결과는 /// 또는 (리소스를 로드할 수 없는 경우) 유형으로 로드된 리소스입니다. public static async UniTask LoadAsync(string key, Action? callback = null) where T : UnityEngine.Object { T? t = null; if (_resources.TryGetValue(key, out var resource)) { t = resource as T; callback?.Invoke(t); return t; } var operation = Resources.LoadAsync(key); await operation; _resources.TryAdd(operation.asset.name, operation.asset); t = operation.asset as T; callback?.Invoke(t); return t; } /// /// 지정된 프리팹 경로 목록에서 모든 에셋을 비동기적으로 로드합니다. /// /// 이 메서드는 프리팹 경로 목록을 순차적으로 처리하여 각 에셋을 /// 비동기적으로 로드합니다. 콜백이 제공된 경우, 로드 작업의 진행 상황을 추적하는 데 사용할 수 있습니다. /// /// 로드할 에셋의 유형입니다. 에서 파생되어야 합니다. /// 로드할 프리팹 경로 목록입니다. /// 각 에셋이 로드된 후 호출되는 선택적 콜백입니다. 이 콜백은 로드된 에셋의 경로, /// 현재 로드된 에셋의 개수, 그리고 로드할 에셋의 총 개수를 받습니다. public static async void LoadAllAsync(List prefabPath, Action? callback = null) where T : UnityEngine.Object { int loadCount = 0; int totalCount = prefabPath.Count; // preloadKeys 배열의 주소들을 로드 foreach (var key in prefabPath) { await LoadAsync(key); loadCount++; callback?.Invoke(key, loadCount, totalCount); } } /// /// 파일 시스템 경로 또는 Resources 경로에서 Sprite를 비동기적으로 로드합니다. /// /// /// 경로가 절대 경로(드라이브 문자 또는 /로 시작)인 경우 파일 시스템에서 로드하고, /// 그렇지 않으면 Resources 폴더에서 로드합니다. /// StreamingAssets 또는 persistentDataPath 경로를 지원합니다. /// /// 이미지 파일 경로 (파일 시스템 절대 경로 또는 Resources 상대 경로) /// 로드된 Sprite, 실패 시 null public static async UniTask LoadSpriteAsync(string path) { if (string.IsNullOrEmpty(path)) return null; // 파일 시스템 절대 경로인지 확인 bool isAbsolutePath = Path.IsPathRooted(path); if (isAbsolutePath) { return await LoadSpriteFromFileAsync(path); } else { // 먼저 Sprite로 로드 시도 var sprite = await LoadOnlyAsync(path); if (sprite != null) return sprite; // Sprite로 로드 실패 시 Texture2D로 로드 후 변환 var texture = await LoadOnlyAsync(path); if (texture != null) { return Sprite.Create( texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f) ); } return null; } } /// /// 파일 시스템에서 이미지를 로드하여 Sprite로 변환합니다. /// /// 이미지 파일의 절대 경로 /// 로드된 Sprite, 실패 시 null private static async UniTask LoadSpriteFromFileAsync(string filePath) { if (!File.Exists(filePath)) { Debug.LogWarning($"[ResourceManager] Image file not found: {filePath}"); return null; } try { // file:// URI로 변환 string uri = "file:///" + filePath.Replace("\\", "/"); using var request = UnityWebRequestTexture.GetTexture(uri); await request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.LogError($"[ResourceManager] Failed to load image: {request.error}"); return null; } var texture = DownloadHandlerTexture.GetContent(request); var sprite = Sprite.Create( texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f) ); return sprite; } catch (Exception e) { Debug.LogError($"[ResourceManager] Failed to load sprite from file: {e.Message}"); return null; } } } }