三国卡牌客户端基础资源仓库
yyl
2026-06-15 7768c12faf0ed86d84ab0f30a1b58fefe31327c2
打包新增一张图片跟图片md5计算的.txt 替换给LaunchStaticWin
4个文件已修改
4个文件已添加
1219 ■■■■ 已修改文件
Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/YooAsset/YooAssetBuildTool.cs 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Launch/Launch.cs 470 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Launch/Manager/YooAssetInitializer.cs 288 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Resources/LaunchStaticWin.prefab 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Resources/LaunchStaticWin.prefab.meta 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Resources/LoginBackground.jpg 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Resources/LoginBackground.jpg.meta 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs
@@ -391,6 +391,11 @@
                EditorApplication.delayCall += ExecuteCopyToLocalCDN;
            }
            if (GUILayout.Button("一键同步启动背景(MD5+拷贝)"))
            {
                EditorApplication.delayCall += ExecuteSyncLaunchBackground;
            }
            EditorGUILayout.Space();
            EditorGUILayout.BeginHorizontal();
@@ -580,6 +585,21 @@
        private void ExecuteCopyToLocalCDN()
        {
            YooAssetBuildTool.CopyBuildOutputToLocalCDNFlat();
        }
        private void ExecuteSyncLaunchBackground()
        {
            bool ok = YooAssetBuildTool.SyncLaunchBackgroundVersionFiles(m_UserData.m_OutputPath);
            if (ok)
            {
                Debug.Log("[AssetBundleBuildTab] 启动背景已同步(MD5 + version 文件 + 图片拷贝)。");
            }
            else
            {
                Debug.LogError("[AssetBundleBuildTab] 启动背景同步失败,请查看 Console 日志。");
            }
            AssetDatabase.Refresh();
        }
        private static readonly string[] YooAssetPackageButtonOrder =
@@ -1015,7 +1035,7 @@
            {
                tempFilePath = outDllFile;
                // 只有Assembly-CSharp.dll.bytes文件才需要加密
                // if (!outDllFile.Contains("Main.dll.bytes"))
                if (!outDllFile.Contains("Main.dll.bytes"))
                {
                    FileExtersion.MakeSureDirectory(tempFilePath);
                    File.Copy(originalDllBytesFilePath, outDllFile, true);
Assets/Editor/YooAsset/YooAssetBuildTool.cs
@@ -40,6 +40,9 @@
    /// HybridCLR .bytes 文件输出目录(相对 Assets)
    /// </summary>
    private const string HYBRIDCLR_DLLS_ASSETS_PATH = "Assets/HybridCLRDlls";
    private const string LaunchBackgroundFileName = "LoginBackground.jpg";
    private const string LaunchBackgroundVersionFileName = "bg_version.txt";
    private const string LaunchBackgroundVersionAliasFileName = "version.txt";
    /// <summary>
    /// AOT 元数据 DLL 列表 —— 所有平台通用部分
@@ -583,6 +586,12 @@
            Debug.LogWarning($"[YooAssetBuildTool] 补齐输出目录启动配置失败,请检查输出目录: {destFullPath}");
        }
        if (!CopyLaunchBackgroundArtifactsToDirectory(destFullPath))
        {
            Debug.LogError($"[YooAssetBuildTool] 拷贝启动背景资源失败,目标目录: {destFullPath}");
            return false;
        }
        Debug.Log($"[YooAssetBuildTool] 已拷贝 StreamingAssets 到输出目录: {copiedCount} 个文件,from={sourceFullPath}, to={destFullPath}");
        return true;
    }
@@ -913,6 +922,103 @@
        return fileName.EndsWith("OPConfig.txt", StringComparison.OrdinalIgnoreCase);
    }
    private static bool CopyLaunchBackgroundArtifactsToDirectory(string targetDirectory)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(targetDirectory))
            {
                Debug.LogError("[YooAssetBuildTool] 启动背景输出目录为空。");
                return false;
            }
            string sourceBackground = System.IO.Path.Combine(Application.dataPath, "Resources", LaunchBackgroundFileName);
            if (!System.IO.File.Exists(sourceBackground))
            {
                Debug.LogError($"[YooAssetBuildTool] 启动背景不存在: {sourceBackground}");
                return false;
            }
            System.IO.Directory.CreateDirectory(targetDirectory);
            string targetBackground = System.IO.Path.Combine(targetDirectory, LaunchBackgroundFileName);
            System.IO.File.Copy(sourceBackground, targetBackground, true);
            string version = ComputeFileMd5(sourceBackground);
            if (string.IsNullOrEmpty(version))
            {
                Debug.LogError($"[YooAssetBuildTool] 计算启动背景版本失败: {sourceBackground}");
                return false;
            }
            string targetVersionFile = System.IO.Path.Combine(targetDirectory, LaunchBackgroundVersionFileName);
            System.IO.File.WriteAllText(targetVersionFile, version);
            string targetVersionAliasFile = System.IO.Path.Combine(targetDirectory, LaunchBackgroundVersionAliasFileName);
            System.IO.File.WriteAllText(targetVersionAliasFile, version);
            Debug.Log($"[YooAssetBuildTool] 已写入启动背景资源: {targetBackground}, version={version}");
            return true;
        }
        catch (Exception ex)
        {
            Debug.LogError($"[YooAssetBuildTool] 拷贝启动背景资源异常: {ex}");
            return false;
        }
    }
    private static string ComputeFileMd5(string filePath)
    {
        try
        {
            using (var stream = System.IO.File.OpenRead(filePath))
            using (var md5 = System.Security.Cryptography.MD5.Create())
            {
                byte[] hash = md5.ComputeHash(stream);
                var builder = new System.Text.StringBuilder(hash.Length * 2);
                for (int i = 0; i < hash.Length; i++)
                {
                    builder.Append(hash[i].ToString("x2"));
                }
                return builder.ToString();
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"[YooAssetBuildTool] 计算文件 MD5 异常: {filePath}, error={ex.Message}");
            return null;
        }
    }
    public static bool SyncLaunchBackgroundVersionFiles(string outputPath = null)
    {
        bool success = true;
        string streamingYooRoot = AssetBundleBuilderHelper.GetStreamingAssetsRoot();
        if (!CopyLaunchBackgroundArtifactsToDirectory(streamingYooRoot))
        {
            success = false;
        }
        if (!string.IsNullOrWhiteSpace(outputPath))
        {
            string destYooRoot = ResolveYooOutputDirectory(outputPath);
            string streamingFullPath = NormalizeFullPath(streamingYooRoot);
            string destFullPath = NormalizeFullPath(destYooRoot);
            if (!string.Equals(streamingFullPath, destFullPath, StringComparison.OrdinalIgnoreCase))
            {
                if (!CopyLaunchBackgroundArtifactsToDirectory(destYooRoot))
                {
                    success = false;
                }
            }
        }
        return success;
    }
    /// <summary>
    /// 将构建输出的所有 Package 最新版本文件扁平复制到本地 CDN 路径。
    /// 结构: CDN/{Platform}/{所有文件}(不含 PackageName 子目录)。
@@ -948,6 +1054,11 @@
            Debug.Log($"[YooAssetBuildTool] 已清空旧 CDN 目录: {destDir}");
        }
        System.IO.Directory.CreateDirectory(destDir);
        if (!CopyLaunchBackgroundArtifactsToDirectory(destDir))
        {
            Debug.LogWarning($"[YooAssetBuildTool] 本地 CDN 未写入启动背景资源,目录: {destDir}");
        }
        int copiedCount = 0;
        foreach (var pkgDir in System.IO.Directory.GetDirectories(srcRoot))
@@ -1347,6 +1458,7 @@
    }
    // ====================================================================
    // HybridCLR DLL 拷贝
    // ====================================================================
Assets/Launch/Launch.cs
@@ -56,13 +56,7 @@
    private GameObject launchStaticWin = null;
    private bool isOpeningLaunchUI = false;
    private bool isOpeningLaunchStaticWin = false;
    private const string PRE_SCENE_RES_PACKAGE_NAME = "PreSceneRes";
    private const string LAUNCH_STATIC_WIN_LOCATION = "Assets/ResourcesOut/PreSceneRes/LaunchStaticWin.prefab";
    private const int PRE_SCENE_RES_VERIFY_MAX_CONCURRENCY = 8;
    // 上次更新成功的 PreSceneRes 版本号(方案 A:自存版本号,下次启动本地激活,不联网)。
    private const string PRE_SCENE_RES_VERSION_KEY = "yoo_ver_PreSceneRes";
    private bool isBackgroundUpdateStarted = false;
    private static void LogTiming(string phase, float startTime)
    {
@@ -104,214 +98,40 @@
        if (isOpeningLaunchStaticWin || launchStaticWin != null)
            return;
        float startTime = Time.realtimeSinceStartup;
        float phaseStart = startTime;
        isOpeningLaunchStaticWin = true;
        try
        {
            GameObject prefab = null;
#if UNITY_EDITOR
            // Editor 模拟(非 AB 模式):直接走 AssetDatabase,秒开,便于本地调试。
            if (!AssetSource.isUseAssetBundle)
            {
                prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(LAUNCH_STATIC_WIN_LOCATION);
                if (prefab == null)
                {
                    Debug.LogError($"[Launch] LaunchStaticWin prefab not found: {LAUNCH_STATIC_WIN_LOCATION}");
                    return;
                }
                LogTiming("OpenLaunchStaticWin.Editor.LoadPrefab", phaseStart);
                float editorInstantiateStart = Time.realtimeSinceStartup;
                launchStaticWin = Instantiate(prefab);
                launchStaticWin.name = "LaunchStaticWin";
                launchStaticWin.AddComponent<DontDestroyOnLoad>();
                LogTiming("OpenLaunchStaticWin.Editor.Instantiate", editorInstantiateStart);
                return;
            }
#endif
#if UNITY_WEBGL && !UNITY_EDITOR
            // WebGL:PreSceneRes 由 YooAssetInitializer 走标准联网初始化,这里等待就绪后异步加载。
            while (YooAssetInitializer.Instance.State != YooAssetInitializer.InitState.Ready)
            {
                if (YooAssetInitializer.Instance.State == YooAssetInitializer.InitState.InitFailed ||
                    YooAssetInitializer.Instance.State == YooAssetInitializer.InitState.VersionFailed)
                {
                    Debug.LogError($"[Launch] Skip LaunchStaticWin because YooAsset state={YooAssetInitializer.Instance.State}");
                    return;
                }
                if (this == null)
                    return;
                await UniTask.Yield();
            }
            LogTiming("OpenLaunchStaticWin.WebGL.WaitReady", phaseStart);
            phaseStart = Time.realtimeSinceStartup;
            var webPackage = YooAssetInitializer.Instance.GetPackage(PRE_SCENE_RES_PACKAGE_NAME);
            if (webPackage == null)
            {
                Debug.LogError($"[Launch] Package '{PRE_SCENE_RES_PACKAGE_NAME}' not initialized. Skip LaunchStaticWin.");
                return;
            }
            var webHandle = webPackage.LoadAssetAsync<GameObject>(LAUNCH_STATIC_WIN_LOCATION);
            await webHandle.ToUniTask();
            if (webHandle.Status != EOperationStatus.Succeed)
            {
                Debug.LogError($"[Launch] Load LaunchStaticWin failed: {webHandle.LastError}");
                return;
            }
            prefab = webHandle.GetAssetObject<GameObject>();
            LogTiming("OpenLaunchStaticWin.WebGL.LoadPrefab", phaseStart);
            var prefab = await Resources.LoadAsync<GameObject>("LaunchStaticWin") as GameObject;
#else
            // Android/iOS/PC(含 Editor AB 模式):本地秒开,独立初始化 PreSceneRes,不等网络。
            YooAssets.Initialize();
            var package = YooAssets.TryGetPackage(PRE_SCENE_RES_PACKAGE_NAME)
                ?? YooAssets.CreatePackage(PRE_SCENE_RES_PACKAGE_NAME);
            // 快路径:若本地已缓存 savedVer 的 manifest/hash,则无需再触发 buildin 版本读取与复制。
            string savedVer = LocalSave.GetString(PRE_SCENE_RES_VERSION_KEY);
            bool hasSavedManifest = !string.IsNullOrEmpty(savedVer) && IsPreSceneResManifestCached(savedVer);
            UniTask<string> buildinVerTask = default;
            if (!hasSavedManifest)
            {
                // 只有在需要 buildin 回退时才读取,避免常规热更后启动的额外开销。
                buildinVerTask = ReadBuildinPreSceneResVersionAsync().Preserve();
            }
            if (package.InitializeStatus != EOperationStatus.Succeed)
            {
                if (!await InitializePreSceneResPackageAsync(package, hasSavedManifest))
                    return;
            }
            LogTiming("OpenLaunchStaticWin.Native.InitPackage", phaseStart);
            phaseStart = Time.realtimeSinceStartup;
            // 本地激活 manifest(不联网):取「上次更新保存的版本」与「包内 buildin 版本」中较新的一个。
            // 版本号格式 yyyy-MM-dd-HHmm 字典序可排序;
            // 这样重新打包带入更新的 buildin 资源时,不会被旧的 LocalSave 版本号覆盖。
            string buildinVer = null;
            string localVer;
            if (hasSavedManifest)
            {
                // 快路径:缓存版本存在时直接激活,避免额外等待读取 buildin 版本。
                localVer = savedVer;
            }
            else
            {
                buildinVer = await buildinVerTask;
                localVer = buildinVer;
            }
            if (!string.IsNullOrEmpty(savedVer) && !hasSavedManifest)
            {
                Debug.LogError($"[Launch] Saved PreSceneRes manifest missing in cache, ignore savedVer={savedVer}, use buildinVer={buildinVer}.");
            }
            if (string.IsNullOrEmpty(localVer))
            {
                Debug.LogError("[Launch] PreSceneRes local version unavailable.");
                return;
            }
            Debug.LogError($"[Launch] PreSceneRes localVer={localVer} (saved={savedVer}, buildin={buildinVer})");
            LogTiming("OpenLaunchStaticWin.Native.PickVersion", phaseStart);
            phaseStart = Time.realtimeSinceStartup;
            // 快路径:当前已激活目标版本时,无需再次加载 manifest。
            if (package.PackageValid)
            {
                string activeVer = package.GetPackageVersion();
                if (!string.IsNullOrEmpty(activeVer) && activeVer == localVer)
                {
                    var fastHandle = package.LoadAssetSync<GameObject>(LAUNCH_STATIC_WIN_LOCATION);
                    if (fastHandle.Status == EOperationStatus.Succeed)
                    {
                        prefab = fastHandle.GetAssetObject<GameObject>();
                    }
                    else
                    {
                        Debug.LogError($"[Launch] Fast-path load LaunchStaticWin failed: {fastHandle.LastError}");
                    }
                    LogTiming("OpenLaunchStaticWin.Native.FastPathLoadPrefab", phaseStart);
                    phaseStart = Time.realtimeSinceStartup;
                }
            }
            if (prefab == null)
            {
                var localManifestOp = package.UpdatePackageManifestAsync(localVer);
                await localManifestOp.ToUniTask();
                if (localManifestOp.Status != EOperationStatus.Succeed)
                {
                    // 缓存快路径可能遇到异常缓存;重建为完整 Host 文件系统后再重试一次 saved 版本。
                    if (hasSavedManifest)
                    {
                        Debug.LogError($"[Launch] Activate saved manifest v{localVer} failed, retry full host init: {localManifestOp.Error}");
                        if (!await ReinitializePreSceneResPackageWithBuildinAsync(package))
                            return;
                        localManifestOp = package.UpdatePackageManifestAsync(localVer);
                        await localManifestOp.ToUniTask();
                    }
                    // 选中的版本激活失败(如缓存被清且无网络),回退到包内 buildin 版本。
                    if (localManifestOp.Status != EOperationStatus.Succeed && string.IsNullOrEmpty(buildinVer))
                    {
                        if (!hasSavedManifest)
                            buildinVer = await buildinVerTask;
                        else
                            buildinVer = await ReadBuildinPreSceneResVersionAsync();
                    }
                    if (localManifestOp.Status != EOperationStatus.Succeed && !string.IsNullOrEmpty(buildinVer) && buildinVer != localVer)
                    {
                        Debug.LogError($"[Launch] Activate manifest v{localVer} failed, fallback buildin v{buildinVer}: {localManifestOp.Error}");
                        localVer = buildinVer;
                        localManifestOp = package.UpdatePackageManifestAsync(localVer);
                        await localManifestOp.ToUniTask();
                    }
                    if (localManifestOp.Status != EOperationStatus.Succeed)
                    {
                        Debug.LogError($"[Launch] Activate PreSceneRes manifest v{localVer} failed: {localManifestOp.Error}");
                        return;
                    }
                }
                LogTiming("OpenLaunchStaticWin.Native.ActivateManifest", phaseStart);
                phaseStart = Time.realtimeSinceStartup;
                var handle = package.LoadAssetSync<GameObject>(LAUNCH_STATIC_WIN_LOCATION);
                if (handle.Status != EOperationStatus.Succeed)
                {
                    Debug.LogError($"[Launch] Load LaunchStaticWin failed: {handle.LastError}");
                    return;
                }
                prefab = handle.GetAssetObject<GameObject>();
                LogTiming("OpenLaunchStaticWin.Native.LoadPrefab", phaseStart);
            }
            // 后台更新(不阻塞秒开):等 cdnUrl 就绪后请求最新版本、更新清单、加载一次触发下载落地,
            // 并保存新版本号,供下次启动直接用最新资源。
            YooAssetInitializer.Instance.UpdateAndWarmupPackageAsync(
                package, LAUNCH_STATIC_WIN_LOCATION, PRE_SCENE_RES_VERSION_KEY, localVer).Forget();
            var prefab = Resources.Load<GameObject>("LaunchStaticWin");
#endif
            if (prefab == null)
            {
                Debug.LogError("[Launch] LaunchStaticWin prefab is null.");
                Debug.LogError("[Launch] LaunchStaticWin prefab not found in Resources.");
                return;
            }
            float instantiateStart = Time.realtimeSinceStartup;
            launchStaticWin = Instantiate(prefab);
            launchStaticWin.name = "LaunchStaticWin";
            launchStaticWin.AddComponent<DontDestroyOnLoad>();
            LogTiming("OpenLaunchStaticWin.Instantiate", instantiateStart);
            var rawImage = launchStaticWin.GetComponentInChildren<UnityEngine.UI.RawImage>();
            if (rawImage != null)
            {
#if UNITY_WEBGL && !UNITY_EDITOR
                var tex = await Resources.LoadAsync<Texture2D>("LoginBackground") as Texture2D;
                if (tex != null)
                    rawImage.texture = tex;
#else
                Texture2D bgTex = LoadBackgroundFromPersistent();
                if (bgTex == null)
                    bgTex = Resources.Load<Texture2D>("LoginBackground");
                if (bgTex != null)
                    rawImage.texture = bgTex;
#endif
            }
        }
        catch (Exception ex)
        {
@@ -319,185 +139,103 @@
        }
        finally
        {
            LogTiming("OpenLaunchStaticWin", startTime);
            isOpeningLaunchStaticWin = false;
        }
    }
    private async UniTask<bool> InitializePreSceneResPackageAsync(ResourcePackage package, bool hasSavedManifest)
    {
        // cdnUrl 在 Awake 阶段通常还没拿到,但 CacheFileSystem 初始化不依赖 cdnUrl。
        // 远程地址由 DynamicCdnRemoteServices 在请求版本/下载时实时读取。
        if (hasSavedManifest)
        {
            var cacheOnlyParams = new HostPlayModeParameters
            {
                CacheFileSystemParameters = CreatePreSceneResCacheFileSystemParameters()
            };
            var cacheOnlyOp = package.InitializeAsync(cacheOnlyParams);
            await cacheOnlyOp.ToUniTask();
            if (cacheOnlyOp.Status == EOperationStatus.Succeed)
            {
                Debug.LogError("[Launch] Init PreSceneRes with cache-only fast path succeeded.");
                return true;
            }
            Debug.LogError($"[Launch] Init PreSceneRes cache-only failed, fallback full host init: {cacheOnlyOp.Error}");
        }
        var fullHostParams = new HostPlayModeParameters
        {
            BuildinFileSystemParameters = CreatePreSceneResBuildinFileSystemParameters(copyBuildinManifest: true),
            CacheFileSystemParameters = CreatePreSceneResCacheFileSystemParameters()
        };
        var initOp = package.InitializeAsync(fullHostParams);
        await initOp.ToUniTask();
        if (initOp.Status != EOperationStatus.Succeed)
        {
            Debug.LogError($"[Launch] Init PreSceneRes failed: {initOp.Error}");
            return false;
        }
        return true;
    }
    private async UniTask<bool> ReinitializePreSceneResPackageWithBuildinAsync(ResourcePackage package)
    {
        var destroyOp = package.DestroyAsync();
        await destroyOp.ToUniTask();
        if (destroyOp.Status != EOperationStatus.Succeed)
        {
            Debug.LogError($"[Launch] Destroy PreSceneRes package before reinit failed: {destroyOp.Error}");
            return false;
        }
        return await InitializePreSceneResPackageAsync(package, hasSavedManifest: false);
    }
    private FileSystemParameters CreatePreSceneResBuildinFileSystemParameters(bool copyBuildinManifest)
    {
        var buildinFsParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
        buildinFsParams.AddParameter(FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST, copyBuildinManifest);
        // 启动期优先速度:只做存在性校验。
        buildinFsParams.AddParameter(FileSystemParametersDefine.FILE_VERIFY_LEVEL, EFileVerifyLevel.Low);
        // 控制并发可降低移动端线程调度开销,缩短初始化尾部抖动。
        buildinFsParams.AddParameter(FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY, PRE_SCENE_RES_VERIFY_MAX_CONCURRENCY);
        // 启动期跳过覆盖安装自动清理,避免首次启动额外磁盘清理开销。
        buildinFsParams.AddParameter(FileSystemParametersDefine.INSTALL_CLEAR_MODE, EOverwriteInstallClearMode.None);
        return buildinFsParams;
    }
    private FileSystemParameters CreatePreSceneResCacheFileSystemParameters()
    {
        var remoteServices = new DynamicCdnRemoteServices(PRE_SCENE_RES_PACKAGE_NAME);
        var cacheFsParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
        // 启动期优先速度:只做存在性校验。
        cacheFsParams.AddParameter(FileSystemParametersDefine.FILE_VERIFY_LEVEL, EFileVerifyLevel.Low);
        // 控制并发可降低移动端线程调度开销,缩短初始化尾部抖动。
        cacheFsParams.AddParameter(FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY, PRE_SCENE_RES_VERIFY_MAX_CONCURRENCY);
        // 启动期跳过覆盖安装自动清理,避免首次启动额外磁盘清理开销。
        cacheFsParams.AddParameter(FileSystemParametersDefine.INSTALL_CLEAR_MODE, EOverwriteInstallClearMode.None);
        return cacheFsParams;
    }
