| | |
| | | using UnityEngine; |
| | | using UnityEngine; |
| | | using System.Collections.Generic; |
| | | using System; |
| | | using UnityEngine.U2D; |
| | |
| | | using System.Threading; |
| | | using ProjSG.Resource; |
| | | using YooAsset; |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | #if UNITY_EDITOR |
| | |
| | | } |
| | | #endif |
| | | |
| | | // // needExt 是否需要函数内部添加后缀 |
| | | // [System.Obsolete("US2: Use LoadAssetAsync<T>(directory, name, needExt) returning UniTask<T> instead.")] |
| | | // public T LoadAsset<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object |
| | | // { |
| | | // directory = directory.Replace("\\", "/"); |
| | | // name = name.Replace("\\", "/"); |
| | | // // 特殊处理 因为有一层图集的关系 directory要传入的应该是atlas的名字 |
| | | // if (typeof(T) == typeof(Sprite)) |
| | | // { |
| | | // return LoadSprite(directory, name) as T; |
| | | // } |
| | | // else if (typeof(T) == typeof(SkeletonDataAsset)) |
| | | // { |
| | | // //文件目录调整,name中包含了路径 |
| | | // if (name.Contains("/")) |
| | | // { |
| | | // directory += name.Substring(0, name.LastIndexOf("/")); |
| | | // name = name.Substring(name.LastIndexOf("/") + 1); |
| | | // } |
| | | // ==================================================================== |
| | | // 同步方法(仅非 WebGL 平台) |
| | | // ==================================================================== |
| | | #if !UNITY_WEBGL |
| | | //needExt 是否需要函数内部添加后缀 |
| | | public T LoadAsset<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | directory = directory.Replace("\\", "/"); |
| | | name = name.Replace("\\", "/"); |
| | | // 特殊处理 因为有一层图集的关系 directory要传入的应该是atlas的名字 |
| | | if (typeof(T) == typeof(Sprite)) |
| | | { |
| | | return LoadSprite(directory, name) as T; |
| | | } |
| | | else if (typeof(T) == typeof(SkeletonDataAsset)) |
| | | { |
| | | //文件目录调整,name中包含了路径 |
| | | if (name.Contains("/")) |
| | | { |
| | | directory += name.Substring(0, name.LastIndexOf("/")); |
| | | name = name.Substring(name.LastIndexOf("/") + 1); |
| | | } |
| | | } |
| | | |
| | | // } |
| | | return LoadAssetInternal<T>(directory, name, needExt); |
| | | } |
| | | |
| | | // return LoadAssetInternal<T>(directory, name, needExt); |
| | | // } |
| | | //needExt 是否需要函数内部添加后缀 |
| | | private T LoadAssetInternal<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | T asset = null; |
| | | var path = ($"Assets/ResourcesOut/{directory}/{name}" + (needExt ? GetExtension(typeof(T)) : "")).Replace("//", "/").Trim().Replace("\\", "/"); |
| | | |
| | | // //needExt 是否需要函数内部添加后缀 |
| | | // private T LoadAssetInternal<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object |
| | | // { |
| | | // // 已禁用同步加载,强制业务全部走异步API。 |
| | | // throw new NotSupportedException("同步资源加载已禁用,请使用异步接口"); |
| | | // } |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | #if UNITY_EDITOR |
| | | asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path); |
| | | #endif |
| | | } |
| | | else |
| | | { |
| | | asset = YooAssetService.Instance.LoadAssetSync<T>(path); |
| | | } |
| | | |
| | | // [System.Obsolete("US2: Use LoadConfigAsync returning UniTask<string[]> instead.")] |
| | | // public string[] LoadConfig(string name) |
| | | // { |
| | | // string path = string.Empty; |
| | | // #if UNITY_EDITOR |
| | | // if (!AssetSource.isUseAssetBundle) |
| | | // { |
| | | // path = ResourcesPath.CONFIG_FODLER + "/" + name + ".txt"; |
| | | // } |
| | | // else |
| | | // #endif |
| | | // { |
| | | // path = AssetVersionUtility.GetAssetFilePath($"config/{name}.txt"); |
| | | // } |
| | | if (asset == null) |
| | | { |
| | | Debug.LogErrorFormat("LoadAsset() => 加载不到资源: {0}", path); |
| | | } |
| | | |
| | | // return File.ReadAllLines(path); |
| | | // } |
| | | return asset; |
| | | } |
| | | |
| | | public string[] LoadConfig(string name) |
| | | { |
| | | #if UNITY_EDITOR |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | string path = ResourcesPath.CONFIG_FODLER + "/" + name + ".txt"; |
| | | return File.ReadAllLines(path); |
| | | } |
| | | #endif |
| | | // AB 模式:通过 YooAsset 同步加载 TextAsset |
| | | var location = $"Assets/ResourcesOut/Config/{name}.txt"; |
| | | var textAsset = YooAssetService.Instance.LoadAssetSync<TextAsset>(location); |
| | | if (textAsset != null && !string.IsNullOrEmpty(textAsset.text)) |
| | | return textAsset.text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); |
| | | Debug.LogError($"[ResManager] LoadConfig failed for '{name}'"); |
| | | return Array.Empty<string>(); |
| | | } |
| | | |
| | | private Sprite LoadSprite(string atlasName, string spriteName) |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | SpriteAtlas atlas = LoadAsset<SpriteAtlas>("Sprite", atlasName.Replace("Sprite/", "")); |
| | | if (null == atlas) |
| | | { |
| | | return null; |
| | | } |
| | | return atlas.GetSprite(spriteName); |
| | | } |
| | | else |
| | | { |
| | | // YooAsset 使用完整路径直接加载 Sprite |
| | | var path = $"Assets/ResourcesOut/{atlasName}/{spriteName}.png".Replace("//", "/"); |
| | | return YooAssetService.Instance.LoadAssetSync<Sprite>(path); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | // ==================================================================== |
| | | // 异步加载方法(所有平台统一) |
| | | // ==================================================================== |
| | | |
| | | //needExt 是否需要函数内部添加后缀(回调版本) |
| | | public void LoadAssetAsync<T>(string directory, string name, Action<bool, UnityEngine.Object> callBack, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | directory = directory.Replace("\\", "/"); |
| | | name = name.Replace("\\", "/"); |
| | | |
| | | // 特殊处理 因为有一层图集的关系 directory要传入的应该是atlas的名字 |
| | | if (typeof(T) == typeof(Sprite)) |
| | | { |
| | | LoadSpriteAsync<T>(directory, name, callBack); |
| | | return; |
| | | } |
| | | else if (typeof(T) == typeof(SkeletonDataAsset)) |
| | | { |
| | | //文件目录调整,name中包含了路径 |
| | | if (name.Contains("/")) |
| | | { |
| | | directory += name.Substring(0, name.LastIndexOf("/")); |
| | | name = name.Substring(name.LastIndexOf("/") + 1); |
| | | } |
| | | } |
| | | |
| | | LoadAssetAsyncInternal<T>(directory, name, callBack, needExt); |
| | | } |
| | | |
| | | public async UniTask<T> LoadAssetAsync<T>(string directory, string name) where T : UnityEngine.Object |
| | | { |
| | | return await LoadAssetAsync<T>(directory, name, needExt: true); |
| | | } |
| | | |
| | | public async UniTask<T> LoadAssetAsync<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | return await LoadAssetAsync<T>(directory, name, needExt, CancellationToken.None); |
| | | } |
| | | |
| | | private void LoadSpriteAsync<T>(string atlasName, string spriteName, Action<bool, UnityEngine.Object> callBack) where T : UnityEngine.Object |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | // Editor 模式下可直接加载 sprite |
| | | LoadAssetAsyncInternal<T>(atlasName, spriteName, callBack); |
| | | } |
| | | else |
| | | { |
| | | // AB 模式下直接加载单独的 Sprite 文件(YooAsset 自动处理 SpriteAtlas 依赖) |
| | | LoadAssetAsyncInternal<Sprite>(atlasName, spriteName, (isLoaded, sprite) => |
| | | { |
| | | callBack?.Invoke(isLoaded, sprite); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | private void LoadAssetAsyncInternal<T>(string directory, string name, Action<bool, UnityEngine.Object> callBack, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | var path = string.Concat($"Assets/ResourcesOut/{directory}/{name}", (needExt ? GetExtension(typeof(T)) : "")).Replace("//", "/"); |
| | | |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | #if UNITY_EDITOR |
| | | var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path); |
| | | callBack?.Invoke(asset != null, asset); |
| | | #endif |
| | | } |
| | | else |
| | | { |
| | | // US1: Route through YooAssetService async |
| | | CoLoadViaYooAsset<T>(path, callBack).Forget(); |
| | | } |
| | | } |
| | | |
| | | private async UniTaskVoid CoLoadViaYooAsset<T>(string path, Action<bool, UnityEngine.Object> callBack, CancellationToken ct = default) where T : UnityEngine.Object |
| | | { |
| | | try |
| | | { |
| | | var asset = await YooAssetService.Instance.LoadAssetAsync<T>(path, ct: ct); |
| | | callBack?.Invoke(asset != null, asset); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[ResManager] Async load via YooAsset failed: {ex.Message}"); |
| | | callBack?.Invoke(false, null); |
| | | } |
| | | } |
| | | |
| | | public void UnloadAsset(string directory, string assetName) |
| | | { |
| | | directory = directory.Replace("\\", "/").TrimEnd('/'); |
| | | assetName = assetName.Replace("\\", "/"); |
| | | string path = ($"Assets/ResourcesOut/{directory}/{assetName}").Replace("//", "/"); |
| | | YooAssetService.Instance.UnloadAsset(path); |
| | | } |
| | | |
| | | public string GetAssetFilePath(string _assetKey) |
| | | { |
| | | var path = Path.Combine(ExternalStorePath, _assetKey); |
| | | if (!File.Exists(path)) |
| | | { |
| | | path = Path.Combine(StreamingAssetPath, _assetKey); |
| | | } |
| | | |
| | | return path; |
| | | } |
| | | |
| | | // ==================================================================== |
| | | // US1: New UniTask-based async variants |
| | | // ==================================================================== |
| | | |
| | | /// <summary> |
| | | /// 异步加载资源(UniTask 版本,US1 新增)。 |
| | | /// </summary> |
| | | public async UniTask<T> LoadAssetAsync<T>(string directory, string name, bool needExt = true, CancellationToken ct = default) where T : UnityEngine.Object |
| | | { |
| | | directory = directory.Replace("\\", "/"); |
| | |
| | | return await YooAssetService.Instance.LoadAssetAsync<T>(path, ct: ct); |
| | | } |
| | | |
| | | // ==================================================================== |
| | | // 异步内部实现(统一) |
| | | // ==================================================================== |
| | | |
| | | private void LoadSpriteAsync<T>(string atlasName, string spriteName, Action<bool, UnityEngine.Object> callBack) where T : UnityEngine.Object |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | // Editor 模式下可直接加载 sprite |
| | | LoadAssetAsyncInternal<T>(atlasName, spriteName, callBack); |
| | | } |
| | | else |
| | | { |
| | | // AB 模式下直接加载单独的 Sprite 文件(YooAsset 自动处理 SpriteAtlas 依赖) |
| | | LoadAssetAsyncInternal<Sprite>(atlasName, spriteName, (isLoaded, sprite) => |
| | | { |
| | | callBack?.Invoke(isLoaded, sprite); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | private void LoadAssetAsyncInternal<T>(string directory, string name, Action<bool, UnityEngine.Object> callBack, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | var path = string.Concat($"Assets/ResourcesOut/{directory}/{name}", (needExt ? GetExtension(typeof(T)) : "")).Replace("//", "/"); |
| | | |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | #if UNITY_EDITOR |
| | | var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path); |
| | | callBack?.Invoke(asset != null, asset); |
| | | #endif |
| | | } |
| | | else |
| | | { |
| | | CoLoadViaYooAsset<T>(path, callBack).Forget(); |
| | | } |
| | | } |
| | | |
| | | private async UniTaskVoid CoLoadViaYooAsset<T>(string path, Action<bool, UnityEngine.Object> callBack, CancellationToken ct = default) where T : UnityEngine.Object |
| | | { |
| | | try |
| | | { |
| | | var asset = await YooAssetService.Instance.LoadAssetAsync<T>(path, ct: ct); |
| | | callBack?.Invoke(asset != null, asset); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[ResManager] Async load via YooAsset failed: {ex.Message}"); |
| | | callBack?.Invoke(false, null); |
| | | } |
| | | } |
| | | |
| | | public void UnloadAsset(string directory, string assetName) |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | return; |
| | | |
| | | directory = directory.Replace("\\", "/").TrimEnd('/'); |
| | | assetName = assetName.Replace("\\", "/"); |
| | | string path = ($"Assets/ResourcesOut/{directory}/{assetName}").Replace("//", "/"); |
| | | YooAssetService.Instance.UnloadAsset(path); |
| | | } |
| | | |
| | | public string GetAssetFilePath(string _assetKey) |
| | | { |
| | | var path = Path.Combine(ExternalStorePath, _assetKey); |
| | | if (!File.Exists(path)) |
| | | { |
| | | path = Path.Combine(StreamingAssetPath, _assetKey); |
| | | } |
| | | |
| | | return path; |
| | | } |
| | | |
| | | // ==================================================================== |
| | | // LoadConfigAsync(所有平台统一) |
| | | // ==================================================================== |
| | | |
| | | /// <summary> |
| | | /// US4: 异步加载资源并走缓存层(缓存命中直接返回,未命中则加载并缓存)。 |
| | | /// 异步加载配置文件。 |
| | | /// AB 模式使用 YooAsset 异步加载 TextAsset,非 AB 模式直接读文件。 |
| | | /// </summary> |
| | | public async UniTask<string[]> LoadConfigAsync(string name, bool needExt = true, CancellationToken ct = default) |
| | | { |
| | | if (AssetSource.isUseAssetBundle) |
| | | { |
| | | if (name.EndsWith(".txt") && needExt) |
| | | { |
| | | name = name.Substring(0, name.Length - 4); |
| | | } |
| | | var location = $"Assets/ResourcesOut/Config/{name}" + (needExt ? ".txt" : ""); |
| | | try |
| | | { |
| | | var asset = await YooAssetService.Instance.LoadAssetAsync( |
| | | location, typeof(TextAsset), 0, ct) as TextAsset; |
| | | if (asset != null && !string.IsNullOrEmpty(asset.text)) |
| | | return asset.text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError($"[ResManager] LoadConfigAsync YooAsset failed for '{name}': {ex.Message}"); |
| | | } |
| | | return Array.Empty<string>(); |
| | | } |
| | | |
| | | // 非 AB 模式: 直接读文件(Editor 开发模式) |
| | | #if UNITY_EDITOR |
| | | string path = ResourcesPath.CONFIG_FODLER + "/" + name + (needExt ? ".txt" : ""); |
| | | return await UniTask.RunOnThreadPool(() => File.ReadAllLines(path)); |
| | | #else |
| | | return Array.Empty<string>(); |
| | | #endif |
| | | } |
| | | |
| | | // ==================================================================== |
| | | // 缓存加载 & Sprite 异步加载(所有平台) |
| | | // ==================================================================== |
| | | |
| | | /// <summary> |
| | | /// 异步加载资源并走缓存层(缓存命中直接返回,未命中则加载并缓存)。 |
| | | /// </summary> |
| | | public async UniTask<T> LoadAssetCachedAsync<T>(string directory, string name, bool needExt = true, CancellationToken ct = default) where T : UnityEngine.Object |
| | | { |
| | |
| | | |
| | | var path = ($"Assets/ResourcesOut/{directory}/{name}" + (needExt ? GetExtension(typeof(T)) : "")) |
| | | .Replace("//", "/").Trim().Replace("\\", "/"); |
| | | // 音频文件扩展名统一转小写,避免 .WAV/.MP3 大小写不匹配 |
| | | // 音频文件扩展名统一转小写 |
| | | if (typeof(T) == typeof(AudioClip)) |
| | | { |
| | | var pathExt2 = System.IO.Path.GetExtension(path); |
| | |
| | | var path = $"Assets/ResourcesOut/{atlasName}/{spriteName}.png" |
| | | .Replace("//", "/").Trim().Replace("\\", "/"); |
| | | var sprite = await YooAssetService.Instance.LoadAssetAsync<Sprite>(path, ct: ct); |
| | | #if UNITY_WEBGL |
| | | if (sprite == null) |
| | | Debug.LogWarning($"[ResManager][WebGL-Diag] Sprite load returned NULL: path={path}"); |
| | | #endif |
| | | Debug.LogWarning($"[ResManager] Sprite load returned NULL: path={path}"); |
| | | return sprite; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 异步加载配置文件(UniTask 版本)。 |
| | | /// AB 模式使用 YooAsset RawFile 异步加载,非 AB 模式直接读文件。 |
| | | /// </summary> |
| | | public async UniTask<string[]> LoadConfigAsync(string name, bool needExt = true, CancellationToken ct = default) |
| | | { |
| | | // AB 模式(含 WebGL): 使用 YooAsset 加载配置文件 |
| | | if (AssetSource.isUseAssetBundle) |
| | | { |
| | | // 判断一下是否原来已经包含.txt 配合 needExt 做兼容,避免调用方传入重复后缀导致路径错误 |
| | | if (name.EndsWith(".txt") && needExt) |
| | | { |
| | | name = name.Substring(0, name.Length - 4); |
| | | } |
| | | var location = $"Assets/ResourcesOut/Config/{name}" + (needExt ? ".txt" : ""); |
| | | try |
| | | { |
| | | #if UNITY_WEBGL |
| | | // WebGL: WebServerFileSystem 不支持 LoadRawFileAsync,改用 LoadAssetAsync<TextAsset> |
| | | // .txt 文件在 Unity 中以 TextAsset 形式导入,WebGL 支持此加载方式 |
| | | var asset = await ProjSG.Resource.YooAssetService.Instance.LoadAssetAsync( |
| | | location, typeof(UnityEngine.TextAsset), 0, ct) as UnityEngine.TextAsset; |
| | | if (asset != null && !string.IsNullOrEmpty(asset.text)) |
| | | return asset.text.Split(new[] { "\r\n", "\n" }, System.StringSplitOptions.None); |
| | | #else |
| | | var textAsset = await ProjSG.Resource.YooAssetService.Instance.LoadAssetAsync<TextAsset>(location, 0, ct); |
| | | if (textAsset != null && !string.IsNullOrEmpty(textAsset.text)) |
| | | return textAsset.text.Split(new[] { "\r\n", "\n" }, System.StringSplitOptions.None); |
| | | #endif |
| | | } |
| | | catch (System.Exception ex) |
| | | { |
| | | UnityEngine.Debug.LogError($"[ResManager] LoadConfigAsync YooAsset failed for '{name}': {ex.Message}"); |
| | | } |
| | | return System.Array.Empty<string>(); |
| | | } |
| | | |
| | | // 非 AB 模式: 直接读文件(Editor 开发模式) |
| | | #if UNITY_EDITOR |
| | | string path = ResourcesPath.CONFIG_FODLER + "/" + name + (needExt ? ".txt" : ""); |
| | | return await UniTask.RunOnThreadPool(() => File.ReadAllLines(path)); |
| | | #else |
| | | return System.Array.Empty<string>(); |
| | | #endif |
| | | } |
| | | |
| | | } |
| | | } |