109 lines
4.4 KiB
C#
109 lines
4.4 KiB
C#
using System.Collections.Generic;
|
||
using UnityEditor;
|
||
using UnityEditor.AddressableAssets;
|
||
using UnityEditor.AddressableAssets.Settings;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEngine;
|
||
using UnityEngine.SceneManagement;
|
||
|
||
/// <summary>
|
||
/// 创建纯 AOT 引导场景 BootScene 并配置 Build Settings / Addressables。
|
||
/// 引导场景保持全空(不挂任何组件),规避 HybridCLR "热更脚本不能挂在随主包场景上" 的限制。
|
||
/// 启动流程:BootScene(空)→ Launcher 加载热更 dll → 通过 Addressables 加载 UIScene。
|
||
/// </summary>
|
||
public static class BootSceneCreator
|
||
{
|
||
private const string BootScenePath = "Assets/MyGame/Scenes/BootScene.unity";
|
||
private const string UIScenePath = "Assets/MyGame/Scenes/UIScene.unity";
|
||
|
||
[MenuItem("Tools/Create BootScene")]
|
||
public static void CreateBootScene()
|
||
{
|
||
// 1. 新建空场景
|
||
// 引导场景必须保持“全空”:不挂任何热更 MonoBehaviour(否则构建时会被判为 missing script),
|
||
// 也不需要挂 AOT 单例——Launcher 是 static 类,靠 RuntimeInitializeOnLoadMethod 自动执行;
|
||
// BeijingTimeManager 是 MonoSingleton(Awake 里 DontDestroyOnLoad),它已经挂在 UIScene 上,
|
||
// 在这里再挂一份反而会产生两个实例(两份定时同步循环)。
|
||
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||
|
||
// 2. 保存场景
|
||
bool saved = EditorSceneManager.SaveScene(scene, BootScenePath);
|
||
if (!saved)
|
||
{
|
||
Debug.LogError("[BootSceneCreator] 引导场景保存失败");
|
||
return;
|
||
}
|
||
|
||
Debug.Log($"[BootSceneCreator] 引导场景已创建: {BootScenePath}");
|
||
|
||
// 3. 更新 Build Settings:
|
||
// - BootScene 设为第一个(启动场景)
|
||
// - UIScene 从 Build Settings 移除(改为通过 Addressables 加载)
|
||
// - 其他场景保留
|
||
UpdateBuildSettings();
|
||
|
||
// 4. 把 UIScene 标记为 Addressable(key = "UIScene"),Launcher 会按这个 key 加载
|
||
MarkUISceneAddressable();
|
||
|
||
// 5. 打开引导场景
|
||
EditorSceneManager.OpenScene(BootScenePath);
|
||
|
||
Debug.Log("[BootSceneCreator] 完成!请运行 HybridCLR > Generate > All 并重新构建。");
|
||
}
|
||
|
||
private static void UpdateBuildSettings()
|
||
{
|
||
var scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
|
||
|
||
// 移除已存在的 BootScene(防重复)
|
||
scenes.RemoveAll(s => s.path == BootScenePath);
|
||
|
||
// BootScene 插到最前面
|
||
scenes.Insert(0, new EditorBuildSettingsScene(BootScenePath, true));
|
||
|
||
// 移除 UIScene(现在由 Addressables 加载,不再随主包)
|
||
scenes.RemoveAll(s => s.path == UIScenePath);
|
||
|
||
EditorBuildSettings.scenes = scenes.ToArray();
|
||
|
||
Debug.Log("[BootSceneCreator] Build Settings 已更新:BootScene 为启动场景,UIScene 已移除(改走 Addressables)。");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 把 UIScene 标记为 Addressable,address key 设为 "UIScene"。
|
||
/// Launcher 会按这个 key 通过 Addressables 加载主场景。
|
||
/// </summary>
|
||
private static void MarkUISceneAddressable()
|
||
{
|
||
var settings = AddressableAssetSettingsDefaultObject.Settings;
|
||
if (settings == null)
|
||
{
|
||
Debug.LogError("[BootSceneCreator] AddressableAssetSettings 不存在,请先初始化 Addressables。");
|
||
return;
|
||
}
|
||
|
||
string uiSceneGuid = AssetDatabase.AssetPathToGUID(UIScenePath);
|
||
if (string.IsNullOrEmpty(uiSceneGuid))
|
||
{
|
||
Debug.LogError($"[BootSceneCreator] 找不到 UIScene: {UIScenePath}");
|
||
return;
|
||
}
|
||
|
||
// 用默认组(Default Local Group)。如果项目有专门的场景组,可手动改。
|
||
AddressableAssetGroup group = settings.DefaultGroup;
|
||
if (group == null)
|
||
{
|
||
Debug.LogError("[BootSceneCreator] Addressables 默认组不存在。");
|
||
return;
|
||
}
|
||
|
||
AddressableAssetEntry entry = settings.CreateOrMoveEntry(uiSceneGuid, group);
|
||
entry.address = "UIScene";
|
||
|
||
EditorUtility.SetDirty(settings);
|
||
AssetDatabase.SaveAssets();
|
||
|
||
Debug.Log($"[BootSceneCreator] UIScene 已标记为 Addressable,key='UIScene',组='{group.Name}'。");
|
||
}
|
||
}
|