boxmoe_header_banner_img

菜就多练喵

文章导读

[C#][Unity][Ib_Core]Ib_Coroutine——基于协程的延迟调用方法


avatar
Ib_Mccf 2026年3月24日 29

特点:基于协程封装的通用延迟调用方法。

性能:每次使用Ib_Coroutine延迟都会开启一次协程,协程开启时会产生GC。如需更佳的性能表现,可以尝试使用UniTask异步延迟。

示例:延迟1s打印信息。

继承Monobehaviour的类直接使用拓展方法this.DelayDoSomething开启协程实现延迟

不继承Monobehaviour的类需要使用Ib_Coroutine.DelayDoSthNoMono借助全局的协程启动器来实现延迟。

/// <summary>
/// 不继承MonoBehaviour的类
/// </summary>
public class Test_Ib_Coroutine
{
    public void Speak()
    {
        Ib_Coroutine.DelayDoSthNoMono(1,()=>Ib_Log.Info($"[{Time.time}]=>无Mono类,延迟1s触发"));
    }
}
public class Sample_Ib_Coroutine : MonoBehaviour
{
    void Start()
    {
        Ib_Log.Info($"[{Time.time}]=>你好");
        //继承MonoBehaviour的类直接靠自己启动协程
        this.DelayDoSomething(1,()=>Ib_Log.Info($"[{Time.time}]=>延迟1s触发"));
        //不继承MonoBehaviour的类需借助协程启动器的帮助
        new Test_Ib_Coroutine().Speak();
    }
}

注意:

继承MonoBehaviour的类使用this.DelayDoSomething延迟的时候,若期间被销毁,延迟调用也会被停止。

使用Ib_Coroutine.DelayDoSthNoMono不会在调用者被销毁时停止,需要注意此时的方法判空,避免报错。

中断延迟

Ib_Coroutine的延迟方法可以返回Coroutine,接收后可在延迟方法调用前提前停止调用。

/// <summary>
/// 不继承MonoBehaviour的类
/// </summary>
public class Test_Ib_Coroutine
{
    Coroutine _coroutine;
    public void Speak()
    {
        _coroutine = Ib_Coroutine.DelayDoSthNoMono(1,()=>Ib_Log.Info($"[{Time.time}]=>无Mono类,延迟1s触发"));
    }

    public void Stop()
    {
        Ib_Coroutine.StopCoroutineNoMono(_coroutine);
        Ib_Log.Info($"[{Time.time}]=>停止无Mono类的延迟调用");
    }
}
public class Sample_Ib_Coroutine : MonoBehaviour
{
    void Start()
    {
        Ib_Log.Info($"[{Time.time}]=>你好");
        var coroutine = this.DelayDoSomething(1,()=>Ib_Log.Info($"[{Time.time}]=>延迟1s触发"));
        //自己启动的协程,直接用StopCoroutine停止
        StopCoroutine(coroutine);
        Ib_Log.Info($"[{Time.time}]=>停止自己启动的延迟调用");

        var test = new Test_Ib_Coroutine();
        test.Speak();
        //中途停止延迟调用
        //DelayDoSthNoMono启动的协程需要用StopCoroutineNoMono停止
        test.Stop();
    }
}

性能:

每帧调用Ib_Coroutine延迟500次

System.Action _action;
void Update()
{
    //性能测试
    if(_action == null)_action = AddCoin;
    for (int i = 0; i < 500; i++)
    {
        this.DelayDoSomething(3, _action);
    }
}

结果:

耗时1.96ms,产生35.2KB的GC。

*需尽量避免大量的频繁调用 ,避免频繁GC造成的卡顿。


源码:

namespace Ib_Core.Coroutine
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public static class Ib_Coroutine
    {
        #region 缓存管理
        /// <summary>
        /// 缓存 WaitForSeconds 对象,以避免频繁创建和销毁
        /// </summary>
        private static readonly Dictionary<float,WaitForSeconds> WaitForSecondsCache = new Dictionary<float, WaitForSeconds>();
        private const int MaxCacheSize = 100;
        private const int ClearThreshold = 256;
        private static int ClearCnt = 0;
        private static WaitForSeconds GetWaitForSeconds(float delay)
        {
            //精度压缩,保留2位小数
            float key = Mathf.Round(delay * 100f) / 100f;

            int cachedCnt = WaitForSecondsCache.Count;
            //防止随机数填充字典
            if (cachedCnt > MaxCacheSize) 
            {
                if (++ClearCnt >= ClearThreshold)
                {
                    ClearCache();
                }
                return new WaitForSeconds(delay);
            }
            else
            {
                ClearCnt = 0;
            }
            
            WaitForSeconds wait;
            if (!WaitForSecondsCache.TryGetValue(key, out wait))
            {
                wait = new WaitForSeconds(delay);
                WaitForSecondsCache[key] = wait;
            }
            return wait;
        }

        /// <summary>
        /// 清理缓存
        /// </summary>
        public static void ClearCache()
        {
            ClearCnt = 0;
            WaitForSecondsCache.Clear();
        }
        #endregion
        
        #region 全局的协程启动器
        public class CoroutineRunner : MonoBehaviour 
        { 
            // 不需要写任何逻辑,只是作为一个载体
        }
        private static CoroutineRunner _runner;
        private const string RunnerName = "[Ib_CoroutineRunner]";
        private static CoroutineRunner Runner
        {
            get
            {
                if (_runner == null)
                {
                    GameObject go = GameObject.Find(RunnerName);
                    if(go!=null && go.TryGetComponent(out _runner)) return _runner;
                    go = new GameObject(RunnerName);
                    Object.DontDestroyOnLoad(go);
                    // 隐藏在层级面板中,防止被误删
                    go.hideFlags = HideFlags.HideAndDontSave;
                    _runner = go.AddComponent<CoroutineRunner>();
                }
                return _runner;
            }
        }

        // 暴露给无 Mono 类的接口
        public static UnityEngine.Coroutine Start(IEnumerator routine)
        {
            return Runner.StartCoroutine(routine);
        }
        #endregion
        
        #region 延迟核心方法处理
        private static IEnumerator DelayDoSomething(float delay, System.Action callback,bool useRealtime=false)
        {
            //WaitForSecondsRealtime不可缓存
            if(delay > 0)yield return useRealtime ? new WaitForSecondsRealtime(delay) : GetWaitForSeconds(delay);
            else yield return null;
            callback?.Invoke();
        }
        
        /// <summary>
        /// 在 Mono 行为中执行延迟操作
        /// </summary>
        /// <param name="mono">Mono 行为</param>
        /// <param name="delay">延迟时间</param>
        /// <param name="callback">回调函数</param>
        /// <param name="useRealtime">是否使用真实时间</param>
        public static Coroutine DelayDoSomething(this MonoBehaviour mono, float delay, System.Action callback,bool useRealtime=false)
        {
            return mono.StartCoroutine(DelayDoSomething(delay, callback,useRealtime));
        }
        /// <summary>
        /// 在没有 Mono 的情况下执行延迟操作
        /// </summary>
        /// <param name="delay">延迟时间</param>
        /// <param name="callback">回调函数</param>
        /// <param name="useRealtime">是否使用真实时间</param>
        public static Coroutine DelayDoSthNoMono(float delay, System.Action callback,bool useRealtime=false)
        {
            return Runner.StartCoroutine(DelayDoSomething(delay, callback,useRealtime));
        }
        /// <summary>
        /// 停止由DelayDoSthNoMono启动的协程
        /// </summary>
        /// <param name="coroutine"></param>
        public static void StopCoroutineNoMono(Coroutine coroutine)
        {
            Runner.StopCoroutine(coroutine);
        }
        /// <summary>
        /// 停止所有由DelayDoSthNoMono启动的协程
        /// </summary>
        public static void StopAllCoroutineNoMono()
        {
            Runner.StopAllCoroutines();
        }
        #endregion
    }
}


评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字

插入代码