boxmoe_header_banner_img

菜就多练喵

文章导读

[C#][Unity][Ib_Core]Ib_UpdateManager统一更新管理器


avatar
Ib_Mccf 2025年12月22日 18

功能:使不同类可以按不同优先级、不同频率,统一的Update。

特点:可以管理不同类的Update顺序与频率。调整顺序可以保证逻辑的一致性,调整频率可以减少不必要的性能消耗,统一调用可以减少Unity底层调用Update()的开销。

使用方法:将Ib_UpdateManager挂载到场景的物体上。类继承接口IUpdate后,实现ManagedUpdate(float deltaTime);方法,并通过Ib_Event事件系统在初始化的时候注册到Ib_UpdateManager,并在销毁时注销即可。重复注册可以更改优先级和频率。

使用示例:

using Cysharp.Threading.Tasks;
using Ib_Core;
using UnityEngine;

public class Sample_Ib_Update_Test1 : MonoBehaviour,IUpdate
{
    void Start()
    {
        //调用IUpdate的拓展方法注册到Ib_UpdateManager,更新优先级为Normal,间隔为0,即每帧更新
        this.RegisterOrUpdateManagedUpdate(Ib_UpdatePriority.Normal,0,this.GetCancellationTokenOnDestroy());
    }

    void OnDestroy()
    {
        //调用IUpdate的拓展方法从Ib_UpdateManager中注销,避免内存泄漏
        this.DeregisterManagedUpdate();
    }
    
    /// <summary>
    /// 实现IUpdate接口的ManagedUpdate方法,可以接收时间差
    /// </summary>
    /// <param name="deltaTime">时间差</param>
    public void ManagedUpdate(float deltaTime)
    {
        Ib_Log.Info($"[{System.DateTime.Now}]>> {GetType().Name} >> ManagedUpdate >> deltaTime:{deltaTime}");
    }
}
public class Sample_Ib_Update_Test2 : MonoBehaviour,IUpdate
{
    void Start()
    {
        //优先级为Early,在Normal之前更新,间隔为0,即每帧更新
        this.RegisterOrUpdateManagedUpdate(Ib_UpdatePriority.Early,0,this.GetCancellationTokenOnDestroy());
    }

    ......

}
public class Sample_Ib_Update_Test3 : MonoBehaviour,IUpdate
{
    void Start()
    {
        //优先级为Normal,间隔为0.5秒,即每0.5秒更新一次
        this.RegisterOrUpdateManagedUpdate(Ib_UpdatePriority.Normal,0.5f,this.GetCancellationTokenOnDestroy());
    }
 
    ......

}

下图可以看到每帧调用下来优先级为Early(值为-1)的Test2总是在优先级为Normal(值为0)的Test1前更新,更新间隔为0.5s、优先级为Normal的Test3仍保持着正确的顺序更新。

源码:

Ib_UpdateManager继承了泛型单例,这不是必须的,所有的注册和注销都是通过Ib_Event事件中心进行的。只需要保证场景上只有一个Ib_UdpateManager在运行即可。若没有Ib_Event可以直接使用单例注册,修改Ib_UpdateExtensions中的注册方法即可。

using System.Collections.Generic;
using Ib_Core;
using Ib_Core.Ib_Controller;
using UnityEngine;

public class Ib_UpdateBox
{
    public IUpdate updateTarget;
    public int updatePriority;
    public float updateInterval;
    public float updateTimer;
    public int listIndex; 
    public Ib_UpdateBox(IUpdate update,int priority, float interval)
    {
        updateTarget = update;
        updatePriority = priority;
        updateInterval = interval;
        updateTimer = 0;
        listIndex = -1;
    }
}
/// <summary>
/// 同一Update管理器。支持优先级和不同更新频率。
/// 通过OnRegisterOrUpdateManagedUpdate方法注册,OnDeregisterManagedUpdate撤销
/// </summary>
public class Ib_UpdateManager : Ib_Singleton<Ib_UpdateManager>
{
    /// <summary>
    /// 最大回溯帧数,用于防止更新频率过低导致的卡顿
    /// </summary>
    private const int MaxBacklogFrame = 5;
    /// <summary>
    /// 检查GO物体是否已被销毁
    /// </summary>
    private const bool CheckFakeNull = false;
    #region 订阅部分
    private readonly Dictionary<int, List<Ib_UpdateBox>> _priorityUpdates = new Dictionary<int, List<Ib_UpdateBox>>();
    private readonly List<Ib_UpdateBox> _pendingAdditions = new List<Ib_UpdateBox>();
    private readonly HashSet<IUpdate> _pendingAdditionsSet = new HashSet<IUpdate>();
    private readonly List<IUpdate> _pendingRemovals = new List<IUpdate>();
    private readonly List<Ib_UpdateBox> _pendingChanges = new List<Ib_UpdateBox>();

    private readonly Dictionary<IUpdate, Ib_UpdateBox> _registeredDic = new Dictionary<IUpdate, Ib_UpdateBox>();
    private List<int> _sortedPriorityKeys = new List<int>();
    private bool _isPriorityKeysDirty = false;
    protected override void Awake()
    {
        base.Awake();
        //订阅注册事件
        OnRegisterOrUpdateManagedUpdate.Register(RegisterManagedUpdate);
        OnDeregisterManagedUpdate.Register(DeregisterManagedUpdate);
    }

    protected override void OnDestroy()
    {
        base.OnDestroy();
        //取消订阅事件
        OnRegisterOrUpdateManagedUpdate.Deregister(RegisterManagedUpdate);
        OnDeregisterManagedUpdate.Deregister(DeregisterManagedUpdate);
    }
    
    private void RegisterManagedUpdate(IUpdate managedUpdate,Ib_UpdatePriority updatePriority,float updateInterval)
    {
        if (managedUpdate == null) return;
        //保证更新频率是非负数
        updateInterval = Mathf.Max(0, updateInterval);
        //完全新增,加入待更新列表
        if (!_registeredDic.ContainsKey(managedUpdate))
        {
            //防止重复添加
            if (_pendingAdditionsSet.Contains(managedUpdate)) return;
            var box = new Ib_UpdateBox(managedUpdate, (int)updatePriority, updateInterval);
            _pendingAdditions.Add(box);
            _pendingAdditionsSet.Add(box.updateTarget);
        }
        else
        {
            var existingBox = _registeredDic[managedUpdate];
            // 仅更新频率
            if (existingBox.updatePriority == (int)updatePriority)
            {
                existingBox.updateInterval = updateInterval;
            }
            // 更新优先级,加入待更新列表
            else
            {
                // 创建一个临时的Box作为数据载体传给 ProcessPendingLists
                var changeBox = new Ib_UpdateBox(managedUpdate, (int)updatePriority, updateInterval);
                _pendingChanges.Add(changeBox);
            }
        }
    }
    private void DeregisterManagedUpdate(IUpdate managedUpdate)
    {
        //如果已经在运行列表中,加入待移除队列
        if (_registeredDic.ContainsKey(managedUpdate))
        {
            _pendingRemovals.Add(managedUpdate);
        }
        //如果还在待添加列表中(同帧注册),直接从待添加列表移除
        else 
        {
            if (!_pendingAdditionsSet.Contains(managedUpdate)) return;
            int pendingIndex = _pendingAdditions.FindIndex(x => x.updateTarget == managedUpdate);
            if (pendingIndex != -1)
            {
                _pendingAdditions.RemoveAt(pendingIndex);
            }
            _pendingAdditionsSet.Remove(managedUpdate);
        }
    }
    
    #endregion

    #region 执行部分

    private Ib_UpdateBox _updateBox;
    private void Update()
    {
        ProcessPendingLists();
            
        if (_isPriorityKeysDirty)
        {
            //当有新的优先级时,进行排序
            SortPriorityKeys();
        }
        // 按照优先级顺序遍历
        foreach (int priority in _sortedPriorityKeys)
        {
            var updateList = _priorityUpdates[priority];
            int updateCnt = updateList.Count;
            float deltaTime = Time.deltaTime;
            for (int i = 0; i < updateCnt; i++)
            {
                _updateBox = updateList[i];
                
                if (CheckFakeNull && _updateBox.updateTarget is UnityEngine.Object uObj && uObj == null) 
                {
                    DeregisterManagedUpdate(_updateBox.updateTarget);
                    continue; 
                }
                
                if (_updateBox.updateInterval == 0)
                {
                    try
                    {
                        _updateBox.updateTarget.ManagedUpdate(deltaTime);
                    }
                    catch (System.Exception ex)
                    {
                        Ib_Log.Error($"Update failed for {_updateBox.updateTarget.GetType().Name}");
                        Ib_Log.Error(ex);
                    }
                    _updateBox.updateTimer = 0;
                    continue; 
                }
                
                _updateBox.updateTimer += deltaTime;
                
                //允许的最大积压更新次数,当卡顿时会一次性更新
                if (_updateBox.updateTimer > _updateBox.updateInterval * MaxBacklogFrame)
                {
                    _updateBox.updateTimer = _updateBox.updateInterval * MaxBacklogFrame;
                }

                while (_updateBox.updateTimer >= _updateBox.updateInterval)
                {
                    try
                    {
                        _updateBox.updateTarget.ManagedUpdate(_updateBox.updateInterval);
                    }
                    catch (System.Exception ex)
                    {
                        // 异常处理逻辑只在这里,当异常真的发生时才执行
                        Ib_Log.Error($"Update failed for {_updateBox.updateTarget.GetType().Name}");
                        Ib_Log.Error(ex);
                    }
                    _updateBox.updateTimer -= _updateBox.updateInterval;
                }
            }
        }
    }

    private void ProcessPendingLists()
    {
        if (_pendingRemovals.Count > 0)
        {
            foreach (var toRemove in _pendingRemovals)
            {
                if (_registeredDic.TryGetValue(toRemove, out var boxToRemove))
                {
                    RemoveBoxFromList(boxToRemove);
                    _registeredDic.Remove(toRemove);
                }
            }
            _pendingRemovals.Clear();
        }
        if (_pendingAdditions.Count > 0)
        {
            foreach (var toAdd in _pendingAdditions)
            {
                if (!_registeredDic.ContainsKey(toAdd.updateTarget))
                {
                    AddBoxToList(toAdd);
                    _registeredDic.Add(toAdd.updateTarget, toAdd);
                }
            }
            _pendingAdditions.Clear();
            _pendingAdditionsSet.Clear();
        }


        if (_pendingChanges.Count > 0)
        {
            foreach (var change in _pendingChanges)
            {
                if (_registeredDic.TryGetValue(change.updateTarget, out var oldBox))
                {
                    RemoveBoxFromList(oldBox);
                    change.listIndex = -1; 
                    change.updateTimer = oldBox.updateTimer; // 继承之前的计时器状态
                    AddBoxToList(change);
                    _registeredDic[change.updateTarget] = change;
                }
            }
            _pendingChanges.Clear();
        }
    }
    private void RemoveBoxFromList(Ib_UpdateBox box)
    {
        if (_priorityUpdates.TryGetValue(box.updatePriority, out var list))
        {
            int indexToRemove = box.listIndex;
            int lastIndex = list.Count - 1;
                
            // 越界保护
            if (indexToRemove < 0 || indexToRemove > lastIndex) return;

            if (indexToRemove < lastIndex)
            {
                var lastBox = list[lastIndex];
                list[indexToRemove] = lastBox;
                lastBox.listIndex = indexToRemove;
            }
            list.RemoveAt(lastIndex);
        }
    }

    private void AddBoxToList(Ib_UpdateBox box)
    {
        if (!_priorityUpdates.TryGetValue(box.updatePriority, out var list))
        {
            list = new List<Ib_UpdateBox>();
            _priorityUpdates[box.updatePriority] = list;
            _isPriorityKeysDirty = true;
        }
        list.Add(box);
        box.listIndex = list.Count - 1;
    }
    private void SortPriorityKeys()
    {
        _sortedPriorityKeys.Clear();
        _sortedPriorityKeys.AddRange(_priorityUpdates.Keys);
        _sortedPriorityKeys.Sort();
        _isPriorityKeysDirty = false;
    }

    #endregion
}
namespace Ib_Core
{
    using System.Threading;
    /// <summary>
    /// ManagedUpdate的优先级
    /// </summary>
    public enum Ib_UpdatePriority
    {
        First = -2,
        Early = -1,
        Normal = 0,   
        Late = 1,
        Last = 2,
    }
    public class OnRegisterOrUpdateManagedUpdate : Ib_Event<OnRegisterOrUpdateManagedUpdate, IUpdate,Ib_UpdatePriority,float> { };
    public class OnDeregisterManagedUpdate : Ib_Event<OnDeregisterManagedUpdate,IUpdate> { };

    public interface IUpdate
    {
        public void ManagedUpdate(float deltaTime);
    }
}
namespace Ib_Core
{
    using System.Threading;
    /// <summary>
    /// Ib_Update的拓展方法,简化了注册
    /// </summary>
    public static class Ib_UpdateExtensions
    {
        /// <summary>
        /// 注册或更新ManagedUpdate
        /// </summary>
        /// <param name="self"></param>
        /// <param name="priority">更新的优先级,越小越早执行</param>
        /// <param name="interval">每次更新的间隔</param>
        /// <param name="cts">异步取消令牌</param>
        public static void RegisterOrUpdateManagedUpdate(this IUpdate self, Ib_UpdatePriority priority, float interval, CancellationToken cts)
        {
            //通过Ib_Event事件来注册到Ib_UpdateManager
            OnRegisterOrUpdateManagedUpdate.InvokeNextFrame(self,priority,interval,cts);
        }

        public static void DeregisterManagedUpdate(this IUpdate self)
        {
            //通过Ib_Event事件来撤销注册到Ib_UpdateManager
            OnDeregisterManagedUpdate.InvokeNextFrame(self,CancellationToken.None);
        }
    }
}


评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字

插入代码