简易拆开即用的高性能计时器(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();
            }
        });
    }
}
相关推荐
格子软件7 小时前
2026年GEO优化系统源码级状态机与多模型调度拆解
java·前端·vue.js·人工智能·vue·geo
HUMHSX8 小时前
Vue 项目启动全流程解析:从入口文件到全局指令注册与页面渲染
前端·javascript·vue.js
有颜有货8 小时前
PMC生产排产的4种算法,一次讲清
java·服务器·前端
小虎牙0078 小时前
Android kotlin图片库Coil源码详解
android·前端
随风一样自由8 小时前
【前端领域】前端开发核心应用场景与落地实践
前端·前端框架
an317429 小时前
弹窗数据流设计的两种高阶架构实践
前端·vue.js·架构
谢尔登9 小时前
【React】 状态管理方案
前端·react.js·前端框架
WarPigs9 小时前
AB包自定义打包工具
unity
用户2136610035729 小时前
Vue商品详情与放大镜组件
前端·javascript
半个落月9 小时前
从Tapas小Demo理清localStorage、事件与this
前端·javascript