You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
7.8 KiB
262 lines
7.8 KiB
#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; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|