简易拆开即用的高性能计时器(C#)

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);
}
  1. 目标绑定自动清理
csharp 复制代码
// 自动清理:目标销毁时计时器自动取消
TimerTask.Instance.Delay(5f, () => {
    Debug.Log("这段代码在目标销毁时不会执行");
}).SetTarget(transform);  // 关键:绑定Transform
  1. 批量清理
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();
    }
}
  1. 忽略时间缩放
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);
    }
}
  1. 条件计时器
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();
            }
        });
    }
}
相关推荐
yuguo.im1 小时前
91 行代码实现一个打飞机游戏(HTML5 Canvas 版)
前端·游戏·html5·打飞机
从文处安2 小时前
「前端何去何从」一直写 Vue ,为何要在 AI 时代去学 React「2」?
前端·react.js
掘金者阿豪2 小时前
深入解读OpenClaw配置文件:一个现代化AI网关的全景洞察
前端
葡萄城技术团队2 小时前
Playwright 官方推荐的 Fixture 模式,为什么大厂架构师却在偷偷弃用?
前端
newbe365242 小时前
ImgBin CLI 工具设计:HagiCode 图片资产管理方案
前端·后端
bluceli2 小时前
CSS Scroll Snap:打造丝滑滚动体验的利器
前端·css
www_stdio2 小时前
深入理解 React Fiber 与浏览器事件循环:从性能瓶颈到调度机制
前端·react.js·面试
工边页字2 小时前
LLM 系统设计核心:为什么必须压缩上下文?有哪些工程策略
前端·人工智能·后端
嚣张丶小麦兜2 小时前
react的理解
前端·react.js·前端框架