#if !UNITY_WEBGL || UNITY_EDITOR
    /// <summary>
    /// 读取包内(StreamingAssets)PreSceneRes 的内置版本号,纯本地不联网。
    /// 仅在 LocalSave 尚无保存版本(首次启动/未更新过)时使用。
    /// </summary>
    private async UniTask<string> ReadBuildinPreSceneResVersionAsync()
    private Texture2D LoadBackgroundFromPersistent()
    {
        float startTime = Time.realtimeSinceStartup;
        try
        string savedVer = LocalSave.GetString("bg_version");
        if (string.IsNullOrEmpty(savedVer))
            return null;
        string path = Application.persistentDataPath + "/Background/LoginBackground_" + savedVer + ".jpg";
        if (!File.Exists(path))
            return null;
        byte[] bytes = File.ReadAllBytes(path);
        var tex = new Texture2D(2, 2);
        if (!tex.LoadImage(bytes))
        {
            string folder = YooAssetSettingsData.GetDefaultYooFolderName();
            string fileName = YooAssetSettingsData.GetPackageVersionFileName(PRE_SCENE_RES_PACKAGE_NAME);
            string filePath = Path.Combine(Application.streamingAssetsPath, folder, PRE_SCENE_RES_PACKAGE_NAME, fileName);
#if !UNITY_ANDROID || UNITY_EDITOR
            // 非 Android 平台优先直接读本地文件,减少 UnityWebRequest 开销。
            if (File.Exists(filePath))
            {
                return File.ReadAllText(filePath).Trim();
            }
#endif
            // Android 上 streamingAssetsPath 已是 jar:file://... 形式,可直接用于 UnityWebRequest;
            // 其它平台为本地文件路径,需补 file:// 前缀。
            string url;
#if UNITY_ANDROID && !UNITY_EDITOR
            url = filePath;
#elif UNITY_EDITOR_WIN
            url = $"file:///{filePath}";
#else
            url = $"file://{filePath}";
#endif
            using (var req = UnityEngine.Networking.UnityWebRequest.Get(url))
            {
                await req.SendWebRequest().ToUniTask();
                if (req.result != UnityEngine.Networking.UnityWebRequest.Result.Success)
                {
                    Debug.LogError($"[Launch] Read buildin PreSceneRes version failed: {req.error}");
                    return null;
                }
                return req.downloadHandler.text.Trim();
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"[Launch] ReadBuildinPreSceneResVersionAsync exception: {ex}");
            UnityEngine.Object.Destroy(tex);
            return null;
        }
        finally
        {
            LogTiming("ReadBuildinPreSceneResVersionAsync", startTime);
        }
        return tex;
    }
    /// <summary>
    /// 检查指定版本的 PreSceneRes manifest/hash 是否已落地到本地缓存。
    /// 仅当本地真实存在时才允许启动阶段激活该版本,避免在 cdnUrl 未就绪时触发远程请求。
    /// </summary>
    private bool IsPreSceneResManifestCached(string packageVersion)
    private async UniTaskVoid UpdateBackgroundAsync()
    {
        if (string.IsNullOrEmpty(packageVersion))
            return false;
        try
        {
            string cacheRoot = GetYooDefaultCacheRoot();
            string manifestRoot = Path.Combine(cacheRoot, PRE_SCENE_RES_PACKAGE_NAME, "ManifestFiles");
            string hashPath = Path.Combine(manifestRoot, YooAssetSettingsData.GetPackageHashFileName(PRE_SCENE_RES_PACKAGE_NAME, packageVersion));
            string manifestPath = Path.Combine(manifestRoot, YooAssetSettingsData.GetManifestBinaryFileName(PRE_SCENE_RES_PACKAGE_NAME, packageVersion));
            return File.Exists(hashPath) && File.Exists(manifestPath);
            float elapsed = 0f;
            while (string.IsNullOrEmpty(VersionConfigEx.config?.cdnUrl))
            {
                await UniTask.Delay(500);
                elapsed += 0.5f;
                if (elapsed > 30f)
                {
                    Debug.LogError("[Launch] UpdateBackground give up: cdnUrl not ready within 30s.");
                    return;
                }
            }
            string cdnUrl = VersionConfigEx.config.cdnUrl.TrimEnd('/');
            string cacheBust = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
            string newVersion = null;
            using (var versionReq = UnityEngine.Networking.UnityWebRequest.Get(cdnUrl + "/version.txt?ts=" + cacheBust))
            {
                await versionReq.SendWebRequest().ToUniTask();
                if (versionReq.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
                {
                    newVersion = versionReq.downloadHandler.text.Trim();
                }
            }
            if (string.IsNullOrEmpty(newVersion))
            {
                using var legacyVersionReq = UnityEngine.Networking.UnityWebRequest.Get(cdnUrl + "/bg_version.txt?ts=" + cacheBust);
                await legacyVersionReq.SendWebRequest().ToUniTask();
                if (legacyVersionReq.result != UnityEngine.Networking.UnityWebRequest.Result.Success)
                {
                    Debug.LogError($"[Launch] Fetch version file failed: {legacyVersionReq.error}");
                    return;
                }
                newVersion = legacyVersionReq.downloadHandler.text.Trim();
            }
            if (string.IsNullOrEmpty(newVersion) || newVersion == LocalSave.GetString("bg_version"))
                return;
            using var imgReq = UnityEngine.Networking.UnityWebRequest.Get(cdnUrl + "/LoginBackground.jpg?ts=" + cacheBust);
            await imgReq.SendWebRequest().ToUniTask();
            if (imgReq.result != UnityEngine.Networking.UnityWebRequest.Result.Success)
            {
                Debug.LogError($"[Launch] Download background image failed: {imgReq.error}");
                return;
            }
            string dir = Application.persistentDataPath + "/Background/";
            Directory.CreateDirectory(dir);
            string oldVer = LocalSave.GetString("bg_version");
            if (!string.IsNullOrEmpty(oldVer))
            {
                string oldPath = dir + "LoginBackground_" + oldVer + ".jpg";
                if (File.Exists(oldPath))
                    File.Delete(oldPath);
            }
            File.WriteAllBytes(dir + "LoginBackground_" + newVersion + ".jpg", imgReq.downloadHandler.data);
            LocalSave.SetString("bg_version", newVersion);
            Debug.LogError($"[Launch] Background updated to version {newVersion}");
        }
        catch (Exception ex)
        {
            Debug.LogError($"[Launch] Check PreSceneRes cache manifest failed: {ex.Message}");
            return false;
            Debug.LogError($"[Launch] UpdateBackgroundAsync exception: {ex}");
        }
    }
    /// <summary>
    /// 对齐 YooAsset 默认缓存根目录规则,供启动阶段做本地 manifest 存在性判断。
    /// </summary>
    private string GetYooDefaultCacheRoot()
    {
        string yooFolder = YooAssetSettingsData.GetDefaultYooFolderName();
#if UNITY_EDITOR
        string projectPath = Path.GetDirectoryName(Application.dataPath);
        return string.IsNullOrEmpty(yooFolder) ? projectPath : Path.Combine(projectPath, yooFolder);
#elif UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX
        return string.IsNullOrEmpty(yooFolder) ? Application.dataPath : Path.Combine(Application.dataPath, yooFolder);
#elif UNITY_STANDALONE_OSX
        return string.IsNullOrEmpty(yooFolder) ? Application.persistentDataPath : Path.Combine(Application.persistentDataPath, yooFolder);
#else
        return string.IsNullOrEmpty(yooFolder) ? Application.persistentDataPath : Path.Combine(Application.persistentDataPath, yooFolder);
#endif
    }
#endif
@@ -677,6 +415,14 @@
        {
            launchExWin = await LaunchExWin.OpenWindow();
            launchExWin.AddComponent<DontDestroyOnLoad>();
#if !UNITY_WEBGL || UNITY_EDITOR
            if (!isBackgroundUpdateStarted)
            {
                isBackgroundUpdateStarted = true;
                UpdateBackgroundAsync().Forget();
            }
#endif
        }
        catch (Exception ex)
        {
Assets/Launch/Manager/YooAssetInitializer.cs
@@ -42,9 +42,6 @@
    public const string DLL_PACKAGE_NAME = "Dll";
    /// <summary>启动前置场景资源包 — 含 LaunchStaticWin 等 Awake 阶段展示资源</summary>
    public const string PRE_SCENE_RES_PACKAGE_NAME = "PreSceneRes";
    public const string DEFAULT_PACKAGE_NAME = BUILTIN_PACKAGE_NAME;
    // Launch 阶段读取的启动配置文件被 CollectResBeforeUpdate 额外收进 Builtin 包。
@@ -58,11 +55,6 @@
    {
        BUILTIN_PACKAGE_NAME,
        DLL_PACKAGE_NAME,
#if UNITY_WEBGL && !UNITY_EDITOR
        // WebGL 下 PreSceneRes 走标准联网初始化流程;
        // 非 WebGL 由 Launch.OpenLaunchStaticWin 独立本地秒开加载,不在此初始化。
        PRE_SCENE_RES_PACKAGE_NAME,
#endif
    };
    private EPlayMode _playMode;
@@ -259,105 +251,15 @@
        try
        {
            foreach (var kvp in _packages)
            var packageEntries = new List<KeyValuePair<string, ResourcePackage>>(_packages);
            var tasks = new List<UniTask>(packageEntries.Count);
            foreach (var kvp in packageEntries)
            {
                var pkgName = kvp.Key;
                var package = kvp.Value;
#if UNITY_WEBGL || TEST_BUILD
                Debug.Log($"[YooAssetInitializer][Diag] Request version begin pkg='{pkgName}', playMode={_playMode}, initStatus={package.InitializeStatus}, packageValid={package.PackageValid}");
#endif
                // 1. 请求版本号(对关键包最多重试3次,间隔1s)
                int versionRetryMax = string.Equals(pkgName, LAUNCH_CONFIG_PACKAGE_NAME, StringComparison.OrdinalIgnoreCase) ? 3 : 0;
                EOperationStatus versionStatus = EOperationStatus.Failed;
                string versionOpError = string.Empty;
                string packageVersion = string.Empty;
                for (int versionAttempt = 0; versionAttempt <= versionRetryMax; versionAttempt++)
                {
                    if (versionAttempt > 0)
                    {
                        Debug.LogWarning($"[YooAssetInitializer] RequestPackageVersion retry {versionAttempt}/{versionRetryMax} for '{pkgName}'");
                        await UniTask.Delay(1000);
                    }
                    var versionOp = package.RequestPackageVersionAsync();
                    await versionOp.ToUniTask();
                    versionStatus = versionOp.Status;
                    versionOpError = versionOp.Error;
                    if (versionStatus == EOperationStatus.Succeed)
                    {
                        packageVersion = versionOp.PackageVersion;
                        break;
                    }
                }
                if (versionStatus != EOperationStatus.Succeed)
                {
                    Debug.LogWarning($"[YooAssetInitializer] RequestPackageVersion failed for '{pkgName}': {versionOpError}");
#if UNITY_WEBGL || TEST_BUILD
                    LogPackageState(pkgName, package, "request-version-failed");
#endif
                    // HostPlayMode 远程版本检查失败时,尝试加载内置 manifest(回退到随包资源)
                    if (_playMode == EPlayMode.HostPlayMode)
                    {
                        Debug.Log($"[YooAssetInitializer] HostPlayMode fallback: loading buildin manifest for '{pkgName}'");
                        var fallbackVersionOp = package.RequestPackageVersionAsync(true);
                        await fallbackVersionOp.ToUniTask();
                        if (fallbackVersionOp.Status == EOperationStatus.Succeed)
                        {
                            var fallbackManifestOp = package.UpdatePackageManifestAsync(fallbackVersionOp.PackageVersion);
                            await fallbackManifestOp.ToUniTask();
                            if (fallbackManifestOp.Status == EOperationStatus.Succeed)
                            {
                                // Manifest 更新成功后清理旧版本缓存(非 Editor 模式下才有磁盘缓存)
#if !UNITY_EDITOR
                                var fallbackClearBundleOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
                                await fallbackClearBundleOp.ToUniTask();
                                var fallbackClearManifestOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedManifestFiles);
                                await fallbackClearManifestOp.ToUniTask();
#endif
                                Debug.Log($"[YooAssetInitializer] Package '{pkgName}' using buildin manifest v{fallbackVersionOp.PackageVersion}");
#if UNITY_WEBGL || TEST_BUILD
                                LogPackageState(pkgName, package, "fallback-manifest-success");
#endif
                                continue;
                            }
                        }
                        Debug.LogWarning($"[YooAssetInitializer] Buildin manifest fallback also failed for '{pkgName}'");
                    }
                    continue;
                }
                Debug.Log($"[YooAssetInitializer] Package '{pkgName}' version: {packageVersion}");
                // 2. 更新 Manifest
                var manifestOp = package.UpdatePackageManifestAsync(packageVersion);
                await manifestOp.ToUniTask();
                if (manifestOp.Status != EOperationStatus.Succeed)
                {
                    Debug.LogWarning($"[YooAssetInitializer] UpdatePackageManifest failed for '{pkgName}': {manifestOp.Error}");
#if UNITY_WEBGL || TEST_BUILD
                    LogPackageState(pkgName, package, "update-manifest-failed");
#endif
                    continue;
                }
                // Manifest 更新成功后清理旧版本缓存(非 Editor 模式下才有磁盘缓存)
#if !UNITY_EDITOR
                var clearBundleOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
                await clearBundleOp.ToUniTask();
                var clearManifestOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedManifestFiles);
                await clearManifestOp.ToUniTask();
#endif
                Debug.Log($"[YooAssetInitializer] Package '{pkgName}' manifest updated to {packageVersion}.");
#if UNITY_WEBGL || TEST_BUILD
                LogPackageState(pkgName, package, "update-manifest-success");
#endif
                tasks.Add(RequestVersionAndUpdateSinglePackageAsync(kvp.Key, kvp.Value));
            }
            await UniTask.WhenAll(tasks);
            State = InitState.VersionChecked;
            State = InitState.Ready;
@@ -377,97 +279,107 @@
        }
    }
    /// <summary>
    /// 对指定包请求最新版本并更新清单,更新成功后立即加载一次目标资源(不实例化),
    /// 让 HostPlayMode 自动把对应 bundle 从 CDN 下载落地,并把新版本号保存到本地,
    /// 供下次启动直接用本地新资源秒开。
    /// 适用于 Launch 独立创建的本地秒开包(如 PreSceneRes),不阻塞当前启动流程。
    /// </summary>
    /// <param name="package">已初始化的包裹(由调用方创建并初始化)</param>
    /// <param name="location">用于触发下载的资源定位地址</param>
    /// <param name="versionKey">保存新版本号的 LocalSave key</param>
    /// <param name="currentVer">当前已激活的本地版本号(与最新一致则跳过)</param>
    public async UniTask UpdateAndWarmupPackageAsync(ResourcePackage package, string location, string versionKey, string currentVer)
    private async UniTask RequestVersionAndUpdateSinglePackageAsync(string pkgName, ResourcePackage package)
    {
        if (package == null)
        {
            Debug.LogWarning("[YooAssetInitializer] UpdateAndWarmup skipped: package is null.");
            return;
        }
        try
        {
            Debug.Log($"[YooAssetInitializer] UpdateAndWarmup enter '{package.PackageName}', current={currentVer}, waiting cdnUrl...");
            // 等待 cdnUrl 就绪(版本服务器返回后由 LocalResManager 写入 VersionConfigEx.config.cdnUrl)。
            // 最多等待 30 秒,超时则放弃本次更新(本次仍用本地版本,不影响秒开)。
            float waitStart = Time.realtimeSinceStartup;
            while (string.IsNullOrEmpty(VersionConfigEx.config?.cdnUrl))
            {
                if (Time.realtimeSinceStartup - waitStart > 30f)
                {
                    Debug.LogWarning("[YooAssetInitializer] UpdateAndWarmup give up: cdnUrl not ready within 30s.");
                    return;
                }
                await UniTask.Yield();
            }
            Debug.Log($"[YooAssetInitializer] UpdateAndWarmup '{package.PackageName}' cdnUrl ready, requesting latest version...");
            var versionOp = package.RequestPackageVersionAsync();
            await versionOp.ToUniTask();
            if (versionOp.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] UpdateAndWarmup request version failed: {versionOp.Error}");
                return;
            }
            string latest = versionOp.PackageVersion;
            Debug.Log($"[YooAssetInitializer] UpdateAndWarmup '{package.PackageName}': current={currentVer}, latest={latest}");
            if (string.IsNullOrEmpty(latest) || latest == currentVer)
                return; // 已是最新
            var manifestOp = package.UpdatePackageManifestAsync(latest);
            await manifestOp.ToUniTask();
            if (manifestOp.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] UpdateAndWarmup update manifest failed: {manifestOp.Error}");
                return;
            }
            // 更新清单后直接加载一次目标资源(不实例化),
            // HostPlayMode 下 LoadAssetAsync 会自动把对应 bundle 从 CDN 下载落地。
            var loadHandle = package.LoadAssetAsync<UnityEngine.Object>(location);
            await loadHandle.ToUniTask();
            if (loadHandle.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] UpdateAndWarmup load failed: {loadHandle.LastError}");
                return;
            }
            loadHandle.Release();
