using UnityEngine; using UnityEngine.SceneManagement; using BaseGames.Core.Events; using BaseGames.Core.Save; namespace BaseGames.Core { /// /// 在 Awake 时(最早执行)向 ServiceLocator 注册所有服务。 /// 挂载在 Persistent 场景的根 GameObject 上。 /// [DefaultExecutionOrder(-2000)] public class GameServiceRegistrar : MonoBehaviour { [SerializeField] private DeathRespawnService _deathRespawnService; [SerializeField] private SceneService _sceneService; [SerializeField] private EventChannelRegistry _eventChannelRegistry; [SerializeField] private SaveManager _saveManager; /// /// Persistent 场景中唯一保留的主 AudioListener(通常挂在主相机上)。 /// 在 Inspector 中绑定后可完全跳过 Awake 时的 FindObjectsOfType 全场景扫描。 /// 未绑定时自动执行运行时扫描。 /// [SerializeField] private AudioListener _primaryListener; private bool _audioListenerFixLogged; private void Awake() { // 若 Inspector 已绑定主 AudioListener,直接使用,跳过全场景扫描 if (_primaryListener != null) DisableDuplicateListenersInCurrentScenes(); else EnsureSingleAudioListener(); // 未绑定时回退:全量扫描并自动缓存 SceneManager.sceneLoaded += OnSceneLoaded; // 注册 NullAudioService 作为兜底;AudioManager.Awake 后会用真实实现覆盖 ServiceLocator.RegisterIfAbsent(new NullAudioService()); if (_deathRespawnService) ServiceLocator.Register(_deathRespawnService); if (_sceneService) ServiceLocator.Register(_sceneService); if (_eventChannelRegistry) ServiceLocator.Register(_eventChannelRegistry); if (_saveManager) ServiceLocator.Register(new SaveServiceAdapter(_saveManager)); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (_primaryListener != null) { // 仅扫描新加载场景的根节点,避免 FindObjectsOfType 全场景遍历 int disabled = 0; foreach (var root in scene.GetRootGameObjects()) foreach (var al in root.GetComponentsInChildren(true)) { if (al == _primaryListener || !al.enabled) continue; al.enabled = false; disabled++; } if (disabled > 0) Debug.LogWarning($"[GameServiceRegistrar] Scene '{scene.name}' 中禁用了 {disabled} 个多余的 AudioListener。"); } else { // 首次加载时 _primaryListener 可能尚未缓存,回退到全量扫描 EnsureSingleAudioListener(); } } /// /// 当 Inspector 已预绑定 _primaryListener 时,无需全场景扫描。 /// 仅遍历当前已加载场景的根节点,禁用多余的 AudioListener。 /// private void DisableDuplicateListenersInCurrentScenes() { int sceneCount = SceneManager.sceneCount; int disabled = 0; for (int s = 0; s < sceneCount; s++) { Scene scene = SceneManager.GetSceneAt(s); if (!scene.isLoaded) continue; foreach (var root in scene.GetRootGameObjects()) foreach (var al in root.GetComponentsInChildren(true)) { if (al == _primaryListener || !al.enabled) continue; al.enabled = false; disabled++; } } if (disabled > 0 && !_audioListenerFixLogged) { Debug.LogWarning($"[GameServiceRegistrar] Disabled {disabled} duplicate AudioListener(s). Primary: '{_primaryListener.gameObject.name}'."); _audioListenerFixLogged = true; } } private void EnsureSingleAudioListener() { AudioListener[] listeners = FindObjectsOfType(true); AudioListener primary = null; int enabledCount = 0; for (int i = 0; i < listeners.Length; i++) { AudioListener listener = listeners[i]; if (listener == null || !listener.enabled || !listener.gameObject.activeInHierarchy) continue; enabledCount++; if (primary == null) primary = listener; if (listener.gameObject.scene.name == "Persistent") primary = listener; } if (enabledCount <= 1 || primary == null) { _primaryListener = primary; // 缓存(含 enabledCount==1 时的单个监听器) return; } int disabled = 0; for (int i = 0; i < listeners.Length; i++) { AudioListener listener = listeners[i]; if (listener == null || listener == primary) continue; if (!listener.enabled || !listener.gameObject.activeInHierarchy) continue; listener.enabled = false; disabled++; } _primaryListener = primary; // 缓存主监听器 if (disabled > 0 && !_audioListenerFixLogged) { Debug.LogWarning($"[GameServiceRegistrar] Found {enabledCount} active AudioListeners. Kept '{primary.gameObject.name}' and disabled {disabled} duplicate listener(s)."); _audioListenerFixLogged = true; } } } /// /// 将 SaveManager 适配为 ISaveService。 /// 由于 BaseGames.Core.Save 与 BaseGames.Core 存在循环依赖风险, /// SaveManager 不直接声明实现 ISaveService;由此 Adapter 桥接, /// 保持 Core → Core.Save 的单向依赖方向不变。 /// internal sealed class SaveServiceAdapter : ISaveService { private readonly BaseGames.Core.Save.SaveManager _save; internal SaveServiceAdapter(BaseGames.Core.Save.SaveManager save) => _save = save; // ── I/O 操作 ────────────────────────────────────────────────────── public System.Threading.Tasks.Task SaveAsync(int slot) => _save.SaveAsync(slot); public System.Threading.Tasks.Task LoadAsync(int slot) => _save.LoadAsync(slot); public void QuickSave() => _save.QuickSave(); public void QuickLoad() => _save.QuickLoad(); public System.Threading.Tasks.Task QuickLoadAsync() => _save.LoadAsync(BaseGames.Core.Save.SaveManager.QuickSaveSlot); public bool HasSave(int slot) => _save.SlotExists(slot); public int ActiveSlot => _save.CurrentSlot; public System.Threading.Tasks.Task DeleteSlotAsync(int slot) => _save.DeleteSlotAsync(slot); // ── 存档点 ──────────────────────────────────────────────────────── public string LastCheckpointScene => _save.LastCheckpointScene; public string LastCheckpointSpawnId => _save.LastCheckpointSpawnId; // ── 世界状态查询 ────────────────────────────────────────────────── public bool IsWorldCollected(string id) => _save.IsWorldCollected(id); public bool IsDoorOpened(string id) => _save.IsDoorOpened(id); public bool IsBossDefeated(string bossId) => _save.IsBossDefeated(bossId); public int GetPlayerMaxHP() => _save.GetPlayerMaxHP(); public bool IsFirstClear(string challengeId) => _save.IsFirstClear(challengeId); // ── 世界标志 & 事件链 ───────────────────────────────────────────── public bool GetFlag(string flagId) => _save.GetFlag(flagId); public void SetFlag(string flagId, bool value) => _save.SetFlag(flagId, value); public System.Collections.Generic.IEnumerable GetCompletedChains() => _save.GetCompletedChains(); public void SetChainCompleted(string chainId) => _save.SetChainCompleted(chainId); } }