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.
CrowdControl/Assets/Plugins/UniTask/Runtime/PlayerLoopTimer.cs

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;
}
}
}
}