#if !UNITY_EDITOR
            // 后台清理旧版本缓存,降低下次启动时 CacheFileSystem 的扫描与校验成本。
            var clearBundleOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
            await clearBundleOp.ToUniTask();
            if (clearBundleOp.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] UpdateAndWarmup clear unused bundles failed: {clearBundleOp.Error}");
            }
            var clearManifestOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedManifestFiles);
            await clearManifestOp.ToUniTask();
            if (clearManifestOp.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] UpdateAndWarmup clear unused manifests failed: {clearManifestOp.Error}");
            }
#if UNITY_WEBGL || TEST_BUILD
            Debug.Log($"[YooAssetInitializer][Diag] Request version begin pkg='{pkgName}', playMode={_playMode}, initStatus={package.InitializeStatus}, packageValid={package.PackageValid}");
#endif
            LocalSave.SetString(versionKey, latest);
            Debug.Log($"[YooAssetInitializer] Package '{package.PackageName}' updated to {latest}, will be used on next launch.");
            // 1. 请求版本号(对关键包最多重试3次,间隔1s)
            int versionRetryMax = string.Equals(pkgName, LAUNCH_CONFIG_PACKAGE_NAME, StringComparison.OrdinalIgnoreCase) ? 3 : 0;
            EOperationStatus versionStatus = EOperationStatus.Failed;
            string versionOpError = string.Empty;
            string packageVersion = string.Empty;
            for (int versionAttempt = 0; versionAttempt <= versionRetryMax; versionAttempt++)
            {
                if (versionAttempt > 0)
                {
                    Debug.LogWarning($"[YooAssetInitializer] RequestPackageVersion retry {versionAttempt}/{versionRetryMax} for '{pkgName}'");
                    await UniTask.Delay(1000);
                }
                var versionOp = package.RequestPackageVersionAsync();
                await versionOp.ToUniTask();
                versionStatus = versionOp.Status;
                versionOpError = versionOp.Error;
                if (versionStatus == EOperationStatus.Succeed)
                {
                    packageVersion = versionOp.PackageVersion;
                    break;
                }
            }
            if (versionStatus != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] RequestPackageVersion failed for '{pkgName}': {versionOpError}");
