using System; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; namespace Core { /// /// 通用计时器(正计时 / 倒计时均可) /// 全程 UniTask,无 GC,支持暂停、继续、提前终止 /// public sealed class UniTimer { public float Duration { get; private set; } public float Elapsed { get; private set; } public float Remaining => Mathf.Max(0, Duration - Elapsed); public bool IsRunning { get; private set; } public bool IsPaused { get; private set; } /// 每帧回调:参数为当前已走时间 public event Action OnTick; /// 计时结束回调 public event Action OnComplete; private CancellationTokenSource _cts; private UniTaskCompletionSource _taskSource; /*------------------------------------------------------*/ /// /// 开始计时 /// /// 时长(秒) /// true=倒计时;false=正计时 /// 可选:每帧刷新 /// 可选:结束回调 public UniTask StartAsync(float duration, bool isCountDown = false, Action onTick = null, Action onComplete = null) { Stop(); // 先清理旧任务 _taskSource = new UniTaskCompletionSource(); // 新建 Duration = duration; Elapsed = isCountDown ? duration : 0f; OnTick = onTick; OnComplete = onComplete; IsRunning = true; IsPaused = false; _cts = new CancellationTokenSource(); return RunAsync(_cts.Token, isCountDown); } /// 立即终止(不可恢复) public void Stop() { if (!IsRunning) return; _cts?.Cancel(); _cts?.Dispose(); _cts = null; IsRunning = false; _taskSource?.TrySetCanceled(); // 提前终止也通知 IsPaused = false; } /// 暂停 public void Pause() => IsPaused = true; /// 继续 public void Resume() => IsPaused = false; /*------------------------------------------------------*/ private async UniTask RunAsync(System.Threading.CancellationToken token, bool countDown) { while (!token.IsCancellationRequested) { if (!IsPaused) { float dt = Time.deltaTime; Elapsed += countDown ? -dt : dt; // 触发回调 OnTick?.Invoke(Elapsed); // 结束条件 if (countDown ? Elapsed <= 0f : Elapsed >= Duration) { Elapsed = countDown ? 0f : Duration; OnTick?.Invoke(Elapsed); // 最后再给一次精确值 break; } } await UniTask.Yield(PlayerLoopTiming.Update, token); } IsRunning = false; _taskSource?.TrySetResult(true); // 广播“结束” if (!token.IsCancellationRequested) OnComplete?.Invoke(); } public UniTask WaitForFinishAsync() => _taskSource?.Task ?? UniTask.CompletedTask; } }