623 lines
17 KiB
C#
623 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityGameFramework.Runtime;
|
|
|
|
namespace SepCore.Timer
|
|
{
|
|
/// <summary>
|
|
/// 通用定时器组件。
|
|
/// </summary>
|
|
[DisallowMultipleComponent]
|
|
[AddComponentMenu("Game Main/Timer")]
|
|
public sealed class TimerComponent : GameFrameworkComponent
|
|
{
|
|
private readonly List<TimerTaskInfo> _timerTasks = new List<TimerTaskInfo>();
|
|
private bool _isPaused = false;
|
|
private bool _isUpdating = false;
|
|
private int _serialId = 0;
|
|
|
|
/// <summary>
|
|
/// 获取当前定时任务数量。
|
|
/// </summary>
|
|
public int Count => _timerTasks.Count;
|
|
|
|
/// <summary>
|
|
/// 获取定时器是否暂停。
|
|
/// </summary>
|
|
public bool IsPaused => _isPaused;
|
|
|
|
private void Update()
|
|
{
|
|
if (_isPaused)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateTasks(Time.deltaTime, Time.unscaledDeltaTime);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
ClearAll();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建定时任务。
|
|
/// </summary>
|
|
/// <param name="timerTask">定时任务描述。</param>
|
|
/// <returns>定时任务句柄。</returns>
|
|
public TimerHandle Schedule(TimerTask timerTask)
|
|
{
|
|
if (timerTask.Callback == null)
|
|
{
|
|
Log.Error("Timer task callback is invalid.");
|
|
return TimerHandle.Invalid;
|
|
}
|
|
|
|
if (timerTask.RepeatCount == 0)
|
|
{
|
|
return TimerHandle.Invalid;
|
|
}
|
|
|
|
float delay = Mathf.Max(0f, timerTask.Delay);
|
|
float interval = Mathf.Max(0f, timerTask.Interval);
|
|
if (timerTask.RepeatCount != 1 && interval <= 0f)
|
|
{
|
|
Log.Error("Timer task interval must be greater than zero for repeated tasks.");
|
|
return TimerHandle.Invalid;
|
|
}
|
|
|
|
int id = GenerateSerialId();
|
|
TimerTaskInfo taskInfo = new TimerTaskInfo(id, delay, interval, timerTask.RepeatCount,
|
|
timerTask.TimeMode, timerTask.Owner, timerTask.Callback);
|
|
_timerTasks.Add(taskInfo);
|
|
return new TimerHandle(id);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建一次性定时任务。
|
|
/// </summary>
|
|
public TimerHandle ScheduleOnce(float delay, Action callback, object owner = null,
|
|
TimerTimeMode timeMode = TimerTimeMode.Scaled)
|
|
{
|
|
return Schedule(new TimerTask(delay, delay, 1, callback, owner, timeMode));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建循环定时任务,首次触发延迟等于循环间隔。
|
|
/// </summary>
|
|
public TimerHandle ScheduleRepeat(float interval, Action callback, int repeatCount = -1, object owner = null,
|
|
TimerTimeMode timeMode = TimerTimeMode.Scaled)
|
|
{
|
|
return ScheduleRepeat(interval, interval, callback, repeatCount, owner, timeMode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建循环定时任务。
|
|
/// </summary>
|
|
public TimerHandle ScheduleRepeat(float delay, float interval, Action callback, int repeatCount = -1,
|
|
object owner = null, TimerTimeMode timeMode = TimerTimeMode.Scaled)
|
|
{
|
|
return Schedule(new TimerTask(delay, interval, repeatCount, callback, owner, timeMode));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 取消定时任务。
|
|
/// </summary>
|
|
public bool Cancel(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = _timerTasks.Count - 1; i >= 0; i--)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id != handle.Id)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
taskInfo.IsCancelled = true;
|
|
if (!_isUpdating)
|
|
{
|
|
_timerTasks.RemoveAt(i);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 取消归属于指定对象的全部定时任务。
|
|
/// </summary>
|
|
public int CancelByOwner(object owner)
|
|
{
|
|
if (owner == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int count = 0;
|
|
for (int i = _timerTasks.Count - 1; i >= 0; i--)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (!ReferenceEquals(taskInfo.Owner, owner))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
taskInfo.IsCancelled = true;
|
|
count++;
|
|
if (!_isUpdating)
|
|
{
|
|
_timerTasks.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 暂停所有定时任务。
|
|
/// </summary>
|
|
public void Pause()
|
|
{
|
|
_isPaused = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 恢复所有定时任务。
|
|
/// </summary>
|
|
public void Resume()
|
|
{
|
|
_isPaused = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 清理所有定时任务。
|
|
/// </summary>
|
|
public void ClearAll()
|
|
{
|
|
if (!_isUpdating)
|
|
{
|
|
_timerTasks.Clear();
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
_timerTasks[i].IsCancelled = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取所有活跃任务的快照(调试用)。
|
|
/// </summary>
|
|
public TimerTaskSnapshot[] GetTaskSnapshots()
|
|
{
|
|
List<TimerTaskSnapshot> snapshots = new List<TimerTaskSnapshot>(_timerTasks.Count);
|
|
foreach (TimerTaskInfo taskInfo in _timerTasks)
|
|
{
|
|
if (taskInfo.IsCancelled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
snapshots.Add(new TimerTaskSnapshot
|
|
{
|
|
Id = taskInfo.Id,
|
|
RemainingTime = taskInfo.RemainingTime,
|
|
Interval = taskInfo.Interval,
|
|
RemainingRepeatCount = taskInfo.RemainingRepeatCount,
|
|
TimeMode = taskInfo.TimeMode,
|
|
Owner = taskInfo.Owner,
|
|
CallbackMethod = $"{taskInfo.Callback.Method.DeclaringType?.Name}.{taskInfo.Callback.Method.Name}",
|
|
HasError = taskInfo.HasError,
|
|
IsPaused = taskInfo.IsPaused
|
|
});
|
|
}
|
|
|
|
return snapshots.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置任务的循环间隔。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <param name="newInterval">新的间隔时间(秒)。</param>
|
|
/// <param name="adjustRemainingTime">是否根据新旧间隔的比例调整当前剩余时间。</param>
|
|
/// <returns>是否设置成功。</returns>
|
|
public bool SetInterval(TimerHandle handle, float newInterval, bool adjustRemainingTime = false)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id != handle.Id || taskInfo.IsCancelled)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float safeInterval = Mathf.Max(0f, newInterval);
|
|
if (adjustRemainingTime && taskInfo.Interval > 0f && safeInterval > 0f)
|
|
{
|
|
float ratio = safeInterval / taskInfo.Interval;
|
|
taskInfo.RemainingTime *= ratio;
|
|
}
|
|
|
|
taskInfo.Interval = safeInterval;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 暂停单个任务。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <returns>是否暂停成功。</returns>
|
|
public bool PauseTask(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id != handle.Id)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
taskInfo.IsPaused = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 恢复单个任务。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <returns>是否恢复成功。</returns>
|
|
public bool ResumeTask(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id != handle.Id)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
taskInfo.IsPaused = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取任务的剩余时间。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <returns>剩余时间(秒),任务不存在返回 -1f。</returns>
|
|
public float GetRemainingTime(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return -1f;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id == handle.Id && !taskInfo.IsCancelled)
|
|
{
|
|
return taskInfo.RemainingTime;
|
|
}
|
|
}
|
|
|
|
return -1f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取任务的循环间隔。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <returns>循环间隔(秒),任务不存在返回 -1f。</returns>
|
|
public float GetInterval(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return -1f;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id == handle.Id && !taskInfo.IsCancelled)
|
|
{
|
|
return taskInfo.Interval;
|
|
}
|
|
}
|
|
|
|
return -1f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取任务的剩余重复次数。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <returns>剩余重复次数,任务不存在返回 -1。</returns>
|
|
public int GetRemainingRepeatCount(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id == handle.Id && !taskInfo.IsCancelled)
|
|
{
|
|
return taskInfo.RemainingRepeatCount;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 重置任务的剩余时间。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <param name="newRemainingTime">新的剩余时间(秒)。</param>
|
|
/// <returns>是否重置成功。</returns>
|
|
public bool ResetRemainingTime(TimerHandle handle, float newRemainingTime)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id == handle.Id && !taskInfo.IsCancelled)
|
|
{
|
|
taskInfo.RemainingTime = Mathf.Max(0f, newRemainingTime);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 任务是否已暂停。
|
|
/// </summary>
|
|
/// <param name="handle">任务句柄。</param>
|
|
/// <returns>任务是否已暂停,任务不存在返回 false。</returns>
|
|
public bool IsTaskPaused(TimerHandle handle)
|
|
{
|
|
if (!handle.IsValid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < _timerTasks.Count; i++)
|
|
{
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.Id == handle.Id && !taskInfo.IsCancelled)
|
|
{
|
|
return taskInfo.IsPaused;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void UpdateTasks(float scaledDeltaTime, float unscaledDeltaTime)
|
|
{
|
|
int taskCount = _timerTasks.Count;
|
|
_isUpdating = true;
|
|
for (int i = taskCount - 1; i >= 0; i--)
|
|
{
|
|
if (i >= _timerTasks.Count)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TimerTaskInfo taskInfo = _timerTasks[i];
|
|
if (taskInfo.IsCancelled || taskInfo.IsPaused)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float deltaTime = taskInfo.TimeMode == TimerTimeMode.Unscaled ? unscaledDeltaTime : scaledDeltaTime;
|
|
UpdateTask(taskInfo, deltaTime);
|
|
}
|
|
|
|
_isUpdating = false;
|
|
RemoveCancelledTasks();
|
|
}
|
|
|
|
private void UpdateTask(TimerTaskInfo taskInfo, float deltaTime)
|
|
{
|
|
if (deltaTime <= 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
taskInfo.RemainingTime -= deltaTime;
|
|
if (taskInfo.RemainingTime > 0f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InvokeCallback(taskInfo);
|
|
if (taskInfo.IsCancelled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (taskInfo.RemainingRepeatCount > 0)
|
|
{
|
|
taskInfo.RemainingRepeatCount--;
|
|
}
|
|
|
|
if (taskInfo.RemainingRepeatCount == 0)
|
|
{
|
|
taskInfo.IsCancelled = true;
|
|
return;
|
|
}
|
|
|
|
taskInfo.RemainingTime += taskInfo.Interval;
|
|
}
|
|
|
|
private void InvokeCallback(TimerTaskInfo taskInfo)
|
|
{
|
|
try
|
|
{
|
|
taskInfo.Callback.Invoke();
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
taskInfo.HasError = true;
|
|
Log.Error("Timer task callback exception: {0}", exception);
|
|
}
|
|
}
|
|
|
|
private void RemoveCancelledTasks()
|
|
{
|
|
for (int i = _timerTasks.Count - 1; i >= 0; i--)
|
|
{
|
|
if (_timerTasks[i].IsCancelled)
|
|
{
|
|
_timerTasks.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
private int GenerateSerialId()
|
|
{
|
|
_serialId++;
|
|
if (_serialId <= 0)
|
|
{
|
|
_serialId = 1;
|
|
}
|
|
|
|
return _serialId;
|
|
}
|
|
|
|
private sealed class TimerTaskInfo
|
|
{
|
|
public TimerTaskInfo(int id, float delay, float interval, int repeatCount, TimerTimeMode timeMode,
|
|
object owner, Action callback)
|
|
{
|
|
Id = id;
|
|
RemainingTime = delay;
|
|
Interval = interval;
|
|
RemainingRepeatCount = repeatCount;
|
|
TimeMode = timeMode;
|
|
Owner = owner;
|
|
Callback = callback;
|
|
IsCancelled = false;
|
|
IsPaused = false;
|
|
}
|
|
|
|
public int Id
|
|
{
|
|
get;
|
|
}
|
|
|
|
public float RemainingTime
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public float Interval
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public int RemainingRepeatCount
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public TimerTimeMode TimeMode
|
|
{
|
|
get;
|
|
}
|
|
|
|
public object Owner
|
|
{
|
|
get;
|
|
}
|
|
|
|
public Action Callback
|
|
{
|
|
get;
|
|
}
|
|
|
|
public bool IsCancelled
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool IsPaused
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public bool HasError
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 定时任务快照(调试用)。
|
|
/// </summary>
|
|
public struct TimerTaskSnapshot
|
|
{
|
|
public int Id;
|
|
public float RemainingTime;
|
|
public float Interval;
|
|
public int RemainingRepeatCount;
|
|
public TimerTimeMode TimeMode;
|
|
public object Owner;
|
|
public string CallbackMethod;
|
|
public bool HasError;
|
|
public bool IsPaused;
|
|
}
|
|
}
|