摄像机区域的优化

This commit is contained in:
2026-05-17 07:56:12 +08:00
parent f264329751
commit d25f237e76
62 changed files with 25774 additions and 5450 deletions

View File

@@ -8,8 +8,17 @@ namespace BaseGames.Core.Assets
public static class AddressKeys
{
// ── Scenes ──────────────────────────────────────────────────────
public const string ScenePersistent = "Scene_Persistent";
public const string SceneMainMenu = "Scene_MainMenu";
/// <summary>Addressable key用于 Addressables.LoadSceneAsync。</summary>
public const string ScenePersistent = "Scene_Persistent";
/// <summary>
/// Unity 场景名(与文件名一致),用于 SceneManager.LoadScene 和 GameBootstrap。
/// 与 <see cref="ScenePersistent"/> 值相同,显式声明以区分两种使用场景。
/// </summary>
public const string ScenePersistentName = "Scene_Persistent";
/// <summary>Addressable key用于 Addressables.LoadSceneAsync。</summary>
public const string SceneMainMenu = "Scene_MainMenu";
// ── Player ──────────────────────────────────────────────────────
public const string PrefabPlayer = "PLY_Player";

View File

@@ -0,0 +1,62 @@
using UnityEngine;
using UnityEngine.SceneManagement;
using BaseGames.Core.Assets;
namespace BaseGames.Core
{
/// <summary>
/// 运行时引导器:在任意场景进入 Play Mode 时,自动保证 Persistent 场景先于起始场景加载。
///
/// 解决的核心问题:
/// 开发者从任意房间场景直接按 PlayGameManager / SceneService 等全局服务不存在。
///
/// 工作时机BeforeSceneLoad
/// 此回调在 Unity 加载第一个场景的资产之前触发。
/// 在这里调用 SceneManager.LoadScene(Additive) 会将 Persistent 场景加入加载队列,
/// 使其 AwakeDefaultExecutionOrder -2000早于起始场景的 Awake 执行,
/// 所有服务在起始场景的第一个 Awake 之前已完成注册。
///
/// 前提:
/// Scene_Persistent 必须已添加到 Build Settings不需要是 Index 0
/// Persistent 不应作为自动加载的 scene 0避免与本脚本冲突导致双重加载。
/// 推荐:将 Main Menu 场景作为 Build Index 0Persistent 作为任意非 0 索引保留。
///
/// 编辑器 Edit Mode 的便利性由 PersistentSceneAutoLoaderEditor 程序集)负责,
/// 本脚本只处理运行时(含发行版 Build的加载保证。
/// </summary>
public static class GameBootstrap
{
// ── 防止同一 Domain 内重复引导 ────────────────────────────────────────
// SubsystemRegistration 在每次 Domain Reload 时最早执行,用于重置静态状态。
private static bool _bootstrapped;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetOnDomainReload() => _bootstrapped = false;
// ── 核心引导入口 ──────────────────────────────────────────────────────
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void EnsurePersistentLoaded()
{
if (_bootstrapped) return;
_bootstrapped = true;
// 若 Persistent 已在 Hierarchy 中(例如从 Persistent 场景本身按 Play跳过。
// 注意BeforeSceneLoad 时 sceneCount 通常为 0尚未加载任何场景
// 此检查主要应对极少数场景管理器提前初始化的情况。
for (int i = 0; i < SceneManager.sceneCount; i++)
{
string name = SceneManager.GetSceneAt(i).name;
if (name == AddressKeys.ScenePersistentName || name == "Persistent")
return;
}
// Additive 加载 Persistent必须在 Build Settings 中注册)
// 由于是 BeforeSceneLoad此加载在起始场景资产加载前完成
// GameServiceRegistrar.Awake(-2000) 将早于起始场景所有 Awake 执行。
#if UNITY_EDITOR
Debug.Log("[GameBootstrap] 自动加载 Persistent 场景BeforeSceneLoad。");
#endif
SceneManager.LoadScene(AddressKeys.ScenePersistentName, LoadSceneMode.Additive);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7713d082a2fb06b4096d6c5c41150606
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -8,10 +8,19 @@ namespace BaseGames.Core
/// <summary>
/// 在 Awake 时(最早执行)向 ServiceLocator 注册所有服务。
/// 挂载在 Persistent 场景的根 GameObject 上。
///
/// 重复加载保护GameBootstrapBeforeSceneLoad与 Persistent 同时存在于 Build Settings
/// 时可能导致双重加载。_registered 静态标志确保注册逻辑只执行一次,
/// 第二个实例的 GameObject 会被立即销毁。
/// </summary>
[DefaultExecutionOrder(-2000)]
public class GameServiceRegistrar : MonoBehaviour
{
// ── 重复加载保护(对应 GameBootstrap 的双重加载边界情况)────────────
private static bool _registered;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetOnDomainReload() => _registered = false;
[SerializeField] private DeathRespawnService _deathRespawnService;
[SerializeField] private SceneService _sceneService;
[SerializeField] private EventChannelRegistry _eventChannelRegistry;
@@ -26,6 +35,10 @@ namespace BaseGames.Core
private void Awake()
{
// 重复加载保护若已有实例完成注册销毁本对象Persistent 双重加载的边界情况)
if (_registered) { Destroy(gameObject); return; }
_registered = true;
// 若 Inspector 已绑定主 AudioListener直接使用跳过全场景扫描
if (_primaryListener != null)
DisableDuplicateListenersInCurrentScenes();