yyl
2026-02-12 5667c0a5e6b83964e2538a17dcfd6b719692ddda
编辑器 模拟使用资源问题处理
7个文件已修改
179 ■■■■ 已修改文件
Main/Core/GameEngine/Launch/YooAssetInitTask.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/StageManager.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/ResModule/BuiltInLoader.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/ResModule/ResManager.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/ResModule/ResourcePreloader.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/ResModule/YooAssetService.cs 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/FontUtility.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/GameEngine/Launch/YooAssetInitTask.cs
@@ -48,7 +48,12 @@
            }
            else
            {
#if UNITY_EDITOR
                // 编辑器下 AB 模式使用 OfflinePlayMode(随包模式),从 StreamingAssets 加载
                playMode = EPlayMode.OfflinePlayMode;
#else
                playMode = EPlayMode.HostPlayMode;
#endif
            }
            // Initialize YooAssetService
Main/Manager/StageManager.cs
@@ -46,7 +46,7 @@
        if (AssetSource.isUseAssetBundle)
        {
            loadingWin.SetProgress(0.05f);
            await YooAssetService.Instance.LoadAllAssetsAsync<UnityEngine.Object>("Assets/ResourcesOut/maps/Login");
            await YooAssetService.Instance.LoadAllAssetsAsync<UnityEngine.Object>("Assets/ResourcesOut/Scenes/Login");
            loadingWin.SetProgress(0.3f);
        }
@@ -144,7 +144,7 @@
        if (AssetSource.isUseAssetBundle)
        {
            loadingWin.SetProgress(0.05f);
            await YooAssetService.Instance.LoadAllAssetsAsync<UnityEngine.Object>("Assets/ResourcesOut/maps/Game");
            await YooAssetService.Instance.LoadAllAssetsAsync<UnityEngine.Object>("Assets/ResourcesOut/Scenes/Game");
            loadingWin.SetProgress(0.3f);
        }
Main/ResModule/BuiltInLoader.cs
@@ -204,7 +204,7 @@
        {
#if UNITY_EDITOR
            var path = StringUtility.Concat(ResourcesPath.ResourcesOutAssetPath,
                                       "BuiltIn/Font/", fontName, ".ttf");
                                       "Font/", fontName, ".ttf");
            font = UnityEditor.AssetDatabase.LoadAssetAtPath<Font>(path);
#endif
        }
@@ -212,7 +212,7 @@
        {
            // US1: Route through YooAssetService sync wrapper
            var path = StringUtility.Concat(ResourcesPath.ResourcesOutAssetPath,
                                       "BuiltIn/Font/", fontName, ".ttf");
                                       "Font/", fontName, ".ttf");
            #pragma warning disable CS0612
            font = YooAssetService.Instance.LoadAssetSync<Font>(path);
            #pragma warning restore CS0612
@@ -270,7 +270,7 @@
    public static async UniTask<Font> LoadFontAsync(string fontName, CancellationToken ct = default)
    {
        var path = StringUtility.Concat(ResourcesPath.ResourcesOutAssetPath,
                                       "BuiltIn/Font/", fontName, ".ttf");
                                       "Font/", fontName, ".ttf");
        return await YooAssetService.Instance.LoadAssetAsync<Font>(path, ct: ct);
    }
Main/ResModule/ResManager.cs
@@ -388,29 +388,35 @@
    /// <summary>
    /// 异步加载配置文件(UniTask 版本)。
    /// WebGL 平台使用 YooAsset RawFile 异步加载,其他平台使用线程池。
    /// AB 模式使用 YooAsset RawFile 异步加载,非 AB 模式直接读文件。
    /// </summary>
    public async UniTask<string[]> LoadConfigAsync(string name, CancellationToken ct = default)
    {
#if UNITY_WEBGL && !UNITY_EDITOR
        // WebGL 不支持多线程和 File.ReadAllLines,使用 YooAsset RawFile
        try
        // AB 模式(含 WebGL): 使用 YooAsset RawFile 加载(配置文件在 YooAsset 沙盒中)
        if (AssetSource.isUseAssetBundle)
        {
            var text = await ProjSG.Resource.YooAssetService.Instance.LoadRawFileTextAsync($"config/{name}", ct);
            if (!string.IsNullOrEmpty(text))
            try
            {
                return text.Split(new[] { "\r\n", "\n" }, System.StringSplitOptions.None);
                var location = $"Assets/ResourcesOut/Config/{name}.txt";
                var text = await ProjSG.Resource.YooAssetService.Instance.LoadRawFileTextAsync(location, ct);
                if (!string.IsNullOrEmpty(text))
                {
                    return text.Split(new[] { "\r\n", "\n" }, System.StringSplitOptions.None);
                }
            }
            catch (System.Exception ex)
            {
                UnityEngine.Debug.LogError($"[ResManager] LoadConfigAsync YooAsset failed for '{name}': {ex.Message}");
            }
            return System.Array.Empty<string>();
        }
        catch (System.Exception ex)
        {
            UnityEngine.Debug.LogError($"[ResManager] LoadConfigAsync WebGL fallback failed for '{name}': {ex.Message}");
        }
        return System.Array.Empty<string>();
        // 非 AB 模式: 直接读文件(Editor 开发模式)
#if UNITY_EDITOR
        string path = ResourcesPath.CONFIG_FODLER + "/" + name + ".txt";
        return await UniTask.RunOnThreadPool(() => File.ReadAllLines(path));
#else
        #pragma warning disable CS0618 // LoadConfig is obsolete — used here as thread-pool fallback for non-WebGL
        return await UniTask.RunOnThreadPool(() => LoadConfig(name));
        #pragma warning restore CS0618
        return System.Array.Empty<string>();
#endif
    }
