在Unity项目中,计时器(Timer)是极其常见的需求——比如延迟执行一段逻辑、循环触发某个事件、生命周期绑定到某个对象等等。
今天要分享的,是我自己在项目中使用的一个轻量、好扩展、工程化水准较高的计时器管理系统 —— TimerManager。
这套系统主要解决以下问题:
-
替代
Invoke/Coroutine,提供更加灵活和可控的定时任务。 -
支持重复调用、无限循环、Transform绑定销毁。
-
简单易用,接口统一,一行代码创建/销毁计时器。
-
不引入额外开销,纯C#逻辑,无任何GC频繁生成问题。
核心设计理念
-
集中管理、统一更新
-
所有
Timer对象由TimerManager集中维护,在Update中统一检查并触发。 -
计时逻辑与主流程解耦。
-
-
灵活的回调支持
-
支持无参数回调(
TimerHandler)和带参数回调(TimerArgsHandler)。 -
满足简单触发和复杂逻辑需求,适合不同场景。
-
-
Transform关联
-
创建计时器时可指定一个
Transform作为挂钩。 -
如果目标
Transform销毁(比如GameObject被Destroy),可以手动调用Clear清理相关Timer,避免幽灵回调或空引用异常。
-
-
扩展方法友好
-
通过
TimerManagerExtensions,可以直接在任何Transform上调用.CreateTimer()、.CreateTimerWithArgs()、.ClearTimer(),体验自然流畅。
-
代码结构解析
1. TimerManager - 单例核心
public class SingletonMonoBase<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<T>();
// 如果在场景中找不到该类型的对象,则创建一个新对象
if (instance == null)
{
GameObject singletonObject = new GameObject(typeof(T).Name);
instance = singletonObject.AddComponent<T>();
}
}
return instance;
}
}
public bool destroyOnLoadScene;
protected virtual void Awake()
{
// 如果该对象不是单例对象,则销毁它
if (instance != null && instance != this)
{
Destroy(gameObject);
}
else
{
// 将当前对象赋值给单例对象
instance = this as T;
if (!destroyOnLoadScene)
// 确保该对象在场景切换时不被销毁
DontDestroyOnLoad(gameObject);
}
}
}
public class TimerManager : SingletonMonoBase<TimerManager>
2. 创建计时器
-
无参数的计时器:
public Timer CreateTimer(TimerHandler callBack, float time, int repeats = 1, Transform associatedTransform = null)
-
带参数的计时器:
public Timer CreateTimerWithArgs(TimerArgsHandler callBack, float time, int repeats, Transform associatedTransform = null, params object[] args)
底层统一走到Create()函数,新建一个Timer对象并添加到内部列表_timers中。
注意:这里传入的repeats如果小于0,意味着无限循环;如果大于0,就是指定次数调用。
3. 销毁计时器
public Timer DestroyTimer(Timer timer)
手动销毁某个Timer并清理资源。
public void Clear(Transform associatedTransform)
批量清理与某个Transform绑定的所有Timer,防止引用悬挂。
4. 驱动逻辑 - GameUpdate
每一帧(或固定频率),执行Update():
-
遍历当前所有Timer。
-
检查当前时间是否超过了Timer设定的触发时间。
-
触发回调,并根据重复次数决定是否销毁。
特别注意:
if (timer.Repeats-- == 0)
{
DestroyTimer(timer);
}
先执行回调,再减少Repeats,保证逻辑稳定性。
扩展用法 —— Transform的甜蜜糖
为了让使用体验更好,我封装了扩展方法:
例如,可以这样优雅地直接在一个Transform上使用:
transform.CreateTimer(() => { Debug.Log("触发了定时任务"); }, 2.0f, 3);
或者带参数:
transform.CreateTimerWithArgs((args) => { Debug.Log($"收到参数: {args[0]}"); }, 1.0f, 1, "Hello");
最后如果这个Transform销毁了,只需要:
transform.ClearTimer();
再也不怕回调到空对象引发Bug。
最后献上完整代码
using Reacool.Core.Update;
using System.Collections.Generic;
using UnityEngine;
namespace Reacool.Core.Timer
{
public class TimerManager : SingletonMonoBase<TimerManager>
{
private List<Timer> _timers = new List<Timer>(); //时间管理器
/// <summary>
/// 创建一个简单的计时器
/// </summary>
/// <param name="callBack">回调函数</param>
/// <param name="time">计时器时间</param>
/// <param name="repeats">回调次数 小于0代表循环 大于0代表repeats次</param>
public Timer CreateTimer(TimerHandler callBack, float time, int repeats = 1, Transform associatedTransform = null)
{
return Create(callBack, null, time, repeats, associatedTransform);
}
public Timer CreateTimerWithArgs(TimerArgsHandler callBack, float time, int repeats, Transform associatedTransform = null, params System.Object[] args)
{
return Create(null, callBack, time, repeats, associatedTransform, args);
}
private Timer Create(TimerHandler callBack, TimerArgsHandler callBackArgs, float time, int repeats, Transform associatedTransform, params System.Object[] args)
{
Timer timer = new Timer(callBack, callBackArgs, time, repeats, args, associatedTransform);
_timers.Add(timer);
return timer;
}
public Timer DestroyTimer(Timer timer)
{
if (timer != null)
{
_timers.Remove(timer);
timer.CleanUp();
timer = null;
}
return timer;
}
public void ClearAll()
{
if (_timers != null)
{
for (int i = 0; i < _timers.Count; i++)
{
_timers[i].CleanUp();
}
_timers.Clear();
}
}
/// <summary>
/// 清理与指定Transform关联的所有计时器
/// </summary>
public void Clear(Transform associatedTransform)
{
for (int i = _timers.Count - 1; i >= 0; i--)
{
if (_timers[i].AssociatedTransform == associatedTransform)
{
DestroyTimer(_timers[i]);
}
}
}
/// <summary>
/// 固定更新检查更新的频率
/// </summary>
public void Update()
{
if (_timers != null && _timers.Count != 0)
{
for (int i = _timers.Count - 1; i >= 0; i--)
{
if (i > _timers.Count - 1)
{
return;
}
Timer timer = _timers[i];
float curTime = Time.time;
if (timer.Frequency + timer.LastTickTime > curTime)
{
continue;
}
timer.LastTickTime = curTime;
if (timer.Repeats-- == 0)
{
//计时完成,可以删除了
DestroyTimer(timer);
}
else
{
//触发计时
timer.Notify();
}
}
}
}
}
public static class TimerManagerExtensions
{
/// <summary>
/// 直接在 Transform 上创建一个计时器,自动将当前 Transform 作为关联对象传入。无参回调!
/// </summary>
public static Timer CreateTimer(this Transform transform, TimerHandler callback, float time, int repeats = 1)
{
return TimerManager.Instance.CreateTimer(callback, time, repeats, transform);
}
/// <summary>
/// 直接在 Transform 上创建一个带参数的计时器,自动将当前 Transform 作为关联对象传入
/// </summary>
public static Timer CreateTimerWithArgs(this Transform transform, TimerArgsHandler callback, float time, int repeats, params object[] args)
{
return TimerManager.Instance.CreateTimerWithArgs(callback, time, repeats, transform, args);
}
/// <summary>
/// 直接在 Transform 上调用,清理与当前 Transform 关联的所有计时器。有参回调!
/// </summary>
public static void ClearTimer(this Transform transform)
{
if (transform == null)
{
Debug.LogWarning("ClearTimer: 尝试对一个 null 的 Transform 清理计时器。");
return;
}
TimerManager.Instance.Clear(transform);
}
}
}
using System;
using UnityEngine;
namespace Reacool.Core.Timer
{
public delegate void TimerHandler();
public delegate void TimerArgsHandler(System.Object[] args);
public class Timer
{
public TimerHandler Handler; //无参的委托
public TimerArgsHandler ArgsHandler; //带参数的委托
public float Frequency; //时间间隔
public int Repeats; //重复次数
public System.Object[] Args;
public float LastTickTime;
public Transform AssociatedTransform; // 关联的Transform
public event Action OnComplete; //计时器完成一次工作
public event Action OnDestroy; //计时器被销毁
public Timer() { }
/// <summary>
/// 创建一个时间事件对象
/// </summary>
/// <param name="Handler">回调函数</param>
/// <param name="ArgsHandler">带参数的回调函数</param>
/// <param name="frequency">时间内执行</param>
/// <param name="repeats">重复次数</param>
/// <param name="Args">参数 可以任意的传不定数量,类型的参数</param>
public Timer(TimerHandler Handler, TimerArgsHandler ArgsHandler, float frequency, int repeats, System.Object[] Args, Transform associatedTransform)
{
this.Handler = Handler;
this.ArgsHandler = ArgsHandler;
this.Frequency = frequency;
this.Repeats = repeats == 0 ? 1 : repeats;
this.Args = Args;
this.LastTickTime = Time.time;
this.AssociatedTransform = associatedTransform; // 赋值
}
public void Notify()
{
if (Handler != null)
Handler();
if (ArgsHandler != null)
ArgsHandler(Args);
if (OnComplete != null)
{
OnComplete();
}
}
/// <summary>
/// 清理计时器,初始化参数 同时清理事件
/// </summary>
public void CleanUp()
{
Handler = null;
ArgsHandler = null;
Repeats = 1;
Frequency = 0;
if (OnDestroy != null)
{
OnDestroy();
}
OnDestroy = null;
OnComplete = null;
}
}
}
16万+

被折叠的 条评论
为什么被折叠?