#if UNITY_WEBGL || TEST_BUILD
                LogPackageState(pkgName, package, "request-version-failed");
#endif
                // HostPlayMode 远程版本检查失败时,尝试加载内置 manifest(回退到随包资源)
                if (_playMode == EPlayMode.HostPlayMode)
                {
                    Debug.Log($"[YooAssetInitializer] HostPlayMode fallback: loading buildin manifest for '{pkgName}'");
                    var fallbackVersionOp = package.RequestPackageVersionAsync(true);
                    await fallbackVersionOp.ToUniTask();
                    if (fallbackVersionOp.Status == EOperationStatus.Succeed)
                    {
                        var fallbackManifestOp = package.UpdatePackageManifestAsync(fallbackVersionOp.PackageVersion);
                        await fallbackManifestOp.ToUniTask();
                        if (fallbackManifestOp.Status == EOperationStatus.Succeed)
                        {
                            // Manifest 更新成功后清理旧版本缓存(非 Editor 模式下才有磁盘缓存)
#if !UNITY_EDITOR
                            var fallbackClearBundleOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
                            await fallbackClearBundleOp.ToUniTask();
                            var fallbackClearManifestOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedManifestFiles);
                            await fallbackClearManifestOp.ToUniTask();
#endif
                            Debug.Log($"[YooAssetInitializer] Package '{pkgName}' using buildin manifest v{fallbackVersionOp.PackageVersion}");
#if UNITY_WEBGL || TEST_BUILD
                            LogPackageState(pkgName, package, "fallback-manifest-success");
#endif
                            return;
                        }
                    }
                    Debug.LogWarning($"[YooAssetInitializer] Buildin manifest fallback also failed for '{pkgName}'");
                }
                return;
            }
            Debug.Log($"[YooAssetInitializer] Package '{pkgName}' version: {packageVersion}");
            // 2. 更新 Manifest
            var manifestOp = package.UpdatePackageManifestAsync(packageVersion);
            await manifestOp.ToUniTask();
            if (manifestOp.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"[YooAssetInitializer] UpdatePackageManifest failed for '{pkgName}': {manifestOp.Error}");