Main/ResModule/ResourcePreloader.cs
@@ -29,16 +29,15 @@
        public void RegisterDefaultConfigs()
        {
            // 启动必需资源(常驻)
            // 注意:路径必须是具体的资源文件(含扩展名或 YooAsset 可识别的无扩展名路径),
            // 不能是目录路径。Shader/Materials 等目录级加载由 ShaderUtility 等专用系统处理。
            RegisterConfig(new PreloadConfig
            {
                ConfigName = "StartupEssential",
                Locations = new[]
                {
                    "Assets/ResourcesOut/Shader",                // Shader 全部
                    "Assets/ResourcesOut/Materials",             // 通用 Material
                    "Assets/ResourcesOut/BuiltIn/Font",          // 常用字体
                    "Assets/ResourcesOut/BuiltIn/UIRoot",        // UIRoot 预制体
                    "Assets/ResourcesOut/BuiltIn/SoundPlayer",   // 音频播放器
                    "Assets/ResourcesOut/BuiltIn/Prefabs/UIRoot.prefab",          // UIRoot 预制体
                    "Assets/ResourcesOut/BuiltIn/Prefabs/SoundPlayer.prefab",     // 音频播放器
                },
                Tags = null,
                IsPermanent = true,
Main/ResModule/YooAssetService.cs
@@ -70,7 +70,47 @@
                    var package = YooAssets.TryGetPackage(pkgName);
                    if (package != null)
                    {
                        Debug.Log($"[YooAssetService] Reusing existing package '{pkgName}' from YooAssetInitializer");
                        // 验证包裹是否已成功初始化
                        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.LogWarning($"[YooAssetService] Package '{pkgName}' re-init failed: {initOp.Error}");
                                continue;
                            }
                            Debug.Log($"[YooAssetService] Package '{pkgName}' re-initialized successfully.");
                            // 重新初始化后必须请求版本并更新 Manifest
                            await RequestVersionAndUpdateForPackageAsync(pkgName, package);
                        }
                    }
                    else
                    {
@@ -85,6 +125,9 @@
                            Debug.LogWarning($"[YooAssetService] Package '{pkgName}' init failed: {initOp.Error}");
                            continue;
                        }
                        // 初始化后必须请求版本并更新 Manifest,否则 ActiveManifest 为 null
                        await RequestVersionAndUpdateForPackageAsync(pkgName, package);
                        Debug.Log($"[YooAssetService] Package '{pkgName}' newly initialized.");
                    }
@@ -112,7 +155,22 @@
            }
            _isInitialized = true;
            Debug.Log($"[YooAssetService] Initialized {_packages.Count}/{YooAssetPackageConfig.AllPackages.Length} packages with PlayMode={playMode}");
            // 输出初始化摘要 — 帮助诊断缺失的包裹
            var allPkgs = YooAssetPackageConfig.AllPackages;
            var missingPkgs = new System.Collections.Generic.List<string>();
            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)}]");
        }
        /// <summary>
@@ -129,6 +187,7 @@
            // 优先复用已存在的包裹(可能由 Launch 阶段创建)
            var package = YooAssets.TryGetPackage(packageName);
            bool needManifest = false;
            if (package == null)
            {
                package = YooAssets.CreatePackage(packageName);
@@ -141,9 +200,17 @@
                    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.");
        }
@@ -206,6 +273,40 @@
                }
                default:
                    throw new ArgumentOutOfRangeException(nameof(playMode), playMode, "Unsupported PlayMode.");
            }
        }
        /// <summary>
        /// 对单个包裹执行版本请求和 Manifest 更新。
        /// 所有运行模式(包括 EditorSimulateMode)都需要此步骤来填充 ActiveManifest。
        /// </summary>
        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}");
            }
        }
@@ -277,7 +378,11 @@
            if (_packages.TryGetValue(packageName, out var package))
                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;
        }
Main/Utility/FontUtility.cs
@@ -7,17 +7,17 @@
public class FontUtility
{
    // T044: Fonts must be pre-loaded via StartupEssential preload config
    // (location: "Assets/ResourcesOut/BuiltIn/Font")
    // T044: Fonts loaded via FontUtility.InitAsync or UILoader.LoadFont
    // Actual location: Assets/ResourcesOut/Font/
    public static Font preferred
    {
        get { return ResourceCacheManager.Instance.GetCached<Font>("Assets/ResourcesOut/BuiltIn/Font/GameFont1.ttf"); }
        get { return ResourceCacheManager.Instance.GetCached<Font>("Assets/ResourcesOut/Font/GameFont1.ttf"); }
    }
    public static Font secondary
    {
        get { return ResourceCacheManager.Instance.GetCached<Font>("Assets/ResourcesOut/BuiltIn/Font/GameFont2.ttf"); }
        get { return ResourceCacheManager.Instance.GetCached<Font>("Assets/ResourcesOut/Font/GameFont2.ttf"); }
    }
    /// <summary>