特点:基于协程封装的通用延迟调用方法。
性能:每次使用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)
暂无评论