| Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Editor/YooAsset/YooAssetBuildTool.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Launch/Launch.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Launch/Manager/YooAssetInitializer.cs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Resources/LaunchStaticWin.prefab | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Resources/LaunchStaticWin.prefab.meta | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Resources/LoginBackground.jpg | 补丁 | 查看 | 原始文档 | blame | 历史 | |
| Assets/Resources/LoginBackground.jpg.meta | ●●●●● 补丁 | 查看 | 原始文档 | 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: