// ============================================================================ // YooAssetService.cs — YooAsset 封装服务 // 实现 IYooAssetService 和 IYooAssetBridge,替代 AssetBundleUtility // ============================================================================ using System; using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.SceneManagement; using YooAsset; namespace ProjSG.Resource { /// /// YooAsset 资源加载服务单例。 /// 封装 YooAsset ResourcePackage 的核心加载能力,提供 UniTask 异步 API。 /// 同时实现 IYooAssetBridge 供 Launch 程序集跨程序集调用。 /// public class YooAssetService : Singleton, IYooAssetService, IYooAssetBridge { private readonly Dictionary _packages = new Dictionary(); private ResourcePackage _defaultPackage; private IRemoteServices _remoteServices; private bool _isInitialized; private EPlayMode _playMode; // ==================================================================== // IYooAssetService Properties // ==================================================================== /// public bool IsInitialized => _isInitialized; /// public EPlayMode PlayMode => _playMode; // ==================================================================== // IYooAssetBridge Properties // ==================================================================== bool IYooAssetBridge.IsRegistered => _isInitialized; // ==================================================================== // Initialization // ==================================================================== /// public async UniTask InitializeAsync(EPlayMode playMode, IRemoteServices remoteServices = null) { if (_isInitialized) { Debug.LogWarning("[YooAssetService] Already initialized."); return; } _playMode = playMode; _remoteServices = remoteServices; // YooAsset 全局初始化(幂等操作) YooAssets.Initialize(); // 初始化所有配置中的包裹 foreach (var pkgName in YooAssetPackageConfig.AllPackages) { try { // 优先复用 Launch 阶段已创建的包裹 var package = YooAssets.TryGetPackage(pkgName); if (package != null) { // 验证包裹是否已成功初始化 if (package.InitializeStatus == EOperationStatus.Succeed) { Debug.Log($"[YooAssetService] Reusing existing package '{pkgName}' (InitializeStatus=Succeed)"); } else { // 包裹存在但初始化未完成或失败(僵尸包裹) // 销毁后重新创建并初始化 Debug.LogWarning($"[YooAssetService] Package '{pkgName}' exists but InitializeStatus={package.InitializeStatus}, destroying and re-creating..."); // 根据状态清理僵尸包裹 if (package.InitializeStatus == EOperationStatus.None) { // 未初始化状态可以直接移除 YooAssets.RemovePackage(pkgName); } else { // Failed/Processing 状态需先销毁再移除 var destroyOp = package.DestroyAsync(); await destroyOp.ToUniTask(); YooAssets.RemovePackage(pkgName); } package = YooAssets.CreatePackage(pkgName); var initParams = CreateInitParameters(playMode, remoteServices, pkgName); var initOp = package.InitializeAsync(initParams); await initOp.ToUniTask(); if (initOp.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] Package '{pkgName}' re-init failed: {initOp.Error}"); try { var d = package.DestroyAsync(); await d.ToUniTask(); YooAssets.RemovePackage(pkgName); } catch { } continue; } Debug.Log($"[YooAssetService] Package '{pkgName}' re-initialized successfully."); // 重新初始化后必须请求版本并更新 Manifest await RequestVersionAndUpdateForPackageAsync(pkgName, package); } } else { // 自行创建并初始化(首次启动或该包未在 Launch 阶段创建) package = YooAssets.CreatePackage(pkgName); var initParams = CreateInitParameters(playMode, remoteServices, pkgName); var initOp = package.InitializeAsync(initParams); await initOp.ToUniTask(); if (initOp.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] Package '{pkgName}' init failed: {initOp.Error}"); try { var dOp = package.DestroyAsync(); await dOp.ToUniTask(); YooAssets.RemovePackage(pkgName); } catch { } continue; } // 初始化后必须请求版本并更新 Manifest,否则 ActiveManifest 为 null await RequestVersionAndUpdateForPackageAsync(pkgName, package); Debug.Log($"[YooAssetService] Package '{pkgName}' newly initialized."); } _packages[pkgName] = package; // 设置默认包 if (_defaultPackage == null || pkgName == YooAssetPackageConfig.DefaultPackage) { _defaultPackage = package; YooAssets.SetDefaultPackage(package); } } catch (Exception ex) { // EditorSimulateMode 下包不在 Collector 中会抛异常,跳过 Debug.LogWarning($"[YooAssetService] Package '{pkgName}' init exception (skipped): {ex.Message}"); } } if (_defaultPackage == null) { Debug.LogError("[YooAssetService] No packages initialized successfully!"); throw new InvalidOperationException("YooAsset initialization failed: no packages available."); } _isInitialized = true; // 输出初始化摘要 — 帮助诊断缺失的包裹 var allPkgs = YooAssetPackageConfig.AllPackages; var missingPkgs = new System.Collections.Generic.List(); foreach (var p in allPkgs) { if (!_packages.ContainsKey(p)) missingPkgs.Add(p); } if (missingPkgs.Count > 0) { Debug.LogError($"[YooAssetService] WARNING: {missingPkgs.Count} package(s) FAILED to initialize: [{string.Join(", ", missingPkgs)}]. " + $"Assets routed to these packages will fail to load! Check earlier console errors for SimulateBuild failures."); } Debug.Log($"[YooAssetService] Initialized {_packages.Count}/{allPkgs.Length} packages with PlayMode={playMode}. " + $"Active: [{string.Join(", ", _packages.Keys)}]"); // 诊断:输出每个包的状态(Debug 构建下始终输出,方便排查 location is invalid 等问题) #if UNITY_DEBUG || UNITY_EDITOR || DEVELOPMENT_BUILD DiagDumpPackageStatus(); #endif } /// /// 初始化指定名称的额外资源包裹。 /// public async UniTask InitializePackageAsync(string packageName, EPlayMode playMode, IRemoteServices remoteServices = null) { if (_packages.ContainsKey(packageName)) { Debug.LogWarning($"[YooAssetService] Package '{packageName}' already initialized."); return; } // 优先复用已存在的包裹(可能由 Launch 阶段创建) var package = YooAssets.TryGetPackage(packageName); bool needManifest = false; if (package == null) { package = YooAssets.CreatePackage(packageName); var initParams = CreateInitParameters(playMode, remoteServices ?? _remoteServices, packageName); var initOp = package.InitializeAsync(initParams); await initOp.ToUniTask(); if (initOp.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] Initialize package '{packageName}' failed: {initOp.Error}"); throw new InvalidOperationException($"YooAsset package '{packageName}' initialization failed: {initOp.Error}"); } needManifest = true; } _packages[packageName] = package; // 确保 ActiveManifest 已加载 if (needManifest) { await RequestVersionAndUpdateForPackageAsync(packageName, package); } Debug.Log($"[YooAssetService] Package '{packageName}' initialized."); } private InitializeParameters CreateInitParameters(EPlayMode playMode, IRemoteServices remoteServices, string packageName) { switch (playMode) { case EPlayMode.EditorSimulateMode: { #if UNITY_EDITOR var simulateResult = EditorSimulateModeHelper.SimulateBuild(packageName); return new EditorSimulateModeParameters { EditorFileSystemParameters = FileSystemParameters .CreateDefaultEditorFileSystemParameters(simulateResult.PackageRootDirectory) }; #else throw new InvalidOperationException("EditorSimulateMode is only available in Unity Editor."); #endif } case EPlayMode.HostPlayMode: { bool hasBuildin = YooAssetInitializer.HasBuildinPackage(packageName); return new HostPlayModeParameters { BuildinFileSystemParameters = hasBuildin ? FileSystemParameters.CreateDefaultBuildinFileSystemParameters() : null, CacheFileSystemParameters = FileSystemParameters .CreateDefaultCacheFileSystemParameters(remoteServices) }; } case EPlayMode.OfflinePlayMode: { return new OfflinePlayModeParameters { BuildinFileSystemParameters = FileSystemParameters .CreateDefaultBuildinFileSystemParameters() }; } case EPlayMode.WebPlayMode: { var webParams = new WebPlayModeParameters(); #if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITOR string packageRoot = $"{WeChatWASM.WX.env.USER_DATA_PATH}/__GAME_FILE_CACHE"; webParams.WebServerFileSystemParameters = WechatFileSystemCreater .CreateFileSystemParameters(packageRoot, remoteServices); #elif UNITY_WEBGL && DOUYINMINIGAME && !UNITY_EDITOR string packageRoot = TTSDK.TTFileSystem.USER_DATA_PATH + "/__GAME_FILE_CACHE"; webParams.WebServerFileSystemParameters = TiktokFileSystemCreater .CreateFileSystemParameters(packageRoot, remoteServices); #else if (remoteServices != null) { // 远程模式(LocalCDN/RemoteCDN):资源不在 StreamingAssets, // 跳过 WebServerFileSystem,只用 WebRemoteFileSystem 从 HTTP 服务器加载 webParams.WebServerFileSystemParameters = FileSystemParameters .CreateDefaultWebRemoteFileSystemParameters(remoteServices); } else { // Local 模式:资源在 StreamingAssets,用 WebServerFileSystem webParams.WebServerFileSystemParameters = FileSystemParameters .CreateDefaultWebServerFileSystemParameters(); } #endif return webParams; } default: throw new ArgumentOutOfRangeException(nameof(playMode), playMode, "Unsupported PlayMode."); } } /// /// 对单个包裹执行版本请求和 Manifest 更新。 /// 所有运行模式(包括 EditorSimulateMode)都需要此步骤来填充 ActiveManifest。 /// private async UniTask RequestVersionAndUpdateForPackageAsync(string pkgName, ResourcePackage package) { try { var versionOp = package.RequestPackageVersionAsync(); await versionOp.ToUniTask(); if (versionOp.Status != EOperationStatus.Succeed) { Debug.LogWarning($"[YooAssetService] RequestPackageVersion failed for '{pkgName}': {versionOp.Error}"); return; } var manifestOp = package.UpdatePackageManifestAsync(versionOp.PackageVersion); await manifestOp.ToUniTask(); if (manifestOp.Status != EOperationStatus.Succeed) { Debug.LogWarning($"[YooAssetService] UpdatePackageManifest failed for '{pkgName}': {manifestOp.Error}"); return; } Debug.Log($"[YooAssetService] Package '{pkgName}' manifest loaded (version={versionOp.PackageVersion})."); } catch (Exception ex) { Debug.LogWarning($"[YooAssetService] RequestVersionAndUpdate for '{pkgName}' exception: {ex.Message}"); } } // ==================================================================== // Asset Loading // ==================================================================== /// /// 资源加载重试配置 /// private const int MAX_RETRY_COUNT = 3; private const int BASE_RETRY_DELAY_MS = 500; // 500ms, 1000ms, 2000ms (exponential) /// /// 带重试的异步操作执行器。 /// 使用指数退避策略(500ms → 1000ms → 2000ms)。 /// /// 要执行的异步操作 /// 操作名称(用于日志) /// 取消令牌 /// 操作结果 private async UniTask ExecuteWithRetryAsync( Func> operation, string operationName, CancellationToken ct = default) { Exception lastException = null; for (int attempt = 0; attempt <= MAX_RETRY_COUNT; attempt++) { try { ct.ThrowIfCancellationRequested(); return await operation(); } catch (OperationCanceledException) { throw; // Don't retry cancellations } catch (Exception ex) { lastException = ex; if (attempt < MAX_RETRY_COUNT) { int delayMs = BASE_RETRY_DELAY_MS * (1 << attempt); // Exponential backoff Debug.LogWarning($"[YooAssetService] {operationName} failed (attempt {attempt + 1}/{MAX_RETRY_COUNT + 1}), retrying in {delayMs}ms: {ex.Message}"); await UniTask.Delay(delayMs, cancellationToken: ct); } } } Debug.LogError($"[YooAssetService] {operationName} failed after {MAX_RETRY_COUNT + 1} attempts: {lastException?.Message}"); return default; } private void ThrowIfNotInitialized() { if (!_isInitialized) throw new InvalidOperationException("[YooAssetService] Service not initialized. Call InitializeAsync first."); } /// /// 根据资源路径查找应使用的 ResourcePackage。 /// 使用 YooAssetPackageConfig 路由表确定目标包,找不到则回退到默认包。 /// private ResourcePackage FindPackageForAsset(string location) { var packageName = YooAssetPackageConfig.GetPackageForLocation(location); if (_packages.TryGetValue(packageName, out var package)) { // 在加载前检查 location 是否在该包的 Manifest 里,提前暴露问题 if (!package.CheckLocationValid(location)) { // 收集所有包的 CheckLocationValid 结果 var validIn = new System.Collections.Generic.List(); foreach (var kv in _packages) if (kv.Value.CheckLocationValid(location)) validIn.Add(kv.Key); Debug.LogError( $"[YooAssetService][★INVALID-LOCATION★] location='{location}' 路由到包='{packageName}'" + $" 但 CheckLocationValid=False!\n" + $" → 包状态: {package.InitializeStatus}\n" + $" → 该资源在以下包中有效: [{(validIn.Count > 0 ? string.Join(", ", validIn) : "无")}]\n" + $" → 可能原因: ①包未重新打包导致 Manifest 缺少此资源 ②资源路径拼写错误 ③包初始化失败 Manifest 为空"); } return package; } // 路由到的包尚未初始化,回退到默认包 — 发出明确警告 Debug.LogWarning($"[YooAssetService] Package '{packageName}' not available for location '{location}'. " + $"Available packages: [{string.Join(", ", _packages.Keys)}]. " + $"Falling back to default package '{_defaultPackage?.PackageName ?? "NULL"}'." + $"\n → This usually means SimulateBuild failed for '{packageName}'. Check earlier console errors."); return _defaultPackage; } /// public async UniTask LoadAssetAsync(string location, uint priority = 0, CancellationToken ct = default) where T : UnityEngine.Object { ThrowIfNotInitialized(); if (string.IsNullOrEmpty(location)) { Debug.LogError("[YooAssetService] LoadAssetAsync: location is null or empty."); return null; } var package = FindPackageForAsset(location); return await ExecuteWithRetryAsync(async () => { var handle = package.LoadAssetAsync(location, priority); await handle.ToUniTask(cancellationToken: ct); if (handle.Status != EOperationStatus.Succeed) { throw new InvalidOperationException($"LoadAssetAsync failed for '{location}': {handle.LastError}"); } return handle.GetAssetObject(); }, $"LoadAssetAsync<{typeof(T).Name}>('{location}')", ct); } public void UnloadAsset(string location) { ThrowIfNotInitialized(); if (string.IsNullOrEmpty(location)) { Debug.LogError("[YooAssetService] UnloadAsset: location is null or empty."); return; } var package = FindPackageForAsset(location); if (null != package) { YooAsset.AssetInfo assetInfo = package.GetAssetInfo(location); if (null != assetInfo) { package.TryUnloadUnusedAsset(assetInfo); } } } /// public async UniTask LoadAssetAsync(string location, Type type, uint priority = 0, CancellationToken ct = default) { ThrowIfNotInitialized(); if (string.IsNullOrEmpty(location)) { Debug.LogError("[YooAssetService] LoadAssetAsync: location is null or empty."); return null; } var package = FindPackageForAsset(location); // Debug.LogError($"[YooAssetService] LoadAssetAsync: Loading asset '{location}' of package '{package.PackageName}'."); return await ExecuteWithRetryAsync(async () => { var handle = package.LoadAssetAsync(location, type, priority); await handle.ToUniTask(cancellationToken: ct); if (handle.Status != EOperationStatus.Succeed) { throw new InvalidOperationException($"LoadAssetAsync failed for '{location}': {handle.LastError}"); } return handle.AssetObject; }, $"LoadAssetAsync('{location}', {type.Name})", ct); } /// /// 同步加载资产(非 WebGL 平台使用)。 /// WebGL 不支持同步加载,请使用 LoadAssetAsync。 /// public T LoadAssetSync(string location) where T : UnityEngine.Object { ThrowIfNotInitialized(); if (string.IsNullOrEmpty(location)) { Debug.LogError("[YooAssetService] LoadAssetSync: location is null or empty."); return null; } var package = FindPackageForAsset(location); var handle = package.LoadAssetSync(location); if (handle.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] LoadAssetSync failed for '{location}': {handle.LastError}"); return null; } return handle.GetAssetObject(); } /// public async UniTask LoadSubAssetsAsync(string location, uint priority = 0, CancellationToken ct = default) where T : UnityEngine.Object { ThrowIfNotInitialized(); var package = FindPackageForAsset(location); var handle = package.LoadSubAssetsAsync(location, priority); await handle.ToUniTask(); ct.ThrowIfCancellationRequested(); if (handle.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] LoadSubAssetsAsync failed for '{location}': {handle.LastError}"); } return handle; } /// public async UniTask LoadAllAssetsAsync(string location, uint priority = 0, CancellationToken ct = default) where T : UnityEngine.Object { ThrowIfNotInitialized(); var package = FindPackageForAsset(location); var handle = package.LoadAllAssetsAsync(location, priority); await handle.ToUniTask(); ct.ThrowIfCancellationRequested(); if (handle.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] LoadAllAssetsAsync failed for '{location}': {handle.LastError}"); } return handle; } // ==================================================================== // RawFile Loading // ==================================================================== /// public async UniTask LoadRawFileTextAsync(string location, CancellationToken ct = default) { ThrowIfNotInitialized(); var rawPackage = FindPackageForAsset(location); return await ExecuteWithRetryAsync(async () => { var handle = rawPackage.LoadRawFileAsync(location); await handle.ToUniTask(cancellationToken: ct); if (handle.Status != EOperationStatus.Succeed) { throw new InvalidOperationException($"LoadRawFileTextAsync failed for '{location}': {handle.LastError}"); } #if UNITY_ANDROID && !UNITY_EDITOR // 真机:StreamingAssets在apk内,需用UnityWebRequest读取 string filePath = handle.GetRawFilePath(); using var uwr = UnityEngine.Networking.UnityWebRequest.Get(filePath); await uwr.SendWebRequest().ToUniTask(cancellationToken: ct); if (uwr.result != UnityEngine.Networking.UnityWebRequest.Result.Success) throw new InvalidOperationException($"LoadRawFileTextAsync UWR failed for '{location}': {uwr.error}"); return uwr.downloadHandler.text; #else return handle.GetRawFileText(); #endif }, $"LoadRawFileTextAsync('{location}')", ct); } /// public async UniTask LoadRawFileBytesAsync(string location, CancellationToken ct = default) { ThrowIfNotInitialized(); var rawPackage = FindPackageForAsset(location); return await ExecuteWithRetryAsync(async () => { var handle = rawPackage.LoadRawFileAsync(location); await handle.ToUniTask(cancellationToken: ct); if (handle.Status != EOperationStatus.Succeed) { throw new InvalidOperationException($"LoadRawFileBytesAsync failed for '{location}': {handle.LastError}"); } #if UNITY_ANDROID && !UNITY_EDITOR // 真机:StreamingAssets在apk内,需用UnityWebRequest读取 string filePath = handle.GetRawFilePath(); using var uwr = UnityEngine.Networking.UnityWebRequest.Get(filePath); await uwr.SendWebRequest().ToUniTask(cancellationToken: ct); if (uwr.result != UnityEngine.Networking.UnityWebRequest.Result.Success) throw new InvalidOperationException($"LoadRawFileBytesAsync UWR failed for '{location}': {uwr.error}"); return uwr.downloadHandler.data; #else return handle.GetRawFileData(); #endif }, $"LoadRawFileBytesAsync('{location}')", ct); } // ==================================================================== // Scene Loading // ==================================================================== /// /// 开始加载场景,返回 SceneHandle 供调用方自行控制进度和挂起。 /// 不会等待加载完成。 /// public SceneHandle BeginLoadScene(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, LocalPhysicsMode physicsMode = LocalPhysicsMode.None, bool suspendLoad = false, uint priority = 0) { ThrowIfNotInitialized(); var package = FindPackageForAsset(location); return package.LoadSceneAsync(location, sceneMode, physicsMode, suspendLoad, priority); } /// public async UniTask LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, LocalPhysicsMode physicsMode = LocalPhysicsMode.None, bool suspendLoad = false, uint priority = 0, CancellationToken ct = default) { ThrowIfNotInitialized(); var package = FindPackageForAsset(location); var handle = package.LoadSceneAsync(location, sceneMode, physicsMode, suspendLoad, priority); await handle.ToUniTask(); ct.ThrowIfCancellationRequested(); if (handle.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] LoadSceneAsync failed for '{location}': {handle.LastError}"); } return handle; } // ==================================================================== // Query // ==================================================================== /// public bool CheckLocationValid(string location) { ThrowIfNotInitialized(); // 先用路由包检查,找不到则遍历所有包 var package = FindPackageForAsset(location); if (package.CheckLocationValid(location)) return true; foreach (var kvp in _packages) { if (kvp.Value != package && kvp.Value.CheckLocationValid(location)) return true; } return false; } /// public YooAsset.AssetInfo[] GetAssetInfosByTag(string tag) { ThrowIfNotInitialized(); // 从所有包收集指定标签的资源信息 var allInfos = new List(); foreach (var kvp in _packages) { var infos = kvp.Value.GetAssetInfos(tag); if (infos != null && infos.Length > 0) allInfos.AddRange(infos); } return allInfos.ToArray(); } /// public bool IsNeedDownloadFromRemote(string location) { ThrowIfNotInitialized(); var package = FindPackageForAsset(location); return package.IsNeedDownloadFromRemote(location); } // ==================================================================== // Download // ==================================================================== /// public async UniTask DownloadByTagsAsync(string[] tags, int downloadingMaxNumber = 10, int failedTryAgain = 3, IProgress progress = null, CancellationToken ct = default) { ThrowIfNotInitialized(); foreach (var tag in tags) { // 对所有包按标签创建下载器 foreach (var kvp in _packages) { var downloader = kvp.Value.CreateResourceDownloader(tag, downloadingMaxNumber, failedTryAgain); if (downloader.TotalDownloadCount == 0) continue; downloader.BeginDownload(); while (!downloader.IsDone) { ct.ThrowIfCancellationRequested(); progress?.Report(downloader.Progress); await UniTask.Yield(); } if (downloader.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] Download tag '{tag}' from package '{kvp.Key}' failed: {downloader.Error}"); throw new InvalidOperationException($"Resource download failed for tag '{tag}': {downloader.Error}"); } } } progress?.Report(1f); } // ==================================================================== // Version Management // ==================================================================== /// public async UniTask RequestPackageVersionAsync(CancellationToken ct = default) { ThrowIfNotInitialized(); var op = _defaultPackage.RequestPackageVersionAsync(); await op.ToUniTask(); ct.ThrowIfCancellationRequested(); if (op.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] RequestPackageVersion failed: {op.Error}"); throw new InvalidOperationException($"Request package version failed: {op.Error}"); } return op.PackageVersion; } /// public async UniTask UpdatePackageManifestAsync(string packageVersion, CancellationToken ct = default) { ThrowIfNotInitialized(); var op = _defaultPackage.UpdatePackageManifestAsync(packageVersion); await op.ToUniTask(); ct.ThrowIfCancellationRequested(); if (op.Status != EOperationStatus.Succeed) { Debug.LogError($"[YooAssetService] UpdatePackageManifest failed: {op.Error}"); throw new InvalidOperationException($"Update package manifest failed: {op.Error}"); } } // ==================================================================== // Release // ==================================================================== /// public void ReleaseHandle(HandleBase handle) { if (handle == null) return; handle.Release(); } /// public async UniTask UnloadUnusedAssetsAsync() { ThrowIfNotInitialized(); // 对所有包执行卸载 foreach (var kvp in _packages) { var op = kvp.Value.UnloadUnusedAssetsAsync(); await op.ToUniTask(); } } /// public async UniTask UnloadAllAssetsAsync() { ThrowIfNotInitialized(); // 对所有包执行卸载 foreach (var kvp in _packages) { var op = kvp.Value.UnloadAllAssetsAsync(); await op.ToUniTask(); } } // ==================================================================== // IYooAssetBridge Implementation // ==================================================================== async UniTask IYooAssetBridge.LoadAssetAsync(string location) { return await LoadAssetAsync(location); } async UniTask IYooAssetBridge.LoadRawFileTextAsync(string location) { return await LoadRawFileTextAsync(location); } async UniTask IYooAssetBridge.LoadRawFileBytesAsync(string location) { return await LoadRawFileBytesAsync(location); } async UniTask IYooAssetBridge.PreloadAsync(string[] locations) { // 批量预加载,使用 UniTask.WhenAll 并行 var tasks = new List(locations.Length); foreach (var loc in locations) { tasks.Add(LoadAssetAsync(loc).AsUniTask()); } await UniTask.WhenAll(tasks); } T IYooAssetBridge.GetCached(string location) { // 委托给 ResourceCacheManager(US4 已集成) if (ProjSG.Resource.ResourceCacheManager.IsValid()) { return ProjSG.Resource.ResourceCacheManager.Instance.GetCached(location); } return null; } // ==================================================================== // Sync Wrappers (Transitional — removed in US2) // ==================================================================== /// /// 同步加载所有同类型资源(过渡期使用)。 /// [System.Obsolete("Use LoadAllAssetsAsync instead. Sync loading will be removed in US2.")] public AllAssetsHandle LoadAllAssetsSync(string location) where T : UnityEngine.Object { ThrowIfNotInitialized(); var package = FindPackageForAsset(location); return package.LoadAllAssetsSync(location); } #if UNITY_WEBGL || UNITY_DEBUG || UNITY_EDITOR || DEVELOPMENT_BUILD /// /// 诊断:输出每个 YooAsset 包的初始化状态和资源数量。 /// private void DiagDumpPackageStatus() { var sb = new System.Text.StringBuilder(); sb.AppendLine("[YooAssetService][Diag] Package status dump:"); foreach (var kv in _packages) { var pkg = kv.Value; var status = pkg.InitializeStatus; int assetTotalCount = -1; int bundleTotalCount = -1; string pkgVersion = "?"; try { var details = pkg.GetPackageDetails(); assetTotalCount = details.AssetTotalCount; bundleTotalCount = details.BundleTotalCount; pkgVersion = details.PackageVersion ?? "null"; } catch { } sb.AppendLine($" [{kv.Key}] status={status}, assets={assetTotalCount}, bundles={bundleTotalCount}, version={pkgVersion}"); } Debug.Log(sb.ToString()); } /// /// 诊断指定 location 在哪个包里、是否能找到。 /// 调用:YooAssetService.Instance.DiagCheckLocation("Assets/ResourcesOut/Config/Foo.txt") /// public void DiagCheckLocation(string location) { var sb = new System.Text.StringBuilder(); sb.AppendLine($"[DiagCheckLocation] location='{location}'"); string routedPkg = YooAssetPackageConfig.GetPackageForLocation(location); sb.AppendLine($" 路由到包: '{routedPkg}'"); foreach (var kv in _packages) { var pkg = kv.Value; bool valid = pkg.CheckLocationValid(location); sb.AppendLine($" [{kv.Key}] CheckLocationValid={valid}, InitStatus={pkg.InitializeStatus}"); } Debug.Log(sb.ToString()); } /// /// 打印所有已初始化包的资源数和版本(排查 Manifest 未加载问题)。 /// public void DiagDumpAllPackages() { var sb = new System.Text.StringBuilder(); sb.AppendLine("[DiagDumpAllPackages]"); foreach (var kv in _packages) { var pkg = kv.Value; int assetTotalCount = -1; int bundleTotalCount = -1; string pkgVersion = "?"; try { var details = pkg.GetPackageDetails(); assetTotalCount = details.AssetTotalCount; bundleTotalCount = details.BundleTotalCount; pkgVersion = details.PackageVersion ?? "null"; } catch { } sb.AppendLine($" [{kv.Key}] status={pkg.InitializeStatus}, assets={assetTotalCount}, bundles={bundleTotalCount}, version={pkgVersion}"); } Debug.Log(sb.ToString()); } #endif } }