using System.Collections.Generic;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
///
/// 场景加载方式配置工具(HybridCLR 配套):
///
/// 规则:**凡是挂了热更脚本(Game.Hotfix)的场景,都不能随主包(Build Settings)加载**,
/// 必须通过 AssetBundle/Addressables 加载,否则运行时会变成 missing script
/// (典型现象:场景里的 MonoBehaviour 不工作,比如背景不动、UI 不响应)。
/// - UIScene:挂了 GameManager/TiShi/LoadingPanel/HuoDe 等 → 走 Addressables
/// - XJMH/FLZY/HHWK/YYSC:挂了 BGManager/BgFit 等 → 走 Addressables
/// - BootScene:纯 AOT 空场景(唯一随主包的场景),只负责启动
///
public static class BootSceneCreator
{
private const string BootScenePath = "Assets/MyGame/Scenes/BootScene.unity";
private const string UIScenePath = "Assets/MyGame/Scenes/UIScene.unity";
/// 游戏场景(内部挂热更脚本,必须走 Addressables 加载)
private static readonly string[] GameSceneNames = { "XJMH", "FLZY", "HHWK", "YYSC" };
private static string GameScenePath(string name) => $"Assets/MyGame/Scenes/{name}.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. 把需要走 Addressables 的场景都标记好、并统一整理 Build Settings
SetupGameScenesToAddressablesInternal();
MarkSceneAddressable(UIScenePath, "UIScene");
// 4. 打开引导场景
EditorSceneManager.OpenScene(BootScenePath);
Debug.Log("[BootSceneCreator] 完成!请运行 HybridCLR > Generate > All 并重新构建。");
}
///
/// 把游戏场景(XJMH/FLZY/HHWK/YYSC)标记为 Addressable(address = 场景名),
/// 并把它们从 Build Settings 移除(改由 Addressables 加载)。
///
[MenuItem("Tools/Setup Game Scenes To Addressables")]
public static void SetupGameScenesToAddressables()
{
SetupGameScenesToAddressablesInternal();
Debug.Log("[BootSceneCreator] 游戏场景已标记为 Addressable(address = 场景名)并从 Build Settings 移除。请重新构建。");
}
private static void SetupGameScenesToAddressablesInternal()
{
foreach (var name in GameSceneNames)
MarkSceneAddressable(GameScenePath(name), name);
ApplyBuildSettings();
AssetDatabase.SaveAssets();
}
///
/// 统一整理 Build Settings:
/// - BootScene 放第一位(启动场景)
/// - UIScene 与 4 个游戏场景全部移除(它们都改走 Addressables 加载)
///
private static void ApplyBuildSettings()
{
var scenes = new List(EditorBuildSettings.scenes);
// 先移除所有需要走 Addressables 的场景,避免重复
scenes.RemoveAll(s => s.path == BootScenePath);
scenes.RemoveAll(s => s.path == UIScenePath);
foreach (var name in GameSceneNames)
{
string path = GameScenePath(name);
scenes.RemoveAll(s => s.path == path);
}
// BootScene 插到最前面
scenes.Insert(0, new EditorBuildSettingsScene(BootScenePath, true));
EditorBuildSettings.scenes = scenes.ToArray();
Debug.Log("[BootSceneCreator] Build Settings 已更新:BootScene 为启动场景,UIScene/XJMH/FLZY/HHWK/YYSC 已移除(改走 Addressables)。");
}
///
/// 把一个场景标记为 Addressable,address key 设为指定值(Launcher / 游戏内加载按这个 key 找)。
///
private static void MarkSceneAddressable(string scenePath, string key)
{
var settings = AddressableAssetSettingsDefaultObject.Settings;
if (settings == null)
{
Debug.LogError("[BootSceneCreator] AddressableAssetSettings 不存在,请先初始化 Addressables。");
return;
}
string guid = AssetDatabase.AssetPathToGUID(scenePath);
if (string.IsNullOrEmpty(guid))
{
Debug.LogError($"[BootSceneCreator] 找不到场景: {scenePath}");
return;
}
// 用默认组(Default Local Group)。如果项目有专门的场景组,可手动改。
AddressableAssetGroup group = settings.DefaultGroup;
if (group == null)
{
Debug.LogError("[BootSceneCreator] Addressables 默认组不存在。");
return;
}
AddressableAssetEntry entry = settings.CreateOrMoveEntry(guid, group);
entry.address = key;
EditorUtility.SetDirty(settings);
Debug.Log($"[BootSceneCreator] 场景已标记为 Addressable: {scenePath}(address='{key}',组='{group.Name}')。");
}
}