diff --git a/src/UniTask.NetCore/UniTask.NetCore.csproj b/src/UniTask.NetCore/UniTask.NetCore.csproj index d335bd7..0060575 100644 --- a/src/UniTask.NetCore/UniTask.NetCore.csproj +++ b/src/UniTask.NetCore/UniTask.NetCore.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/UniTask/Assets/Plugins/UniTask/IAsyncEnumerable.cs b/src/UniTask/Assets/Plugins/UniTask/IUniTaskAsyncEnumerable.cs similarity index 100% rename from src/UniTask/Assets/Plugins/UniTask/IAsyncEnumerable.cs rename to src/UniTask/Assets/Plugins/UniTask/IUniTaskAsyncEnumerable.cs diff --git a/src/UniTask/Assets/Plugins/UniTask/IAsyncEnumerable.cs.meta b/src/UniTask/Assets/Plugins/UniTask/IUniTaskAsyncEnumerable.cs.meta similarity index 100% rename from src/UniTask/Assets/Plugins/UniTask/IAsyncEnumerable.cs.meta rename to src/UniTask/Assets/Plugins/UniTask/IUniTaskAsyncEnumerable.cs.meta diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions.meta b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions.meta new file mode 100644 index 0000000..1097c37 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 669f5459819f7284ca1b35f4d55fe226 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/EveryUpdate.cs b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/EveryUpdate.cs new file mode 100644 index 0000000..ccb5727 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/EveryUpdate.cs @@ -0,0 +1,79 @@ +using Cysharp.Threading.Tasks.Internal; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq +{ + public static partial class UniTaskAsyncEnumerable + { + public static IUniTaskAsyncEnumerable EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) + { + return new EveryUpdate(updateTiming); + } + } + + internal class EveryUpdate : IUniTaskAsyncEnumerable + { + readonly PlayerLoopTiming updateTiming; + + public EveryUpdate(PlayerLoopTiming updateTiming) + { + this.updateTiming = updateTiming; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(updateTiming, cancellationToken); + } + + class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator, IPlayerLoopItem + { + readonly PlayerLoopTiming updateTiming; + CancellationToken cancellationToken; + + bool disposed; + + public Enumerator(PlayerLoopTiming updateTiming, CancellationToken cancellationToken) + { + this.updateTiming = updateTiming; + + TaskTracker.TrackActiveTask(this, 2); + PlayerLoopHelper.AddAction(updateTiming, this); + } + + public AsyncUnit Current => default; + + public UniTask MoveNextAsync() + { + // return false instead of throw + if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False; + + completionSource.Reset(); + return new UniTask(this, completionSource.Version); + } + + public UniTask DisposeAsync() + { + if (!disposed) + { + disposed = true; + TaskTracker.RemoveTracking(this); + } + return default; + } + + public bool MoveNext() + { + if (disposed || cancellationToken.IsCancellationRequested) + { + completionSource.TrySetResult(false); + return false; + } + + completionSource.TrySetResult(true); + return true; + } + } + } +} \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/EveryUpdate.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/EveryUpdate.cs.meta new file mode 100644 index 0000000..6336e0e --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/EveryUpdate.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00520eb52e49b5b4e8d9870d6ff1aced +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Interval.cs b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Interval.cs new file mode 100644 index 0000000..8b9e529 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Interval.cs @@ -0,0 +1,275 @@ +using System; +using System.Threading; + +namespace Cysharp.Threading.Tasks.Linq +{ + public static partial class UniTaskAsyncEnumerable + { + public static IUniTaskAsyncEnumerable Timer(TimeSpan dueTime, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) + { + return new Timer(dueTime, null, updateTiming, ignoreTimeScale); + } + + public static IUniTaskAsyncEnumerable Timer(TimeSpan dueTime, TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) + { + return new Timer(dueTime, period, updateTiming, ignoreTimeScale); + } + + public static IUniTaskAsyncEnumerable Interval(TimeSpan period, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update, bool ignoreTimeScale = false) + { + return new Timer(period, period, updateTiming, ignoreTimeScale); + } + + public static IUniTaskAsyncEnumerable TimerFrame(int dueTimeFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) + { + return new TimerFrame(dueTimeFrameCount, null, updateTiming); + } + + public static IUniTaskAsyncEnumerable TimerFrame(int dueTimeFrameCount, int periodFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) + { + return new TimerFrame(dueTimeFrameCount, periodFrameCount, updateTiming); + } + + public static IUniTaskAsyncEnumerable IntervalFrame(int intervalFrameCount, PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) + { + return new TimerFrame(intervalFrameCount, intervalFrameCount, updateTiming); + } + } + + internal class Timer : IUniTaskAsyncEnumerable + { + readonly PlayerLoopTiming updateTiming; + readonly TimeSpan dueTime; + readonly TimeSpan? period; + readonly bool ignoreTimeScale; + + public Timer(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale) + { + this.updateTiming = updateTiming; + this.dueTime = dueTime; + this.period = period; + this.ignoreTimeScale = ignoreTimeScale; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(dueTime, period, updateTiming, ignoreTimeScale, cancellationToken); + } + + class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator, IPlayerLoopItem + { + readonly float dueTime; + readonly float? period; + readonly PlayerLoopTiming updateTiming; + readonly bool ignoreTimeScale; + CancellationToken cancellationToken; + + float elapsed; + bool dueTimePhase; + bool disposed; + + public Enumerator(TimeSpan dueTime, TimeSpan? period, PlayerLoopTiming updateTiming, bool ignoreTimeScale, CancellationToken cancellationToken) + { + this.dueTime = (float)dueTime.TotalSeconds; + this.period = (period == null) ? null : (float?)period.Value.TotalSeconds; + + if (this.dueTime <= 0) this.dueTime = 0; + if (this.period != null) + { + if (this.period <= 0) this.period = 1; + } + + this.dueTimePhase = true; + this.updateTiming = updateTiming; + this.ignoreTimeScale = ignoreTimeScale; + TaskTracker.TrackActiveTask(this, 2); + PlayerLoopHelper.AddAction(updateTiming, this); + } + + public AsyncUnit Current => default; + + public UniTask MoveNextAsync() + { + // return false instead of throw + if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False; + + completionSource.Reset(); + return new UniTask(this, completionSource.Version); + } + + public UniTask DisposeAsync() + { + if (!disposed) + { + disposed = true; + TaskTracker.RemoveTracking(this); + } + return default; + } + + public bool MoveNext() + { + if (disposed || cancellationToken.IsCancellationRequested) + { + completionSource.TrySetResult(false); + return false; + } + + AddElapsed(); + if (dueTimePhase) + { + if (elapsed >= dueTime) + { + dueTimePhase = false; + elapsed = 0; + completionSource.TrySetResult(true); + } + } + else + { + if (period == null) + { + completionSource.TrySetResult(false); + return false; + } + + if (elapsed >= period) + { + elapsed = 0; + completionSource.TrySetResult(true); + } + } + + return true; + } + + void AddElapsed() + { + if (updateTiming == PlayerLoopTiming.FixedUpdate) + { + if (ignoreTimeScale) + { + elapsed += UnityEngine.Time.fixedUnscaledDeltaTime; + } + else + { + elapsed += UnityEngine.Time.fixedDeltaTime; + } + } + else + { + if (ignoreTimeScale) + { + elapsed += UnityEngine.Time.unscaledDeltaTime; + } + else + { + elapsed += UnityEngine.Time.deltaTime; + } + } + } + } + } + + internal class TimerFrame : IUniTaskAsyncEnumerable + { + readonly PlayerLoopTiming updateTiming; + readonly int dueTimeFrameCount; + readonly int? periodFrameCount; + + public TimerFrame(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming) + { + this.updateTiming = updateTiming; + this.dueTimeFrameCount = dueTimeFrameCount; + this.periodFrameCount = periodFrameCount; + } + + public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + return new Enumerator(dueTimeFrameCount, periodFrameCount, updateTiming, cancellationToken); + } + + class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator, IPlayerLoopItem + { + readonly int dueTimeFrameCount; + readonly int? periodFrameCount; + CancellationToken cancellationToken; + + int currentFrame; + bool dueTimePhase; + bool disposed; + + public Enumerator(int dueTimeFrameCount, int? periodFrameCount, PlayerLoopTiming updateTiming, CancellationToken cancellationToken) + { + if (dueTimeFrameCount <= 0) dueTimeFrameCount = 0; + if (periodFrameCount != null) + { + if (periodFrameCount <= 0) periodFrameCount = 1; + } + + this.dueTimePhase = true; + this.dueTimeFrameCount = dueTimeFrameCount; + this.periodFrameCount = periodFrameCount; + + TaskTracker.TrackActiveTask(this, 2); + PlayerLoopHelper.AddAction(updateTiming, this); + } + + public AsyncUnit Current => default; + + public UniTask MoveNextAsync() + { + // return false instead of throw + if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False; + + completionSource.Reset(); + return new UniTask(this, completionSource.Version); + } + + public UniTask DisposeAsync() + { + if (!disposed) + { + disposed = true; + TaskTracker.RemoveTracking(this); + } + return default; + } + + public bool MoveNext() + { + if (disposed || cancellationToken.IsCancellationRequested) + { + completionSource.TrySetResult(false); + return false; + } + + if (dueTimePhase) + { + if (currentFrame++ == dueTimeFrameCount) + { + dueTimePhase = false; + completionSource.TrySetResult(true); + currentFrame = -1; + } + } + else + { + if (periodFrameCount == null) + { + completionSource.TrySetResult(false); + return false; + } + + if (++currentFrame == periodFrameCount) + { + completionSource.TrySetResult(true); + currentFrame = 0; + } + } + + return true; + } + } + } +} \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Interval.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Interval.cs.meta new file mode 100644 index 0000000..ffb5f6d --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Interval.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14e8116614488eb43b1428191cef8fe5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Timer.cs b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Timer.cs new file mode 100644 index 0000000..32993c1 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Timer.cs @@ -0,0 +1,76 @@ +//using Cysharp.Threading.Tasks.Internal; +//using System; +//using System.Collections.Generic; +//using System.Threading; + +//namespace Cysharp.Threading.Tasks.Linq +//{ +// public static partial class UniTaskAsyncEnumerable +// { +// public static IUniTaskAsyncEnumerable EveryUpdate(PlayerLoopTiming updateTiming = PlayerLoopTiming.Update) +// { +// return new EveryUpdate(updateTiming); +// } +// } + +// internal class EveryUpdate : IUniTaskAsyncEnumerable +// { +// readonly PlayerLoopTiming updateTiming; + +// public EveryUpdate(PlayerLoopTiming updateTiming) +// { +// this.updateTiming = updateTiming; +// } + +// public IUniTaskAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) +// { +// return new Enumerator(updateTiming, cancellationToken); +// } + +// class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator, IPlayerLoopItem +// { +// readonly PlayerLoopTiming updateTiming; +// CancellationToken cancellationToken; + +// bool disposed; + +// public Enumerator(PlayerLoopTiming updateTiming, CancellationToken cancellationToken) +// { +// this.updateTiming = updateTiming; + +// TaskTracker.TrackActiveTask(this, 2); +// PlayerLoopHelper.AddAction(updateTiming, this); +// } + +// public AsyncUnit Current => default; + +// public UniTask MoveNextAsync() +// { +// return false instead of throw +// if (disposed || cancellationToken.IsCancellationRequested) return CompletedTasks.False; + +// completionSource.Reset(); +// return new UniTask(this, completionSource.Version); +// } + +// public UniTask DisposeAsync() +// { +// if (!disposed) +// { +// disposed = true; +// TaskTracker.RemoveTracking(this); +// } +// return default; +// } + +// public bool MoveNext() +// { +// if (disposed) return false; +// if (cancellationToken.IsCancellationRequested) return false; + +// completionSource.TrySetResult(true); +// return true; +// } +// } +// } +//} \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Timer.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Timer.cs.meta new file mode 100644 index 0000000..aa790c5 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Linq/UnityExtensions/Timer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 382caacde439855418709c641e4d7b04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index 79cf010..64f3e25 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -146,10 +146,12 @@ public class SandboxMain : MonoBehaviour //StartCoroutine(cor); - await okButton.OnClickAsAsyncEnumerable().Where((x, i) => i % 2 == 0).ForEachAsync(_ => + Debug.Log("E:" + DateTime.Now.ToString()); + + await UniTaskAsyncEnumerable.Timer(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), PlayerLoopTiming.Update).ForEachAsync(_ => { - Debug.Log("Call"); - }); + Debug.Log("Call:" + DateTime.Now.ToString()); + }, cancellationToken: this.GetCancellationTokenOnDestroy()); //try //{