192 lines
9.1 KiB
C#
192 lines
9.1 KiB
C#
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);
|
||
}
|
||
}
|