| | |
| | | using UnityEngine; |
| | | using UnityEngine; |
| | | using System.Collections.Generic; |
| | | using System; |
| | | using UnityEngine.U2D; |
| | | using System.IO; |
| | | using UnityEngine.Video; |
| | | using Spine.Unity; |
| | | |
| | | using UnityEngine.UI; |
| | | using Cysharp.Threading.Tasks; |
| | | using System.Threading; |
| | | using ProjSG.Resource; |
| | | using YooAsset; |
| | | |
| | | |
| | | #if UNITY_EDITOR |
| | |
| | | {typeof(Sprite), "png"}, |
| | | {typeof(Texture2D), "jpg"}, |
| | | {typeof(Texture), "jpg"}, |
| | | { typeof(Shader), "shader"}, |
| | | {typeof(Shader), "shader"}, |
| | | {typeof(TextAsset), "txt"}, |
| | | {typeof(AudioClip), "wav"}, |
| | | {typeof(Font), "ttf"}, |
| | |
| | | |
| | | public void Init() |
| | | { |
| | | |
| | | |
| | | } |
| | | |
| | | public void Release() |
| | |
| | | } |
| | | #endif |
| | | |
| | | public T LoadAsset<T> (string directory, string name) where T : UnityEngine.Object |
| | | // ==================================================================== |
| | | // 同步方法(仅非 WebGL 平台) |
| | | // ==================================================================== |
| | | #if !UNITY_WEBGL |
| | | //needExt 是否需要函数内部添加后缀 |
| | | public T LoadAsset<T>(string directory, string name, bool needExt = true) where T : UnityEngine.Object |
| | | { |
| | | T asset = null; |
| | | directory = directory.Replace("\\", "/"); |
| | | name = name.Replace("\\", "/"); |
| | | // 特殊处理 因为有一层图集的关系 directory要传入的应该是atlas的名字 |
| | | if (typeof(T) == typeof(Sprite)) |
| | | { |
| | |
| | | directory += name.Substring(0, name.LastIndexOf("/")); |
| | | name = name.Substring(name.LastIndexOf("/") + 1); |
| | | } |
| | | |
| | | directory = directory.Replace("\\", "/"); |
| | | } |
| | | |
| | | return LoadAssetInternal<T>(directory, name); |
| | | return LoadAssetInternal<T>(directory, name, needExt); |
| | | } |
| | | |
| | | private T LoadAssetInternal<T>(string directory, string name) where T : UnityEngine.Object |
| | | //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("\\", "/"); |
| | | |
| | | var path = ($"Assets/ResourcesOut/{directory}/{name}" + GetExtension(typeof(T))).Replace("//", "/").Trim().Replace("\\", "/"); |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | #if UNITY_EDITOR |
| | |
| | | } |
| | | else |
| | | { |
| | | if (directory == "UI" || directory == "UIComp" || directory.StartsWith("Sprite") |
| | | || directory == "Battle/Prefabs" || directory == "Materials") |
| | | { |
| | | directory = "UI/" + directory; |
| | | } |
| | | else if (name == "Hero_001") |
| | | { |
| | | directory = "UI/Hero/SpineRes"; |
| | | } |
| | | |
| | | else if (directory.Contains("Texture")) |
| | | { |
| | | directory = "maps/"+name; |
| | | } |
| | | |
| | | var assetInfo = new AssetInfo(directory.ToLower(), name.ToLower()); |
| | | asset = AssetBundleUtility.Instance.Sync_LoadAsset(assetInfo, typeof(T)) as T; |
| | | asset = YooAssetService.Instance.LoadAssetSync<T>(path); |
| | | } |
| | | |
| | | if (asset == null) |
| | |
| | | |
| | | public string[] LoadConfig(string name) |
| | | { |
| | | string path = string.Empty; |
| | | #if UNITY_EDITOR |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | path = ResourcesPath.CONFIG_FODLER + "/" + name + ".txt"; |
| | | string path = ResourcesPath.CONFIG_FODLER + "/" + name + ".txt"; |
| | | return File.ReadAllLines(path); |
| | | } |
| | | else |
| | | #endif |
| | | { |
| | | path = AssetVersionUtility.GetAssetFilePath($"config/{name}.txt"); |
| | | } |
| | | |
| | | return File.ReadAllLines(path); |
| | | // 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 |
| | | return LoadAssetInternal<Sprite>(atlasName, spriteName); |
| | | { |
| | | // YooAsset 使用完整路径直接加载 Sprite |
| | | var path = $"Assets/ResourcesOut/{atlasName}/{spriteName}.png".Replace("//", "/"); |
| | | return YooAssetService.Instance.LoadAssetSync<Sprite>(path); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | public void LoadAssetAsync<T>(string directory, string name, Action<bool, UnityEngine.Object> callBack) where T : UnityEngine.Object |
| | | // ==================================================================== |
| | | // 异步加载方法(所有平台统一) |
| | | // ==================================================================== |
| | | |
| | | //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); |
| | | 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, CancellationToken ct = default) where T : UnityEngine.Object |
| | | { |
| | | directory = directory.Replace("\\", "/"); |
| | | name = name.Replace("\\", "/"); |
| | | |
| | | if (typeof(T) == typeof(Sprite)) |
| | | { |
| | | return await LoadSpriteAsyncUniTask(directory, name, ct) as T; |
| | | } |
| | | |
| | | if (typeof(T) == typeof(SkeletonDataAsset)) |
| | | { |
| | | if (name.Contains("/")) |
| | | { |
| | | directory += name.Substring(0, name.LastIndexOf("/")); |
| | | name = name.Substring(name.LastIndexOf("/") + 1); |
| | | } |
| | | } |
| | | |
| | | var path = ($"Assets/ResourcesOut/{directory}/{name}" + (needExt ? GetExtension(typeof(T)) : "")) |
| | | .Replace("//", "/").Trim().Replace("\\", "/"); |
| | | // 音频文件扩展名统一转小写,避免 .WAV/.MP3 大小写不匹配 |
| | | if (typeof(T) == typeof(AudioClip)) |
| | | { |
| | | var pathExt = System.IO.Path.GetExtension(path); |
| | | if (!string.IsNullOrEmpty(pathExt) && pathExt != pathExt.ToLower()) |
| | | path = path.Substring(0, path.Length - pathExt.Length) + pathExt.ToLower(); |
| | | } |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | #if UNITY_EDITOR |
| | | return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path); |
| | | #else |
| | | return null; |
| | | #endif |
| | | } |
| | | |
| | | 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 !UNITY_EDITOR |
| | | LoadAssetAsync<SpriteAtlas>(atlasName, spriteName, (isLoaded, atlas) => { |
| | | if (isLoaded) |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | // Editor 模式下可直接加载 sprite |
| | | LoadAssetAsyncInternal<T>(atlasName, spriteName, callBack); |
| | | } |
| | | else |
| | | { |
| | | // AB 模式下直接加载单独的 Sprite 文件(YooAsset 自动处理 SpriteAtlas 依赖) |
| | | LoadAssetAsyncInternal<Sprite>(atlasName, spriteName, (isLoaded, sprite) => |
| | | { |
| | | SpriteAtlas _atlas = atlas as SpriteAtlas; |
| | | callBack?.Invoke(isLoaded, _atlas.GetSprite(spriteName)); |
| | | } |
| | | else |
| | | { |
| | | callBack?.Invoke(false, null); |
| | | } |
| | | }); |
| | | #else |
| | | // 编辑器下可以直接加载没啥问题 |
| | | LoadAssetAsyncInternal<T>(atlasName, spriteName, callBack); |
| | | #endif |
| | | callBack?.Invoke(isLoaded, sprite); |
| | | }); |
| | | } |
| | | } |
| | | |
| | | private void LoadAssetAsyncInternal<T>(string directory, string name, Action<bool, UnityEngine.Object> callBack) where T : UnityEngine.Object |
| | | 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}", GetExtension(typeof(T))).Replace("//", "/"); |
| | | var path = string.Concat($"Assets/ResourcesOut/{directory}/{name}", (needExt ? GetExtension(typeof(T)) : "")).Replace("//", "/"); |
| | | |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | var assetInfo = new AssetInfo(directory.ToLower(), name.ToLower()); |
| | | AssetBundleUtility.Instance.Co_LoadAsset(assetInfo, callBack); |
| | | CoLoadViaYooAsset<T>(path, callBack).Forget(); |
| | | } |
| | | } |
| | | |
| | | public void UnloadAsset(string assetBundleName, string assetName) |
| | | private async UniTaskVoid CoLoadViaYooAsset<T>(string path, Action<bool, UnityEngine.Object> callBack, CancellationToken ct = default) where T : UnityEngine.Object |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | return; |
| | | |
| | | AssetBundleUtility.Instance.UnloadAsset(assetBundleName, assetName); |
| | | 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 UnloadAssetBundle(string assetBundleName, bool unloadAllLoadedObjects, bool includeDependenice) |
| | | public void UnloadAsset(string directory, string assetName) |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | return; |
| | | AssetBundleUtility.Instance.UnloadAssetBundle(assetBundleName, unloadAllLoadedObjects, includeDependenice); |
| | | |
| | | directory = directory.Replace("\\", "/").TrimEnd('/'); |
| | | assetName = assetName.Replace("\\", "/"); |
| | | string path = ($"Assets/ResourcesOut/{directory}/{assetName}").Replace("//", "/"); |
| | | YooAssetService.Instance.UnloadAsset(path); |
| | | } |
| | | |
| | | public string GetAssetFilePath(string _assetKey) |
| | |
| | | return path; |
| | | } |
| | | |
| | | // ==================================================================== |
| | | // LoadConfigAsync(所有平台统一) |
| | | // ==================================================================== |
| | | |
| | | } |
| | | /// <summary> |
| | | /// 异步加载配置文件。 |
| | | /// 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 |
| | | { |
| | | directory = directory.Replace("\\", "/"); |
| | | name = name.Replace("\\", "/"); |
| | | |
| | | var path = ($"Assets/ResourcesOut/{directory}/{name}" + (needExt ? GetExtension(typeof(T)) : "")) |
| | | .Replace("//", "/").Trim().Replace("\\", "/"); |
| | | // 音频文件扩展名统一转小写 |
| | | if (typeof(T) == typeof(AudioClip)) |
| | | { |
| | | var pathExt2 = System.IO.Path.GetExtension(path); |
| | | if (!string.IsNullOrEmpty(pathExt2) && pathExt2 != pathExt2.ToLower()) |
| | | path = path.Substring(0, path.Length - pathExt2.Length) + pathExt2.ToLower(); |
| | | } |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | #if UNITY_EDITOR |
| | | return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path); |
| | | #else |
| | | return null; |
| | | #endif |
| | | } |
| | | |
| | | return await ResourceCacheManager.Instance.GetOrLoadAsync<T>(path); |
| | | } |
| | | |
| | | private async UniTask<Sprite> LoadSpriteAsyncUniTask(string atlasName, string spriteName, CancellationToken ct = default) |
| | | { |
| | | if (!AssetSource.isUseAssetBundle) |
| | | { |
| | | var atlas = await LoadAssetAsync<SpriteAtlas>("Sprite", atlasName.Replace("Sprite/", ""), ct: ct); |
| | | return atlas?.GetSprite(spriteName); |
| | | } |
| | | else |
| | | { |
| | | var path = $"Assets/ResourcesOut/{atlasName}/{spriteName}.png" |
| | | .Replace("//", "/").Trim().Replace("\\", "/"); |
| | | var sprite = await YooAssetService.Instance.LoadAssetAsync<Sprite>(path, ct: ct); |
| | | if (sprite == null) |
| | | Debug.LogWarning($"[ResManager] Sprite load returned NULL: path={path}"); |
| | | return sprite; |
| | | } |
| | | } |
| | | |
| | | } |