Files
5.24BuYu/Assets/MyGame/Editor/BootSceneCreator.cs
T
2026-10-20 18:30:03 +08:00

140 lines
5.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.AddressableAssets;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 场景加载方式配置工具(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 空场景(唯一随主包的场景),只负责启动
/// </summary>
public static class BootSceneCreator
{
private const string BootScenePath = "Assets/MyGame/Scenes/BootScene.unity";
private const string UIScenePath = "Assets/MyGame/Scenes/UIScene.unity";
/// <summary>游戏场景(内部挂热更脚本,必须走 Addressables 加载)</summary>
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 并重新构建。");
}
/// <summary>
/// 把游戏场景(XJMH/FLZY/HHWK/YYSC)标记为 Addressable(address = 场景名),
/// 并把它们从 Build Settings 移除(改由 Addressables 加载)。
/// </summary>
[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();
}
/// <summary>
/// 统一整理 Build Settings:
/// - BootScene 放第一位(启动场景)
/// - UIScene 与 4 个游戏场景全部移除(它们都改走 Addressables 加载)
/// </summary>
private static void ApplyBuildSettings()
{
var scenes = new List<EditorBuildSettingsScene>(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)。");
}
/// <summary>
/// 把一个场景标记为 Addressable,address key 设为指定值(Launcher / 游戏内加载按这个 key 找)。
/// </summary>
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}')。");
}
}