boxmoe_header_banner_img

菜就多练喵

文章导读

[C#][Unity][Ib_Core]Ib_Async——基于UniTask的自定义更新频率任务创建器<Ib_Async.CreateUpdateTask>


avatar
Ib_Mccf 2025年11月12日 52

UniTask地址:Cysharp/UniTask: Provides an efficient allocation free async/await integration for Unity.

功能:创建一个与Update()或FixedUpdate()类似,但更新频率和生命周期可以自定义的循环任务。

特点: 自定义更新频率与生命周期。不用在Update()中反复实现计时器。

*若有大量的异步方法同时在执行,会导致严重的性能问题。因此该方法不建议大量同时使用,不可代替Update()方法。

示例: 每0.2s恢复1点生命值。

//取消令牌,用来管理回血任务的生命周期
private CancellationTokenSource hpRegnCts;

void Start()
{
    //启用一个更新周期为0.2s,每次回复1点生命值的循环任务。包含任务开始与结束的回调。
    Ib_Async.CreateUpdateTask(HpRegn,0.2f,Ib_Async.ResetCts(ref hpRegnCts).Token);
}

void OnDestroy()
{
    //结束循环任务
    Ib_Async.ClearCts(ref hpRegnCts);
}

public int Hp = 0;
//回血1点
private void HpRegn()
{
    Hp += 1;
    Ib_Log.Info($"[{Time.time}]>>Hp增加了1点,当前Hp为{Hp}");
}

可以通过取消令牌随时结束循环任务,如下使用Ib_Async.DelayDoSomethingUniTask延迟调用方法在3秒后结束回血任务,自由控制任务的生命周期。还可以在创建循环任务时添加任务开始与结束的调用。

//取消令牌,用来管理回血任务的生命周期
private CancellationTokenSource hpRegnCts;
void Start()
{
    //启用一个更新周期为0.2s,每次回复1点生命值的循环任务。包含任务开始与结束的回调。
    Ib_Async.CreateUpdateTask(HpRegn,0.2f,Ib_Async.ResetCts(ref hpRegnCts).Token,true,OnEnterHpRegn,OnExitHpRegn);
    //在3秒后取消回血任务
    Ib_Async.DelayDoSomethingUniTask(3,EndHpRegn,this.GetCancellationTokenOnDestroy());
}

public int Hp = 0;
//回血1点
private void HpRegn()
{
    Hp += 1;
    Ib_Log.Info($"[{Time.time}]>>Hp增加了1点,当前Hp为{Hp}");
}
//结束回血
private void EndHpRegn()
{
    Ib_Async.ClearCts(ref hpRegnCts);
}

//循环任务的开始与结束的回调
private void OnEnterHpRegn() => Ib_Log.Info($"[{Time.time}]>>开始回血");
private void OnExitHpRegn() => Ib_Log.Info($"[{Time.time}]>>结束回血");

不完美之处:0.2秒的间隔是不太准确的,如上3秒内只调用了14次,理论上应该是15次,有一定的精度偏差。

源码:

namespace Ib_Core
{
    using System;
    using System.Threading;
    using Cysharp.Threading.Tasks;
    using UnityEngine;

    public static partial class Ib_Async
    {
        /// <summary>
        /// 重置令牌
        /// </summary>
        /// <param name="cts">取消令牌</param>
        /// <returns></returns>
        public static CancellationTokenSource ResetCts(ref CancellationTokenSource cts)
        {
            if (cts != null)
            {
                cts.Cancel();
                cts.Dispose();
            }

            cts = new();
            return cts;
        }
        /// <summary>
        /// 清除
        /// </summary>
        /// <param name="cts">取消令牌</param>
        /// <returns></returns>
        public static void ClearCts(ref CancellationTokenSource cts)
        {
            if (cts != null)
            {
                cts.Cancel();
                cts.Dispose();
            }

            cts = null;
        }
    }

    /// <summary>
    /// 基于Unitask的异步Update申请方法,支持自定义更新频率。通过令牌控制生命周期
    /// </summary>
    public static partial class Ib_Async
    {
        #region 循环任务创建调用

        /// <summary>
        /// 创建一个在 Update 循环中异步执行的任务
        /// </summary>
        /// <param name="action">要执行的操作</param>
        /// <param name="updateInterval">更新间隔(秒),0 表示每帧执行</param>
        /// <param name="cts">取消令牌</param>
        /// <param name="ignoreTimeScale">是否忽略时间缩放</param>
        /// <param name="onBegin">任务开始时的回调</param>
        /// <param name="onEnd">任务结束时的回调(无论正常结束还是取消都会调用)</param>
        public static void CreateUpdateTask(Action action, float updateInterval, CancellationToken cts, bool ignoreTimeScale = true, Action onBegin = null, Action onEnd = null)
        {
            if (action == null) throw new ArgumentNullException(nameof(action));
            if (updateInterval < 0) throw new ArgumentOutOfRangeException(nameof(updateInterval), "更新间隔不能为负数");
            DealUpdateTask(action, Mathf.Max(0, updateInterval), ignoreTimeScale ? DelayType.UnscaledDeltaTime : DelayType.DeltaTime, PlayerLoopTiming.Update, cts, onBegin, onEnd)
                .Forget();
        }
        /// <summary>
        /// 创建一个在 FixedUpdate 循环中异步执行的任务
        /// </summary>
        /// <param name="action">要执行的操作</param>
        /// <param name="updateInterval">更新间隔(秒),0 表示每帧执行</param>
        /// <param name="cts">取消令牌</param>
        /// <param name="onBegin">任务开始时的回调</param>
        /// <param name="onEnd">任务结束时的回调(无论正常结束还是取消都会调用)</param>
        public static void CreateFixedUpdateTask(Action action, float updateInterval, CancellationToken cts, Action onBegin = null, Action onEnd = null)
        {
            if (action == null) throw new ArgumentNullException(nameof(action));
            if (updateInterval < 0) throw new ArgumentOutOfRangeException(nameof(updateInterval), "更新间隔不能为负数");
            DealUpdateTask(action, Mathf.Max(0, updateInterval), DelayType.DeltaTime, PlayerLoopTiming.FixedUpdate, cts, onBegin, onEnd).Forget();
        }

        #endregion

        #region 核心循环调用执行方法
        private static async UniTaskVoid DealUpdateTask(Action action, float updateInterval, DelayType delayType, PlayerLoopTiming loopTiming, CancellationToken cts,
            Action onBegin = null, Action onEnd = null)
        {
            try
            {
                onBegin?.Invoke();
                if (updateInterval > 0)
                {
                    var interval = TimeSpan.FromSeconds(updateInterval);
                    while (!cts.IsCancellationRequested)
                    {
                        SafeInvokeAction(action);
                        await UniTask.Delay(interval, delayType, loopTiming, cts);
                    }
                }
                else
                {
                    while (!cts.IsCancellationRequested)
                    {
                        SafeInvokeAction(action);
                        await UniTask.Yield(loopTiming, cts);
                    }
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception ex) // 捕获其他所有异常
            {
                Ib_Log.Error(ex);
            }
            finally
            {
                onEnd?.Invoke(); // 确保 onEnd 总是被调用
            }
        }
        
        private static void SafeInvokeAction(Action action)
        {
            try
            {
                action?.Invoke();
            }
            catch (Exception ex)
            {
                Ib_Log.Error(ex);
            }
        }
        
        #endregion
    }
}



评论(1)

查看评论列表
评论头像
[…] Ib_EventAsync——异步事件中心:基于Unitask异步,实用性与可靠性暂未验证,包含同步事件的延迟一帧调用拓展,延迟调用基于Ib_Async的延迟方法。 […]

发表评论

表情 颜文字

插入代码