using System.Collections.Generic; using UnityEngine; using UnityEngine.SceneManagement; using Utils; // 假设此命名空间包含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 { 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; } } } /// /// 存储所有需要在常规Update中执行Tick逻辑的对象。使用HashSet确保唯一性并提高查找效率。 /// private readonly HashSet _ticks = new(); /// /// 存储所有需要在FixedUpdate中执行物理Tick逻辑的对象。使用HashSet确保唯一性并提高查找效率。 /// private readonly HashSet _tickPhysics = new(); /// /// 存储所有需要在常规Update中执行UI Tick逻辑的对象。使用HashSet确保唯一性并提高查找效率。 /// private readonly HashSet _tickUIs = new(); /// /// 待添加的ITick对象缓冲区。在LateUpdate中统一处理,以避免在迭代主列表时修改列表。 /// private readonly HashSet _ticksToAdd = new(); /// /// 待添加的ITickPhysics对象缓冲区。在LateUpdate中统一处理。 /// private readonly HashSet _tickPhysicsToAdd = new(); /// /// 待添加的ITickUI对象缓冲区。在LateUpdate中统一处理。 /// private readonly HashSet _tickUIsToAdd = new(); /// /// 待移除的ITick对象缓冲区。在LateUpdate中统一处理,以避免在迭代主列表时修改列表。 /// private readonly HashSet _ticksToRemove = new(); /// /// 待移除的ITickPhysics对象缓冲区。在LateUpdate中统一处理。 /// private readonly HashSet _tickPhysicsToRemove = new(); /// /// 待移除的ITickUI对象缓冲区。在LateUpdate中统一处理。 /// private readonly HashSet _tickUIsToRemove = new(); private void Update() { // 如果游戏未暂停,则执行常规的Tick更新。 if (!_pause) { // 迭代主列表,确保_ticks在Update生命周期内不会被Add/Remove直接修改。 // 添加和移除操作通过缓冲区在LateUpdate处理。 foreach (var tick in _ticks) { tick.Tick(); } } // UI更新通常不受游戏暂停影响(例如菜单动画、UI计时器等)。 // 这是当前的默认设计,如果需要UI也暂停,则需修改此处逻辑或引入单独的UI暂停状态。 foreach (var uiTick in _tickUIs) { uiTick.TickUI(); } } private void FixedUpdate() { // 如果游戏未暂停,则执行物理Tick更新。 if (!_pause) { foreach (var physicsTick in _tickPhysics) { physicsTick.TickPhysics(); } } } /// /// 在所有Update操作完成后,应用Tick注册和注销的缓冲区更改,确保列表在迭代期间不被修改。 /// private void LateUpdate() { ApplyBufferedChanges(); } private void OnDestroy() { // 在对象销毁时,取消订阅场景加载事件,避免潜在的内存泄漏。 SceneManager.sceneLoaded -= OnSceneLoaded; } /// /// 单例的初始化方法,在Clock实例的生命周期开始时调用(Unity的Awake后)。 /// 订阅场景加载事件,并执行初始设置。 /// protected override void OnStart() { SceneManager.sceneLoaded += OnSceneLoaded; Init(); // 初始化时清空所有列表并重新填充 } /// /// 场景加载完成时回调,用于重置所有Tick列表,以适应新场景中的对象。 /// /// 已加载的场景。 /// 场景加载模式。 private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { Init(); // 场景加载时重置 } /// /// 初始化或重置计时器系统。清空所有注册列表和缓冲列表,并重新扫描场景中的所有MonoBehaviour以注册相应的Tick接口。 /// public void Init() { // 清空所有主列表 _ticks.Clear(); _tickPhysics.Clear(); _tickUIs.Clear(); // 清空所有缓冲区列表 _ticksToAdd.Clear(); _tickPhysicsToAdd.Clear(); _tickUIsToAdd.Clear(); _ticksToRemove.Clear(); _tickPhysicsToRemove.Clear(); _tickUIsToRemove.Clear(); // 扫描场景中所有MonoBehaviour并注册它们实现的Tick接口。 // 使用HashSet会自动处理重复添加,确保列表唯一性。 foreach (var obj in FindObjectsByType(FindObjectsSortMode.None)) { if (obj is ITick tickObj) _ticks.Add(tickObj); if (obj is ITickPhysics physicsObj) _tickPhysics.Add(physicsObj); if (obj is ITickUI uiObj) _tickUIs.Add(uiObj); } } /// /// 将一个ITick对象添加到待添加缓冲区。它将在下一个LateUpdate中被添加到主Tick列表。 /// 如果对象已经在待移除列表中,则会先从待移除列表中移除,以处理添加/移除冲突。 /// /// 要添加的ITick对象。 public static void AddTick(ITick tick) { // 确保Clock实例存在且对象不为空。 if (Instance != null && tick != null) { Instance._ticksToAdd.Add(tick); Instance._ticksToRemove.Remove(tick); // 如果在待移除列表,则先从待移除中删除 } } /// /// 将一个ITick对象添加到待移除缓冲区。它将在下一个LateUpdate中从主Tick列表移除。 /// 如果对象已经在待添加列表中,则会先从待添加列表中移除,以处理添加/移除冲突。 /// /// 要移除的ITick对象。 public static void RemoveTick(ITick tick) { // 确保Clock实例存在且对象不为空。 if (Instance != null && tick != null) { Instance._ticksToRemove.Add(tick); Instance._ticksToAdd.Remove(tick); // 如果在待添加列表,则先从待添加中删除 } } /// /// 将一个ITickPhysics对象添加到待添加缓冲区。它将在下一个LateUpdate中被添加到主物理Tick列表。 /// 如果对象已经在待移除列表中,则会先从待移除列表中移除,以处理添加/移除冲突。 /// /// 要添加的ITickPhysics对象。 public static void AddTickPhysics(ITickPhysics physics) { // 确保Clock实例存在且对象不为空。 if (Instance != null && physics != null) { Instance._tickPhysicsToAdd.Add(physics); Instance._tickPhysicsToRemove.Remove(physics); } } /// /// 将一个ITickPhysics对象添加到待移除缓冲区。它将在下一个LateUpdate中从主物理Tick列表移除。 /// 如果对象已经在待添加列表中,则会先从待添加列表中移除,以处理添加/移除冲突。 /// /// 要移除的ITickPhysics对象。 public static void RemoveTickPhysics(ITickPhysics physics) { // 确保Clock实例存在且对象不为空。 if (Instance != null && physics != null) { Instance._tickPhysicsToRemove.Add(physics); Instance._tickPhysicsToAdd.Remove(physics); } } /// /// 将一个ITickUI对象添加到待添加缓冲区。它将在下一个LateUpdate中被添加到主UI Tick列表。 /// 如果对象已经在待移除列表中,则会先从待移除列表中移除,以处理添加/移除冲突。 /// /// 要添加的ITickUI对象。 public static void AddTickUI(ITickUI ui) { // 确保Clock实例存在且对象不为空。 if (Instance != null && ui != null) { Instance._tickUIsToAdd.Add(ui); Instance._tickUIsToRemove.Remove(ui); } } /// /// 将一个ITickUI对象添加到待移除缓冲区。它将在下一个LateUpdate中从主UI Tick列表移除。 /// 如果对象已经在待添加列表中,则会先从待添加列表中移除,以处理添加/移除冲突。 /// /// 要移除的ITickUI对象。 public static void RemoveTickUI(ITickUI ui) { // 确保Clock实例存在且对象不为空。 if (Instance != null && 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); // 从主列表移除,HashSet的移除操作平均为O(1) } _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) { // 添加到主列表。HashSet.Add平均为O(1),并自动处理重复添加(会忽略重复项)。 _ticks.Add(tick); } _ticksToAdd.Clear(); // 清空添加缓冲区 } if (_tickPhysicsToAdd.Count > 0) { foreach (var physicsTick in _tickPhysicsToAdd) { _tickPhysics.Add(physicsTick); // HashSet.Add 会自动处理重复 } _tickPhysicsToAdd.Clear(); } if (_tickUIsToAdd.Count > 0) { foreach (var uiTick in _tickUIsToAdd) { _tickUIs.Add(uiTick); // HashSet.Add 会自动处理重复 } _tickUIsToAdd.Clear(); } } } }