Files
zeling_v2/Assets/Scripts/Core/GameServiceRegistrar.cs
2026-05-12 15:34:08 +08:00

192 lines
9.1 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 UnityEngine;
using UnityEngine.SceneManagement;
using BaseGames.Core.Events;
using BaseGames.Core.Save;
namespace BaseGames.Core
{
/// <summary>
/// 在 Awake 时(最早执行)向 ServiceLocator 注册所有服务。
/// 挂载在 Persistent 场景的根 GameObject 上。
/// </summary>
[DefaultExecutionOrder(-2000)]
public class GameServiceRegistrar : MonoBehaviour
{
[SerializeField] private DeathRespawnService _deathRespawnService;
[SerializeField] private SceneService _sceneService;
[SerializeField] private EventChannelRegistry _eventChannelRegistry;
[SerializeField] private SaveManager _saveManager;
/// <summary>
/// Persistent 场景中唯一保留的主 AudioListener通常挂在主相机上
/// 在 Inspector 中绑定后可完全跳过 Awake 时的 FindObjectsOfType 全场景扫描。
/// 未绑定时自动执行运行时扫描。
/// </summary>
[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<IAudioService>(new NullAudioService());
if (_deathRespawnService)
ServiceLocator.Register<IDeathRespawnService>(_deathRespawnService);
if (_sceneService)
ServiceLocator.Register<ISceneService>(_sceneService);
if (_eventChannelRegistry)
ServiceLocator.Register<IEventChannelRegistry>(_eventChannelRegistry);
if (_saveManager)
ServiceLocator.Register<ISaveService>(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<AudioListener>(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();
}
}
/// <summary>
/// 当 Inspector 已预绑定 _primaryListener 时,无需全场景扫描。
/// 仅遍历当前已加载场景的根节点,禁用多余的 AudioListener。
/// </summary>
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<AudioListener>(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<AudioListener>(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;
}
}
}
/// <summary>
/// 将 SaveManager 适配为 ISaveService。
/// 由于 BaseGames.Core.Save 与 BaseGames.Core 存在循环依赖风险,
/// SaveManager 不直接声明实现 ISaveService由此 Adapter 桥接,
/// 保持 Core → Core.Save 的单向依赖方向不变。
/// </summary>
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<bool> 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<string> GetCompletedChains() => _save.GetCompletedChains();
public void SetChainCompleted(string chainId) => _save.SetChainCompleted(chainId);
}
}