csharp
using System;
using System.Collections;
using Architecture.Unit;
using UnityEngine;
/// <summary>
/// 计时器接口,提供统一的外部控制方法
/// </summary>
public interface ITimer
{
float Duration { get; }
float Elapsed { get; }
bool IsCompleted { get; }
void Cancel();
void Pause();
void Resume();
ITimer SetLoop(bool loop);
ITimer SetTarget(Transform target);
ITimer SetIgnoreTimeScale(bool ignoreTimeScale);
ITimer OnComplete(Action onComplete);
ITimer OnUpdate(Action<float> onUpdate);
}
/// <summary>
/// 基于时间的计时器实现
/// </summary>
public sealed class Timer : ITimer, IComparable<Timer>
{
// 公开属性
public float Duration => _duration;
public float Elapsed => CalculateElapsed();
public bool IsCompleted => IsCancelled || (!IsLoop && Elapsed >= Duration);
// 配置属性
public bool IsLoop { get; private set; }
public bool IsCancelled { get; private set; }
public bool IsPaused { get; private set; }
public bool IgnoreTimeScale { get; private set; }
public Transform Target { get; private set; }
// 回调
public Action OnCompleteCallback { get; set; }
public Action<float> OnUpdateCallback { get; set; }
// 内部状态
private float _startTime;
private float _pausedTime;
private float _duration;
private TimerTask _manager;
// 用于排序
public float ExecuteTime { get; set; }
public Timer(TimerTask manager)
{
_manager = manager;
}
internal void Init(float delay, float duration, bool isLoop)
{
_duration = duration;
IsLoop = isLoop;
IsCancelled = false;
IsPaused = false;
IgnoreTimeScale = false;
float time = GetTime();
_startTime = time;
ExecuteTime = time + delay;
}
public void Cancel()
{
if (IsCancelled) return;
IsCancelled = true; // 仅设置标志,实际移除由管理器处理
}
public void Pause()
{
if (IsPaused || IsCancelled) return;
IsPaused = true;
_pausedTime = GetTime();
}
public void Resume()
{
if (!IsPaused || IsCancelled) return;
float currentTime = GetTime();
float pauseDuration = currentTime - _pausedTime;
_startTime += pauseDuration;
ExecuteTime += pauseDuration;
IsPaused = false;
}
public ITimer SetLoop(bool loop)
{
IsLoop = loop;
return this;
}
public ITimer SetTarget(Transform target)
{
if (Target == target) return this;
_manager?.UnregisterTargetTimer(Target, this);
Target = target;
_manager?.RegisterTargetTimer(target, this);
return this;
}
public ITimer SetIgnoreTimeScale(bool ignoreTimeScale)
{
IgnoreTimeScale = ignoreTimeScale;
return this;
}
public ITimer OnComplete(Action onComplete)
{
OnCompleteCallback = onComplete;
return this;
}
public ITimer OnUpdate(Action<float> onUpdate)
{
OnUpdateCallback = onUpdate;
return this;
}
/// <summary>
/// 更新计时器状态,返回true表示应移除
/// </summary>
internal bool Tick(float currentTime)
{
if (IsCancelled || IsPaused) return false;
// 检查目标是否有效
if (Target != null && Target.gameObject == null) // 注意:Target可能已被销毁但未置null,用gameObject判断
{
Cancel();
return true;
}
// 调用更新回调
if (OnUpdateCallback != null)
{
float progress = _duration > 0 ? Mathf.Clamp01((currentTime - _startTime) / _duration) : 0f;
try
{
OnUpdateCallback(progress);
}
catch (Exception e)
{
Debug.LogError($"计时器更新回调出错: {e.Message}\n{e.StackTrace}");
}
}
// 检查是否到达执行时间
if (currentTime >= ExecuteTime)
{
try
{
OnCompleteCallback?.Invoke();
}
catch (Exception e)
{
Debug.LogError($"计时器完成回调出错: {e.Message}\n{e.StackTrace}");
}
if (IsLoop && !IsCancelled)
{
// 循环计时器:重置时间
_startTime = currentTime;
ExecuteTime = currentTime + _duration;
return false; // 不移除
}
else
{
// 非循环或已取消:移除
Cancel();
return true;
}
}
return false;
}
public void CleanUp()
{
OnCompleteCallback = null;
OnUpdateCallback = null;
Target = null;
// 不重置 IsCancelled,因为回收后可能重用
}
private float CalculateElapsed()
{
if (IsCancelled) return Duration;
if (IsPaused) return _pausedTime - _startTime;
return GetTime() - _startTime;
}
private float GetTime()
{
return IgnoreTimeScale ? Time.unscaledTime : Time.time;
}
public int CompareTo(Timer other)
{
return ExecuteTime.CompareTo(other.ExecuteTime);
}
}
/// <summary>
/// 帧计时器包装器(基于协程)
/// </summary>
public sealed class FrameTimer : ITimer
{
private readonly TimerTask _manager;
private readonly MonoBehaviour _coroutineRunner;
private Coroutine _coroutine;
private bool _isCancelled;
private bool _isPaused;
private bool _isActive; // 协程是否正在运行
private Transform _target;
private Action _onCompleteCallback;
private Action<float> _onUpdateCallback;
// 对于帧计时器,这些属性返回默认值
public float Duration => 0f;
public float Elapsed => 0f;
public bool IsCompleted => !_isActive; // 协程结束即完成
public bool IsActive => _isActive;
public FrameTimer(TimerTask manager, MonoBehaviour coroutineRunner)
{
_manager = manager;
_coroutineRunner = coroutineRunner;
}
public void Cancel()
{
if (_isCancelled) return;
_isCancelled = true;
_isActive = false;
if (_coroutine != null)
{
_coroutineRunner.StopCoroutine(_coroutine);
_coroutine = null;
}
CleanUp();
}
public void Pause()
{
_isPaused = true;
}
public void Resume()
{
_isPaused = false;
}
public ITimer SetLoop(bool loop)
{
// 帧计时器不支持循环,通过重复计数实现
return this;
}
public ITimer SetTarget(Transform target)
{
_target = target;
return this;
}
public ITimer SetIgnoreTimeScale(bool ignoreTimeScale)
{
// 帧计时器总是忽略时间缩放
return this;
}
public ITimer OnComplete(Action onComplete)
{
_onCompleteCallback = onComplete;
return this;
}
public ITimer OnUpdate(Action<float> onUpdate)
{
_onUpdateCallback = onUpdate;
return this;
}
/// <summary>
/// 开始帧延迟(不接收callback,使用OnComplete注册)
/// </summary>
public void StartDelayFrames(int frames)
{
Cancel(); // 停止之前的协程
_isCancelled = false;
_isActive = true;
_coroutine = _coroutineRunner.StartCoroutine(DelayFramesCoroutine(frames));
}
/// <summary>
/// 开始帧间隔
/// </summary>
public void StartIntervalFrames(int frameInterval, Action callback, int repeatCount = -1)
{
Cancel();
_isCancelled = false;
_isActive = true;
// 将外部回调通过OnComplete注册,内部协程只调用_onCompleteCallback
OnComplete(callback);
_coroutine = _coroutineRunner.StartCoroutine(IntervalFramesCoroutine(frameInterval, repeatCount));
}
private IEnumerator DelayFramesCoroutine(int frames)
{
int framesElapsed = 0;
while (!_isCancelled && framesElapsed < frames)
{
if (!_isPaused)
{
framesElapsed++;
if (_onUpdateCallback != null)
{
try
{
float progress = Mathf.Clamp01((float)framesElapsed / frames);
_onUpdateCallback(progress);
}
catch (Exception e)
{
Debug.LogError($"帧计时器更新回调出错: {e.Message}\n{e.StackTrace}");
}
}
if (_target != null && _target.gameObject == null)
{
Cancel();
yield break;
}
}
yield return null;
}
if (!_isCancelled)
{
try
{
_onCompleteCallback?.Invoke();
}
catch (Exception e)
{
Debug.LogError($"帧计时器完成回调出错: {e.Message}\n{e.StackTrace}");
}
}
_isActive = false;
CleanUp();
}
private IEnumerator IntervalFramesCoroutine(int frameInterval, int repeatCount)
{
int framesElapsed = 0;
int executions = 0;
bool isInfinite = repeatCount < 0;
while (!_isCancelled && (isInfinite || executions < repeatCount))
{
if (!_isPaused)
{
framesElapsed++;
if (_onUpdateCallback != null)
{
try
{
float progress = isInfinite ? 0f : (float)executions / repeatCount;
_onUpdateCallback(progress);
}
catch (Exception e)
{
Debug.LogError($"帧计时器更新回调出错: {e.Message}\n{e.StackTrace}");
}
}
if (_target != null && _target.gameObject == null)
{
Cancel();
yield break;
}
if (framesElapsed >= frameInterval)
{
try
{
_onCompleteCallback?.Invoke(); // 注意:这里会触发回调
}
catch (Exception e)
{
Debug.LogError($"帧计时器间隔回调出错: {e.Message}\n{e.StackTrace}");
}
framesElapsed = 0;
executions++;
}
}
yield return null;
}
if (!_isCancelled)
{
// 不额外调用_onCompleteCallback,因为每次间隔已经调用过了
// 如果需要在所有间隔完成后调用一次,可以再调用一次,但一般不需要
}
_isActive = false;
CleanUp();
}
public void CleanUp()
{
_onCompleteCallback = null;
_onUpdateCallback = null;
_target = null;
_coroutine = null;
_isCancelled = true; // 确保不再运行
}
}
csharp
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Architecture.Unit
{
/// <summary>
/// 计时器管理器,使用单例模式
/// </summary>
public sealed class TimerTask
{
// 活跃的时间计时器列表(按执行时间排序)
private readonly List<Timer> _activeTimers = new List<Timer>();
// 需要重新排序的计时器(循环计时器触发后)
private readonly HashSet<Timer> _timersToReorder = new HashSet<Timer>();
// 对象池,减少GC
private readonly Stack<Timer> _timerPool = new Stack<Timer>();
private const int MaxPoolSize = 100;
// 待添加队列,避免遍历时修改集合
private readonly Queue<Timer> _timersToAdd = new Queue<Timer>();
// 按目标分组的计时器
private readonly Dictionary<Transform, HashSet<Timer>> _targetTimers =
new Dictionary<Transform, HashSet<Timer>>();
// 帧计时器列表
private readonly List<FrameTimer> _frameTimers = new List<FrameTimer>();
private readonly Stack<FrameTimer> _frameTimerPool = new Stack<FrameTimer>();
// 单例实例
public static TimerTask Instance { get; private set; }
private bool _isQuitting;
private TimerUpdater _updater;
#region 私有方法
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
if (Instance != null) return;
Instance = new TimerTask();
GameObject managerObject = new GameObject("TimerManager");
var updater = managerObject.AddComponent<TimerUpdater>();
Instance._updater = updater;
UnityEngine.Object.DontDestroyOnLoad(managerObject);
Application.quitting += Instance.OnApplicationQuit;
}
private void OnApplicationQuit()
{
_isQuitting = true;
ClearAll();
}
private class TimerUpdater : MonoBehaviour
{
private float _lastUpdateTime;
private float _lastUnscaledTime;
private void Start()
{
_lastUpdateTime = Time.time;
_lastUnscaledTime = Time.unscaledTime;
}
private void Update()
{
if (Instance._isQuitting) return;
float currentTime = Time.time;
float currentUnscaledTime = Time.unscaledTime;
float deltaTime = currentTime - _lastUpdateTime;
float unscaledDeltaTime = currentUnscaledTime - _lastUnscaledTime;
_lastUpdateTime = currentTime;
_lastUnscaledTime = currentUnscaledTime;
Instance.UpdateTimers(deltaTime, unscaledDeltaTime);
}
private void OnDestroy()
{
if (!Instance._isQuitting)
Instance.ClearAll();
}
#if UNITY_EDITOR
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
Instance.PauseAllTimers();
else
Instance.ResumeAllTimers();
}
#endif
}
private static readonly Comparison<Timer> TimerComparison = (a, b) => a.ExecuteTime.CompareTo(b.ExecuteTime);
private void UpdateTimers(float deltaTime, float unscaledDeltaTime)
{
// 1. 添加新计时器
while (_timersToAdd.Count > 0)
{
Timer timer = _timersToAdd.Dequeue();
if (!timer.IsCancelled)
{
_activeTimers.Add(timer);
}
}
// 2. 按执行时间排序(新加入的计时器需要排序)
_activeTimers.Sort(TimerComparison);
// 3. 处理时间计时器
ProcessTimers(Time.time, Time.unscaledTime);
// 4. 清理已完成的帧计时器
CleanupFrameTimers();
}
private void ProcessTimers(float time, float unscaledTime)
{
List<Timer> toRemove = new List<Timer>();
for (int i = 0; i < _activeTimers.Count; i++)
{
Timer timer = _activeTimers[i];
// 跳过已取消的计时器(等待移除)
if (timer.IsCancelled)
{
toRemove.Add(timer);
continue;
}
if (timer.IsPaused)
continue;
float currentTime = timer.IgnoreTimeScale ? unscaledTime : time;
// 如果执行时间大于当前时间,后面的计时器都更大,可以提前退出
if (timer.ExecuteTime > currentTime)
break;
// 记录旧执行时间,用于判断是否需要重排
float oldExecuteTime = timer.ExecuteTime;
// 调用Tick,返回true表示应移除
bool shouldRemove = timer.Tick(currentTime);
if (shouldRemove)
{
toRemove.Add(timer);
}
else
{
// 如果是循环计时器且执行时间已更新(即刚触发过),需要重排
if (timer.IsLoop && Math.Abs(timer.ExecuteTime - oldExecuteTime) > 1e-6)
{
_timersToReorder.Add(timer);
}
}
}
// 移除并回收已完成的计时器
foreach (Timer timer in toRemove)
{
_activeTimers.Remove(timer);
RecycleTimer(timer);
}
// 重新排序需要重排的计时器
if (_timersToReorder.Count > 0)
{
_activeTimers.Sort(TimerComparison);
_timersToReorder.Clear();
}
}
private void CleanupFrameTimers()
{
for (int i = _frameTimers.Count - 1; i >= 0; i--)
{
FrameTimer timer = _frameTimers[i];
if (!timer.IsActive)
{
_frameTimers.RemoveAt(i);
RecycleFrameTimer(timer);
}
}
}
/// <summary>
/// 创建计时器(内部使用)
/// </summary>
private Timer CreateTimer(float delay, float duration, bool loop)
{
if (delay < 0) delay = 0;
if (duration < 0) duration = 0;
Timer timer = GetTimerFromPool();
timer.Init(delay, duration, loop);
_timersToAdd.Enqueue(timer);
return timer;
}
private Timer GetTimerFromPool()
{
if (_timerPool.Count > 0)
{
Timer timer = _timerPool.Pop();
// 确保从所有集合中清除(理论上池中的计时器已清理干净)
return timer;
}
return new Timer(this);
}
private FrameTimer GetFrameTimerFromPool()
{
if (_frameTimerPool.Count > 0)
{
return _frameTimerPool.Pop();
}
return new FrameTimer(this, _updater);
}
private ITimer GetDummyTimer()
{
// 返回一个什么都不做的虚拟计时器
return new FrameTimer(this, _updater);
}
internal void RecycleTimer(Timer timer)
{
timer.CleanUp();
if (_timerPool.Count < MaxPoolSize)
_timerPool.Push(timer);
}
private void RecycleFrameTimer(FrameTimer timer)
{
timer.CleanUp();
if (_frameTimerPool.Count < MaxPoolSize)
_frameTimerPool.Push(timer);
}
internal void RegisterTargetTimer(Transform target, Timer timer)
{
if (target == null) return;
if (!_targetTimers.TryGetValue(target, out HashSet<Timer> timers))
{
timers = new HashSet<Timer>();
_targetTimers[target] = timers;
}
timers.Add(timer);
}
internal void UnregisterTargetTimer(Transform target, Timer timer)
{
if (target == null) return;
if (_targetTimers.TryGetValue(target, out HashSet<Timer> timers))
{
timers.Remove(timer);
if (timers.Count == 0)
_targetTimers.Remove(target);
}
}
#endregion
#region 公开API
/// <summary>
/// 添加时间延迟计时器
/// </summary>
public ITimer Delay(float delay, Action callback)
{
return CreateTimer(delay, delay, false).OnComplete(callback);
}
/// <summary>
/// 添加间隔执行计时器
/// </summary>
/// <param name="interval">间隔时间</param>
/// <param name="callback">回调</param>
/// <param name="repeatCount">重复次数,-1表示无限循环</param>
public ITimer Interval(float interval, Action callback, int repeatCount = -1)
{
// 总是创建循环计时器
var timer = CreateTimer(interval, interval, true);
if (repeatCount >= 0)
{
int count = 0;
timer.OnComplete(() =>
{
callback();
count++;
if (count >= repeatCount)
timer.Cancel();
});
}
else
{
timer.OnComplete(callback);
}
return timer;
}
/// <summary>
/// 添加帧延迟计时器
/// </summary>
public ITimer DelayFrames(int frames, Action callback)
{
if (frames <= 0)
{
callback?.Invoke();
return GetDummyTimer();
}
var frameTimer = GetFrameTimerFromPool();
frameTimer.OnComplete(callback);
frameTimer.StartDelayFrames(frames);
_frameTimers.Add(frameTimer);
return frameTimer;
}
/// <summary>
/// 添加帧间隔计时器
/// </summary>
public ITimer IntervalFrames(int frameInterval, Action callback, int repeatCount = -1)
{
if (frameInterval <= 0)
{
Debug.LogWarning("帧间隔必须大于0");
return GetDummyTimer();
}
var frameTimer = GetFrameTimerFromPool();
frameTimer.StartIntervalFrames(frameInterval, callback, repeatCount);
_frameTimers.Add(frameTimer);
return frameTimer;
}
/// <summary>
/// 清除所有计时器
/// </summary>
public void ClearAll()
{
// 取消所有时间计时器
foreach (Timer timer in _activeTimers)
timer.Cancel();
_activeTimers.Clear();
_timersToReorder.Clear();
// 取消所有帧计时器
foreach (FrameTimer timer in _frameTimers)
timer.Cancel();
_frameTimers.Clear();
_timersToAdd.Clear();
_timerPool.Clear();
_frameTimerPool.Clear();
_targetTimers.Clear();
}
/// <summary>
/// 清除指定目标的所有计时器
/// </summary>
public void ClearTimersWithTarget(Transform target)
{
if (target == null || !_targetTimers.TryGetValue(target, out HashSet<Timer> timers))
return;
foreach (Timer timer in new List<Timer>(timers))
timer.Cancel();
_targetTimers.Remove(target);
}
/// <summary>
/// 暂停所有计时器
/// </summary>
public void PauseAllTimers()
{
foreach (Timer timer in _activeTimers)
timer.Pause();
foreach (FrameTimer timer in _frameTimers)
timer.Pause();
}
/// <summary>
/// 恢复所有计时器
/// </summary>
public void ResumeAllTimers()
{
foreach (Timer timer in _activeTimers)
timer.Resume();
foreach (FrameTimer timer in _frameTimers)
timer.Resume();
}
#endregion
}
}
TimerTask是一个基于C#的高性能Unity计时器管理器,采用单例模式设计,支持时间和帧两种计时方式,具备自动内存管理、异常安全、目标绑定等高级特性。
🎯 核心特性
✅ 双模式计时:支持时间(秒)和帧两种计时单位
✅ 自动生命周期管理:目标对象销毁时自动取消关联计时器
✅ 异常安全机制:回调异常不影响系统运行
✅ 对象池优化:减少GC,提升性能
✅ 完整控制接口:暂停、恢复、取消、进度回调
✅ 编辑器友好:支持Unity编辑器暂停/恢复
基本使用示例:
csharp
using Architecture.Unit;
using UnityEngine;
public class TimerExample : MonoBehaviour
{
private void Start()
{
// 1. 延迟执行
TimerTask.Instance.Delay(2f, () => {
Debug.Log("2秒后执行");
});
// 2. 循环执行(每秒一次)
TimerTask.Instance.Interval(1f, () => {
Debug.Log("每秒执行一次");
});
// 3. 帧延迟
TimerTask.Instance.DelayFrames(5, () => {
Debug.Log("5帧后执行");
});
// 4. 链式调用
var timer = TimerTask.Instance.Delay(3f, () => {
Debug.Log("计时器完成");
})
.SetTarget(transform) // 绑定到当前对象
.SetIgnoreTimeScale(true) // 忽略时间缩放
.OnUpdate((progress) => { // 进度回调
Debug.Log($"进度: {progress:P0}");
});
// 随时控制
timer.Pause(); // 暂停
timer.Resume(); // 恢复
timer.Cancel(); // 取消
}
}
使用建议
1.对象池机制利用
csharp
// 推荐:重用计时器引用
private ITimer _reusableTimer;
void StartPeriodicTask()
{
if (_reusableTimer != null && !_reusableTimer.IsCompleted)
_reusableTimer.Cancel();
_reusableTimer = TimerTask.Instance.Interval(1f, DoTask);
}
- 目标绑定自动清理
csharp
// 自动清理:目标销毁时计时器自动取消
TimerTask.Instance.Delay(5f, () => {
Debug.Log("这段代码在目标销毁时不会执行");
}).SetTarget(transform); // 关键:绑定Transform
- 批量清理
csharp
public class GameManager : MonoBehaviour
{
private List<ITimer> _gameTimers = new();
private void AddGameTimer(float delay, Action callback)
{
var timer = TimerTask.Instance.Delay(delay, callback)
.SetTarget(transform);
_gameTimers.Add(timer);
}
private void OnSceneUnload()
{
// 批量清理所有游戏相关计时器
foreach(var timer in _gameTimers)
timer?.Cancel();
_gameTimers.Clear();
}
}
- 忽略时间缩放
csharp
// 适用于UI动画、暂停菜单等
public class PauseMenu : MonoBehaviour
{
public void ShowMenu()
{
Time.timeScale = 0f; // 暂停游戏
// 菜单动画仍正常播放
TimerTask.Instance.Delay(0.5f, () => {
GetComponent<Animator>().SetTrigger("Show");
}).SetIgnoreTimeScale(true); // 忽略时间缩放
}
}
2.进度回调实现
csharp
public class ProgressBar : MonoBehaviour
{
[SerializeField] private Image fillImage;
public void StartProgress(float duration)
{
TimerTask.Instance.Delay(duration, () => {
Debug.Log("进度完成");
}).OnUpdate((progress) => {
fillImage.fillAmount = progress; // 实时更新进度条
}).SetTarget(transform);
}
}
- 条件计时器
csharp
public class ConditionalTimer
{
private ITimer _timer;
public void StartWithCondition(float delay, Action callback, Func<bool> condition)
{
_timer = TimerTask.Instance.Interval(0.1f, () => {
if (condition())
{
callback?.Invoke();
_timer.Cancel();
}
}).SetLoop(true);
// 设置超时
TimerTask.Instance.Delay(delay, () => {
if (!_timer.IsCompleted)
{
Debug.Log("条件计时器超时");
_timer.Cancel();
}
});
}
}