You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
7.8 KiB
C#
263 lines
7.8 KiB
C#
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
|
|
using System.Threading;
|
|
using System;
|
|
using Cysharp.Threading.Tasks.Internal;
|
|
using UnityEngine;
|
|
|
|
namespace Cysharp.Threading.Tasks
|
|
{
|
|
public abstract class PlayerLoopTimer : IDisposable, IPlayerLoopItem
|
|
{
|
|
readonly CancellationToken cancellationToken;
|
|
readonly Action<object> timerCallback;
|
|
readonly object state;
|
|
readonly PlayerLoopTiming playerLoopTiming;
|
|
readonly bool periodic;
|
|
|
|
bool isRunning;
|
|
bool tryStop;
|
|
bool isDisposed;
|
|
|
|
protected PlayerLoopTimer(bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
|
{
|
|
this.periodic = periodic;
|
|
this.playerLoopTiming = playerLoopTiming;
|
|
this.cancellationToken = cancellationToken;
|
|
this.timerCallback = timerCallback;
|
|
this.state = state;
|
|
}
|
|
|
|
public static PlayerLoopTimer Create(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
|
{
|
|
#if UNITY_EDITOR
|
|
// force use Realtime.
|
|
if (PlayerLoopHelper.IsMainThread && !UnityEditor.EditorApplication.isPlaying)
|
|
{
|
|
delayType = DelayType.Realtime;
|
|
}
|
|
#endif
|
|
|
|
switch (delayType)
|
|
{
|
|
case DelayType.UnscaledDeltaTime:
|
|
return new IgnoreTimeScalePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
|
|
case DelayType.Realtime:
|
|
return new RealtimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
|
|
case DelayType.DeltaTime:
|
|
default:
|
|
return new DeltaTimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state);
|
|
}
|
|
}
|
|
|
|
public static PlayerLoopTimer StartNew(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
|
{
|
|
var timer = Create(interval, periodic, delayType, playerLoopTiming, cancellationToken, timerCallback, state);
|
|
timer.Restart();
|
|
return timer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restart(Reset and Start) timer.
|
|
/// </summary>
|
|
public void Restart()
|
|
{
|
|
if (isDisposed) throw new ObjectDisposedException(null);
|
|
|
|
ResetCore(null); // init state
|
|
if (!isRunning)
|
|
{
|
|
isRunning = true;
|
|
PlayerLoopHelper.AddAction(playerLoopTiming, this);
|
|
}
|
|
tryStop = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restart(Reset and Start) and change interval.
|
|
/// </summary>
|
|
public void Restart(TimeSpan interval)
|
|
{
|
|
if (isDisposed) throw new ObjectDisposedException(null);
|
|
|
|
ResetCore(interval); // init state
|
|
if (!isRunning)
|
|
{
|
|
isRunning = true;
|
|
PlayerLoopHelper.AddAction(playerLoopTiming, this);
|
|
}
|
|
tryStop = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop timer.
|
|
/// </summary>
|
|
public void Stop()
|
|
{
|
|
tryStop = true;
|
|
}
|
|
|
|
protected abstract void ResetCore(TimeSpan? newInterval);
|
|
|
|
public void Dispose()
|
|
{
|
|
isDisposed = true;
|
|
}
|
|
|
|
bool IPlayerLoopItem.MoveNext()
|
|
{
|
|
if (isDisposed)
|
|
{
|
|
isRunning = false;
|
|
return false;
|
|
}
|
|
if (tryStop)
|
|
{
|
|
isRunning = false;
|
|
return false;
|
|
}
|
|
if (cancellationToken.IsCancellationRequested)
|
|
{
|
|
isRunning = false;
|
|
return false;
|
|
}
|
|
|
|
if (!MoveNextCore())
|
|
{
|
|
timerCallback(state);
|
|
|
|
if (periodic)
|
|
{
|
|
ResetCore(null);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
isRunning = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected abstract bool MoveNextCore();
|
|
}
|
|
|
|
sealed class DeltaTimePlayerLoopTimer : PlayerLoopTimer
|
|
{
|
|
int initialFrame;
|
|
float elapsed;
|
|
float interval;
|
|
|
|
public DeltaTimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
|
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
|
|
{
|
|
ResetCore(interval);
|
|
}
|
|
|
|
protected override bool MoveNextCore()
|
|
{
|
|
if (elapsed == 0.0f)
|
|
{
|
|
if (initialFrame == Time.frameCount)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
elapsed += Time.deltaTime;
|
|
if (elapsed >= interval)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected override void ResetCore(TimeSpan? interval)
|
|
{
|
|
this.elapsed = 0.0f;
|
|
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
|
if (interval != null)
|
|
{
|
|
this.interval = (float)interval.Value.TotalSeconds;
|
|
}
|
|
}
|
|
}
|
|
|
|
sealed class IgnoreTimeScalePlayerLoopTimer : PlayerLoopTimer
|
|
{
|
|
int initialFrame;
|
|
float elapsed;
|
|
float interval;
|
|
|
|
public IgnoreTimeScalePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
|
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
|
|
{
|
|
ResetCore(interval);
|
|
}
|
|
|
|
protected override bool MoveNextCore()
|
|
{
|
|
if (elapsed == 0.0f)
|
|
{
|
|
if (initialFrame == Time.frameCount)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
elapsed += Time.unscaledDeltaTime;
|
|
if (elapsed >= interval)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected override void ResetCore(TimeSpan? interval)
|
|
{
|
|
this.elapsed = 0.0f;
|
|
this.initialFrame = PlayerLoopHelper.IsMainThread ? Time.frameCount : -1;
|
|
if (interval != null)
|
|
{
|
|
this.interval = (float)interval.Value.TotalSeconds;
|
|
}
|
|
}
|
|
}
|
|
|
|
sealed class RealtimePlayerLoopTimer : PlayerLoopTimer
|
|
{
|
|
ValueStopwatch stopwatch;
|
|
long intervalTicks;
|
|
|
|
public RealtimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state)
|
|
: base(periodic, playerLoopTiming, cancellationToken, timerCallback, state)
|
|
{
|
|
ResetCore(interval);
|
|
}
|
|
|
|
protected override bool MoveNextCore()
|
|
{
|
|
if (stopwatch.ElapsedTicks >= intervalTicks)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected override void ResetCore(TimeSpan? interval)
|
|
{
|
|
this.stopwatch = ValueStopwatch.StartNew();
|
|
if (interval != null)
|
|
{
|
|
this.intervalTicks = interval.Value.Ticks;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|