using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Utils; using Object = UnityEngine.Object; // 假设此命名空间包含MonoSingleton namespace Base { /// /// 定义一个Tick更新接口,用于在常规Update中执行逻辑。 /// public interface ITick { public void Tick(); } /// /// 定义一个TickPhysics更新接口,用于在FixedUpdate中执行物理逻辑。 /// public interface ITickPhysics { public void TickPhysics(); } /// /// 定义一个TickUI更新接口,用于在常规Update中执行UI逻辑。 /// public interface ITickUI { public void TickUI(); } /// /// 全局计时器和更新管理器,负责在Unity的Update、FixedUpdate和LateUpdate生命周期中调度注册的Ticks。 /// 支持游戏暂停、场景加载时自动重置以及缓冲区的添加/移除操作以避免迭代器错误。 /// public class Clock : MonoSingleton { // 存储所有需要在FixedUpdate中执行物理Tick逻辑的对象 private readonly HashSet _tickPhysics = new(); // 待添加的ITickPhysics对象缓冲区 private readonly HashSet _tickPhysicsToAdd = new(); // 待移除的ITickPhysics对象缓冲区 private readonly HashSet _tickPhysicsToRemove = new(); // 存储所有需要在常规Update中执行Tick逻辑的对象 private readonly HashSet _ticks = new(); // 待添加的ITick对象缓冲区 private readonly HashSet _ticksToAdd = new(); // 待移除的ITick对象缓冲区 private readonly HashSet _ticksToRemove = new(); // 存储所有需要在常规Update中执行UI Tick逻辑的对象 private readonly HashSet _tickUIs = new(); // 待添加的ITickUI对象缓冲区 private readonly HashSet _tickUIsToAdd = new(); // 待移除的ITickUI对象缓冲区 private readonly HashSet _tickUIsToRemove = new(); // 游戏是否暂停 private bool _pause; /// /// 获取或设置游戏的暂停状态。当设置为true时,Time.timeScale将变为0;当设置为false时,Time.timeScale将恢复为1。 /// 该操作会检查当前状态,避免重复设置Time.timeScale。 /// public bool Pause { get => _pause; set { // 如果新值与当前暂停状态不同,则更新Time.timeScale。 if (value != _pause) { Time.timeScale = value ? 0 : 1; _pause = value; } } } /// /// 每帧更新方法。 /// 如果游戏未暂停,则执行所有注册的ITick对象的Tick方法; /// 执行所有注册的ITickUI对象的TickUI方法(UI通常不受暂停影响)。 /// private void Update() { ApplyBufferedChanges(); // 如果游戏未暂停,则执行常规的Tick更新。 if (!_pause) foreach (var tick in _ticks) try { tick.Tick(); } catch (Exception e) { Debug.LogError($"[Clock] ITick对象 '{tick.GetType().Name}' 在Tick()执行期间抛出异常: {e}"); } // UI更新通常不受游戏暂停影响(例如菜单动画、UI计时器等)。 foreach (var uiTick in _tickUIs) try { uiTick.TickUI(); } catch (Exception e) { Debug.LogError($"[Clock] ITickUI对象 '{uiTick.GetType().Name}' 在TickUI()执行期间抛出异常: {e}"); } } /// /// 每固定帧更新方法。 /// 如果游戏未暂停,则执行所有注册的ITickPhysics对象的TickPhysics方法。 /// private void FixedUpdate() { // 如果游戏未暂停,则执行物理Tick更新。 if (!_pause) foreach (var physicsTick in _tickPhysics) try { physicsTick.TickPhysics(); } catch (Exception e) { Debug.LogError( $"[Clock] ITickPhysics对象 '{physicsTick.GetType().Name}' 在TickPhysics()执行期间抛出异常: {e}"); } } /// /// 在Clock组件被销毁时调用。 /// 取消订阅场景加载事件并清理所有内部Tick列表。 /// protected void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoadedCallback; ClearAllTicksInternal(false); } /// /// 在单例首次创建时调用。 /// protected override void OnStart() { SceneManager.sceneLoaded += OnSceneLoadedCallback; } /// /// 场景加载完成时的回调方法。 /// 用于在加载新场景后清理所有Tick列表并重置游戏暂停状态。 /// /// 被加载的场景。 /// 场景加载模式。 private void OnSceneLoadedCallback(Scene scene, LoadSceneMode mode) { ClearAllTicksInternal(true); } /// /// 将一个ITick对象添加到待添加缓冲区。它将在下一个LateUpdate中被添加到主Tick列表。 /// 如果对象已经在待移除列表中,则会先从待移除列表中移除,以处理添加/移除冲突。 /// /// 要添加的ITick对象。 public static void AddTick(ITick tick) { if (tick != null) { Instance._ticksToAdd.Add(tick); Instance._ticksToRemove.Remove(tick); } } /// /// 将一个ITick对象添加到待移除缓冲区。它将在下一个LateUpdate中从主Tick列表移除。 /// 如果对象已经在待添加列表中,则会先从待添加列表中移除,以处理添加/移除冲突。 /// /// 要移除的ITick对象。 public static void RemoveTick(ITick tick) { if (tick != null) { Instance._ticksToRemove.Add(tick); Instance._ticksToAdd.Remove(tick); } } /// /// 将一个ITickPhysics对象添加到待添加缓冲区。它将在下一个LateUpdate中被添加到主物理Tick列表。 /// 如果对象已经在待移除列表中,则会先从待移除列表中移除,以处理添加/移除冲突。 /// /// 要添加的ITickPhysics对象。 public static void AddTickPhysics(ITickPhysics physics) { if (physics != null) { Instance._tickPhysicsToAdd.Add(physics); Instance._tickPhysicsToRemove.Remove(physics); } } /// /// 将一个ITickPhysics对象添加到待移除缓冲区。它将在下一个LateUpdate中从主物理Tick列表移除。 /// 如果对象已经在待添加列表中,则会先从待添加列表中移除,以处理添加/移除冲突。 /// /// 要移除的ITickPhysics对象。 public static void RemoveTickPhysics(ITickPhysics physics) { if (physics != null) { Instance._tickPhysicsToRemove.Add(physics); Instance._tickPhysicsToAdd.Remove(physics); } } /// /// 将一个ITickUI对象添加到待添加缓冲区。它将在下一个LateUpdate中被添加到主UI Tick列表。 /// 如果对象已经在待移除列表中,则会先从待移除列表中移除,以处理添加/移除冲突。 /// /// 要添加的ITickUI对象。 public static void AddTickUI(ITickUI ui) { if (ui != null) { Instance._tickUIsToAdd.Add(ui); Instance._tickUIsToRemove.Remove(ui); } } /// /// 将一个ITickUI对象添加到待移除缓冲区。它将在下一个LateUpdate中从主UI Tick列表移除。 /// 如果对象已经在待添加列表中,则会先从待添加列表中移除,以处理添加/移除冲突。 /// /// 要移除的ITickUI对象。 public static void RemoveTickUI(ITickUI ui) { if (ui != null) { Instance._tickUIsToRemove.Add(ui); Instance._tickUIsToAdd.Remove(ui); } } /// /// 将缓冲区中的添加和移除操作应用到主Tick列表中。 /// 此方法应在LateUpdate中调用,以确保在所有Tick执行完毕后进行列表修改,从而避免迭代器错误。 /// private void ApplyBufferedChanges() { if (_ticksToRemove.Count > 0) { foreach (var tick in _ticksToRemove) _ticks.Remove(tick); _ticksToRemove.Clear(); } if (_tickPhysicsToRemove.Count > 0) { foreach (var physicsTick in _tickPhysicsToRemove) _tickPhysics.Remove(physicsTick); _tickPhysicsToRemove.Clear(); } if (_tickUIsToRemove.Count > 0) { foreach (var uiTick in _tickUIsToRemove) _tickUIs.Remove(uiTick); _tickUIsToRemove.Clear(); } if (_ticksToAdd.Count > 0) { foreach (var tick in _ticksToAdd) { if (tick is Object unityObject && !unityObject) continue; // 跳过已销毁的Unity对象 _ticks.Add(tick); } _ticksToAdd.Clear(); } if (_tickPhysicsToAdd.Count > 0) { foreach (var physicsTick in _tickPhysicsToAdd) { if (physicsTick is Object unityObject && !unityObject) continue; _tickPhysics.Add(physicsTick); } _tickPhysicsToAdd.Clear(); } if (_tickUIsToAdd.Count > 0) { foreach (var uiTick in _tickUIsToAdd) { if (uiTick is Object unityObject && unityObject == null) continue; _tickUIs.Add(uiTick); } _tickUIsToAdd.Clear(); } } /// /// 集中处理所有Tick列表和缓冲区的清理。 /// 此方法会在场景加载、Clock被禁用或销毁时调用。 /// /// 指示是否同时重置Pause状态和Time.timeScale。 private void ClearAllTicksInternal(bool clearPauseState) { _ticks.Clear(); _tickPhysics.Clear(); _tickUIs.Clear(); // 逻辑修改: // 在场景加载时,不应清除_ToAdd缓冲区。 // 这些缓冲区用于收集当前帧内(包括场景加载期间)的有效注册请求。 // 清除它们会导致在场景加载过程中新注册的Ticks被立即抹除。 // 对于前一场景中已销毁的Tick引用,将在ApplyBufferedChanges中进行过滤处理。 // _ticksToAdd.Clear(); // _tickPhysicsToAdd.Clear(); // _tickUIsToAdd.Clear(); _ticksToRemove.Clear(); _tickPhysicsToRemove.Clear(); _tickUIsToRemove.Clear(); if (clearPauseState) Pause = false; } } }