#if UNITY_WEBGL || TEST_BUILD
                LogPackageState(pkgName, package, "update-manifest-failed");
#endif
                return;
            }
            // Manifest 更新成功后清理旧版本缓存(非 Editor 模式下才有磁盘缓存)
#if !UNITY_EDITOR
            var clearBundleOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
            await clearBundleOp.ToUniTask();
            var clearManifestOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedManifestFiles);
            await clearManifestOp.ToUniTask();
#endif
            Debug.Log($"[YooAssetInitializer] Package '{pkgName}' manifest updated to {packageVersion}.");
#if UNITY_WEBGL || TEST_BUILD
            LogPackageState(pkgName, package, "update-manifest-success");
#endif
        }
        catch (Exception ex)
        {
            Debug.LogError($"[YooAssetInitializer] UpdateAndWarmupPackageAsync exception: {ex}");
            Debug.LogError($"[YooAssetInitializer] RequestVersionAndUpdateSinglePackageAsync exception for '{pkgName}': {ex}");
        }
    }
Assets/Resources/LaunchStaticWin.prefab
New file
@@ -0,0 +1,180 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1123346103264518
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 224225136505173382}
  - component: {fileID: 222748354593637026}
  - component: {fileID: 3580133804252199982}
  m_Layer: 5
  m_Name: BackGround
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &224225136505173382
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1123346103264518}
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
  m_LocalPosition: {x: 0, y: 0, z: 0}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_ConstrainProportionsScale: 1
  m_Children: []
  m_Father: {fileID: 224707953458779256}
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0.5, y: 0.5}
  m_AnchorMax: {x: 0.5, y: 0.5}
  m_AnchoredPosition: {x: 0, y: 0}
  m_SizeDelta: {x: 750, y: 1750}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &222748354593637026
CanvasRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1123346103264518}
  m_CullTransparentMesh: 0
--- !u!114 &3580133804252199982
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1123346103264518}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_Material: {fileID: 0}
  m_Color: {r: 1, g: 1, b: 1, a: 1}
  m_RaycastTarget: 1
  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
  m_Maskable: 1
  m_OnCullStateChanged:
    m_PersistentCalls:
      m_Calls: []
  m_Texture: {fileID: 2800000, guid: 94b2ae648114cf8479cc3555f3739015, type: 3}
  m_UVRect:
    serializedVersion: 2
    x: 0
    y: 0
    width: 1
    height: 1
--- !u!1 &1445774415548612
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 224707953458779256}
  - component: {fileID: 222263388365653932}
  - component: {fileID: 225812526638688240}
  - component: {fileID: 963064466861716392}
  - component: {fileID: -5385168211531889644}
  m_Layer: 5
  m_Name: LaunchStaticWin
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &224707953458779256
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1445774415548612}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: 0, y: 0, z: 0}
  m_LocalScale: {x: 0, y: 0, z: 0}
  m_ConstrainProportionsScale: 0
  m_Children:
  - {fileID: 224225136505173382}
  m_Father: {fileID: 0}
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0, y: 0}
  m_AnchorMax: {x: 1, y: 1}
  m_AnchoredPosition: {x: 0, y: 0}
  m_SizeDelta: {x: 0, y: 0}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &222263388365653932
CanvasRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1445774415548612}
  m_CullTransparentMesh: 0
--- !u!225 &225812526638688240
CanvasGroup:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1445774415548612}
  m_Enabled: 1
  m_Alpha: 1
  m_Interactable: 1
  m_BlocksRaycasts: 1
  m_IgnoreParentGroups: 0
--- !u!223 &963064466861716392
Canvas:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1445774415548612}
  m_Enabled: 1
  serializedVersion: 3
  m_RenderMode: 0
  m_Camera: {fileID: 0}
  m_PlaneDistance: 100
  m_PixelPerfect: 0
  m_ReceivesEvents: 1
  m_OverrideSorting: 0
  m_OverridePixelPerfect: 0
  m_SortingBucketNormalizedSize: 0
  m_VertexColorAlwaysGammaSpace: 0
  m_AdditionalShaderChannelsFlag: 0
  m_UpdateRectTransformForStandalone: 0
  m_SortingLayerID: 0
  m_SortingOrder: 0
  m_TargetDisplay: 0
--- !u!114 &-5385168211531889644
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1445774415548612}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_UiScaleMode: 1
  m_ReferencePixelsPerUnit: 100
  m_ScaleFactor: 1
  m_ReferenceResolution: {x: 750, y: 1334}
  m_ScreenMatchMode: 0
  m_MatchWidthOrHeight: 0
  m_PhysicalUnit: 3
  m_FallbackScreenDPI: 96
  m_DefaultSpriteDPI: 96
  m_DynamicPixelsPerUnit: 1
  m_PresetInfoIsWorld: 0
Assets/Resources/LaunchStaticWin.prefab.meta
New file
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7d6261451fd813d4d81ccf6c8b2b570c
PrefabImporter:
  externalObjects: {}
  userData:
  assetBundleName: builtin/prefabs
  assetBundleVariant:
Assets/Resources/LoginBackground.jpg
Assets/Resources/LoginBackground.jpg.meta
New file
@@ -0,0 +1,140 @@
fileFormatVersion: 2
guid: 94b2ae648114cf8479cc3555f3739015
TextureImporter:
  internalIDToNameTable: []
  externalObjects: {}
  serializedVersion: 13
  mipmaps:
    mipMapMode: 0
    enableMipMap: 0
    sRGBTexture: 1
    linearTexture: 0
    fadeOut: 0
    borderMipMap: 0
    mipMapsPreserveCoverage: 0
    alphaTestReferenceValue: 0.5
    mipMapFadeDistanceStart: 1
    mipMapFadeDistanceEnd: 3
  bumpmap:
    convertToNormalMap: 0
    externalNormalMap: 0
    heightScale: 0.25
    normalMapFilter: 0
    flipGreenChannel: 0
  isReadable: 0
  streamingMipmaps: 0
  streamingMipmapsPriority: 0
  vTOnly: 0
  ignoreMipmapLimit: 0
  grayScaleToAlpha: 0
  generateCubemap: 6
  cubemapConvolution: 0
  seamlessCubemap: 0
  textureFormat: 1
  maxTextureSize: 2048
  textureSettings:
    serializedVersion: 2
    filterMode: 1
    aniso: 1
    mipBias: 0
    wrapU: 1
    wrapV: 1
    wrapW: 1
  nPOTScale: 0
  lightmap: 0
  compressionQuality: 50
  spriteMode: 1
  spriteExtrude: 1
  spriteMeshType: 1
  alignment: 0
  spritePivot: {x: 0.5, y: 0.5}
  spritePixelsToUnits: 100
  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
  spriteGenerateFallbackPhysicsShape: 1
  alphaUsage: 1
  alphaIsTransparency: 1
  spriteTessellationDetail: -1
  textureType: 8
  textureShape: 1
  singleChannelComponent: 0
  flipbookRows: 1
  flipbookColumns: 1
  maxTextureSizeSet: 0
  compressionQualitySet: 0
  textureFormatSet: 0
  ignorePngGamma: 0
  applyGammaDecoding: 0
  swizzle: 50462976
  cookieLightType: 0
  platformSettings:
  - serializedVersion: 3
    buildTarget: DefaultTexturePlatform
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: Standalone
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: WebGL
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  - serializedVersion: 3
    buildTarget: Android
    maxTextureSize: 2048
    resizeAlgorithm: 0
    textureFormat: -1
    textureCompression: 1
    compressionQuality: 50
    crunchedCompression: 0
    allowsAlphaSplitting: 0
    overridden: 0
    ignorePlatformSupport: 0
    androidETC2FallbackOverride: 0
    forceMaximumCompressionQuality_BC6H_BC7: 0
  spriteSheet:
    serializedVersion: 2
    sprites: []
    outline: []
    physicsShape: []
    bones: []
    spriteID: 5e97eb03825dee720800000000000000
    internalID: 0
    vertices: []
    indices:
    edges: []
    weights: []
    secondaryTextures: []
    nameFileIdTable: {}
  mipmapLimitGroupName:
  pSDRemoveMatte: 0
  userData:
  assetBundleName:
  assetBundleVariant: