mirror of https://github.com/Cysharp/UniTask
Merge remote-tracking branch 'origin/master'
commit
05fdf48058
|
@ -81,6 +81,7 @@ jobs:
|
|||
|
||||
# Store artifacts.
|
||||
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021
|
||||
with:
|
||||
name: UniTask.unitypackage-${{ matrix.unity }}.zip
|
||||
path: ./src/UniTask/*.unitypackage
|
||||
|
|
|
@ -526,6 +526,9 @@ It indicates when to run, you can check [PlayerLoopList.md](https://gist.github.
|
|||
|
||||
In UniTask, await directly uses native timing, while `WithCancellation` and `ToUniTask` use specified timing. This is usually not a particular problem, but with `LoadSceneAsync`, it causes a different order of Start and continuation after await. So it is recommended not to use `LoadSceneAsync.ToUniTask`.
|
||||
|
||||
> Note: When using Unity 2023.1 or newer, ensure you have `using UnityEngine;` in the using statements of your file when working with new `UnityEngine.Awaitable` methods like `SceneManager.LoadSceneAsync`.
|
||||
> This prevents compilation errors by avoiding the use of the `UnityEngine.AsyncOperation` version.
|
||||
|
||||
In the stacktrace, you can check where it is running in playerloop.
|
||||
|
||||
![image](https://user-images.githubusercontent.com/46207/83735571-83caea80-a68b-11ea-8d22-5e22864f0d24.png)
|
||||
|
|
|
@ -184,6 +184,78 @@ namespace Cysharp.Threading.Tasks
|
|||
return () => asyncAction(state).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg) => asyncAction(arg).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg0, arg1) => asyncAction(arg0, arg1).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, UniTaskVoid> asyncAction)
|
||||
{
|
||||
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3).Forget();
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T arg, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T> UnityAction<T>(Func<T, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg) => asyncAction(arg, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1> UnityAction<T0, T1>(Func<T0, T1, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg0, arg1) => asyncAction(arg0, arg1, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2> UnityAction<T0, T1, T2>(Func<T0, T1, T2, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg0, arg1, arg2) => asyncAction(arg0, arg1, arg2, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create async void(UniTaskVoid) UnityAction.
|
||||
/// For example: onClick.AddListener(UniTask.UnityAction(async (T0 arg0, T1 arg1, T2 arg2, T3 arg3, CancellationToken cancellationToken) => { /* */ } ))
|
||||
/// </summary>
|
||||
public static UnityEngine.Events.UnityAction<T0, T1, T2, T3> UnityAction<T0, T1, T2, T3>(Func<T0, T1, T2, T3, CancellationToken, UniTaskVoid> asyncAction, CancellationToken cancellationToken)
|
||||
{
|
||||
return (arg0, arg1, arg2, arg3) => asyncAction(arg0, arg1, arg2, arg3, cancellationToken).Forget();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
@ -202,6 +274,22 @@ namespace Cysharp.Threading.Tasks
|
|||
return new UniTask<T>(new DeferPromise<T>(factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defer the task creation just before call await.
|
||||
/// </summary>
|
||||
public static UniTask Defer<TState>(TState state, Func<TState, UniTask> factory)
|
||||
{
|
||||
return new UniTask(new DeferPromiseWithState<TState>(state, factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defer the task creation just before call await.
|
||||
/// </summary>
|
||||
public static UniTask<TResult> Defer<TState, TResult>(TState state, Func<TState, UniTask<TResult>> factory)
|
||||
{
|
||||
return new UniTask<TResult>(new DeferPromiseWithState<TState, TResult>(state, factory), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never complete.
|
||||
/// </summary>
|
||||
|
@ -465,6 +553,93 @@ namespace Cysharp.Threading.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
sealed class DeferPromiseWithState<TState> : IUniTaskSource
|
||||
{
|
||||
Func<TState, UniTask> factory;
|
||||
TState argument;
|
||||
UniTask task;
|
||||
UniTask.Awaiter awaiter;
|
||||
|
||||
public DeferPromiseWithState(TState argument, Func<TState, UniTask> factory)
|
||||
{
|
||||
this.argument = argument;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
awaiter.GetResult();
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f != null)
|
||||
{
|
||||
task = f(argument);
|
||||
awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DeferPromiseWithState<TState, TResult> : IUniTaskSource<TResult>
|
||||
{
|
||||
Func<TState, UniTask<TResult>> factory;
|
||||
TState argument;
|
||||
UniTask<TResult> task;
|
||||
UniTask<TResult>.Awaiter awaiter;
|
||||
|
||||
public DeferPromiseWithState(TState argument, Func<TState, UniTask<TResult>> factory)
|
||||
{
|
||||
this.argument = argument;
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
public TResult GetResult(short token)
|
||||
{
|
||||
return awaiter.GetResult();
|
||||
}
|
||||
|
||||
void IUniTaskSource.GetResult(short token)
|
||||
{
|
||||
awaiter.GetResult();
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f != null)
|
||||
{
|
||||
task = f(argument);
|
||||
awaiter = task.GetAwaiter();
|
||||
}
|
||||
|
||||
return task.Status;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
awaiter.SourceOnCompleted(continuation, state);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return task.Status;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class NeverPromise<T> : IUniTaskSource<T>
|
||||
{
|
||||
static readonly Action<object> cancellationCallback = CancellationCallback;
|
||||
|
|
|
@ -15,11 +15,21 @@ namespace Cysharp.Threading.Tasks
|
|||
return new UniTask(WaitUntilPromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitUntil<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitUntilPromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitWhile(Func<bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitWhilePromise.Create(predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitWhile<T>(T state, Func<T, bool> predicate, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitWhilePromise<T>.Create(state, predicate, timing, cancellationToken, cancelImmediately, out var token), token);
|
||||
}
|
||||
|
||||
public static UniTask WaitUntilCanceled(CancellationToken cancellationToken, PlayerLoopTiming timing = PlayerLoopTiming.Update, bool completeImmediately = false)
|
||||
{
|
||||
return new UniTask(WaitUntilCanceledPromise.Create(cancellationToken, timing, completeImmediately, out var token), token);
|
||||
|
@ -162,6 +172,135 @@ namespace Cysharp.Threading.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
sealed class WaitUntilPromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilPromise<T>>
|
||||
{
|
||||
static TaskPool<WaitUntilPromise<T>> pool;
|
||||
WaitUntilPromise<T> nextNode;
|
||||
public ref WaitUntilPromise<T> NextNode => ref nextNode;
|
||||
|
||||
static WaitUntilPromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitUntilPromise<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<T, bool> predicate;
|
||||
T argument;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitUntilPromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitUntilPromise<T>();
|
||||
}
|
||||
|
||||
result.predicate = predicate;
|
||||
result.argument = argument;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitUntilPromise<T>)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!predicate(argument))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
core.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
predicate = default;
|
||||
argument = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WaitWhilePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise>
|
||||
{
|
||||
static TaskPool<WaitWhilePromise> pool;
|
||||
|
@ -288,6 +427,135 @@ namespace Cysharp.Threading.Tasks
|
|||
}
|
||||
}
|
||||
|
||||
sealed class WaitWhilePromise<T> : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitWhilePromise<T>>
|
||||
{
|
||||
static TaskPool<WaitWhilePromise<T>> pool;
|
||||
WaitWhilePromise<T> nextNode;
|
||||
public ref WaitWhilePromise<T> NextNode => ref nextNode;
|
||||
|
||||
static WaitWhilePromise()
|
||||
{
|
||||
TaskPool.RegisterSizeGetter(typeof(WaitWhilePromise<T>), () => pool.Size);
|
||||
}
|
||||
|
||||
Func<T, bool> predicate;
|
||||
T argument;
|
||||
CancellationToken cancellationToken;
|
||||
CancellationTokenRegistration cancellationTokenRegistration;
|
||||
bool cancelImmediately;
|
||||
|
||||
UniTaskCompletionSourceCore<object> core;
|
||||
|
||||
WaitWhilePromise()
|
||||
{
|
||||
}
|
||||
|
||||
public static IUniTaskSource Create(T argument, Func<T, bool> predicate, PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
||||
}
|
||||
|
||||
if (!pool.TryPop(out var result))
|
||||
{
|
||||
result = new WaitWhilePromise<T>();
|
||||
}
|
||||
|
||||
result.predicate = predicate;
|
||||
result.argument = argument;
|
||||
result.cancellationToken = cancellationToken;
|
||||
result.cancelImmediately = cancelImmediately;
|
||||
|
||||
if (cancelImmediately && cancellationToken.CanBeCanceled)
|
||||
{
|
||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
||||
{
|
||||
var promise = (WaitWhilePromise<T>)state;
|
||||
promise.core.TrySetCanceled(promise.cancellationToken);
|
||||
}, result);
|
||||
}
|
||||
|
||||
TaskTracker.TrackActiveTask(result, 3);
|
||||
|
||||
PlayerLoopHelper.AddAction(timing, result);
|
||||
|
||||
token = result.core.Version;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetResult(short token)
|
||||
{
|
||||
try
|
||||
{
|
||||
core.GetResult(token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
|
||||
{
|
||||
TryReturn();
|
||||
}
|
||||
else
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UniTaskStatus GetStatus(short token)
|
||||
{
|
||||
return core.GetStatus(token);
|
||||
}
|
||||
|
||||
public UniTaskStatus UnsafeGetStatus()
|
||||
{
|
||||
return core.UnsafeGetStatus();
|
||||
}
|
||||
|
||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||
{
|
||||
core.OnCompleted(continuation, state, token);
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
core.TrySetCanceled(cancellationToken);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (predicate(argument))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
core.TrySetException(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
core.TrySetResult(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryReturn()
|
||||
{
|
||||
TaskTracker.RemoveTracking(this);
|
||||
core.Reset();
|
||||
predicate = default;
|
||||
argument = default;
|
||||
cancellationToken = default;
|
||||
cancellationTokenRegistration.Dispose();
|
||||
cancelImmediately = default;
|
||||
return pool.TryPush(this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class WaitUntilCanceledPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<WaitUntilCanceledPromise>
|
||||
{
|
||||
static TaskPool<WaitUntilCanceledPromise> pool;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "com.cysharp.unitask",
|
||||
"displayName": "UniTask",
|
||||
"author": { "name": "Cysharp, Inc.", "url": "https://cysharp.co.jp/en/" },
|
||||
"version": "2.5.5",
|
||||
"version": "2.5.7",
|
||||
"unity": "2018.4",
|
||||
"description": "Provides an efficient async/await integration to Unity.",
|
||||
"keywords": [ "async/await", "async", "Task", "UniTask" ],
|
||||
|
|
|
@ -145,6 +145,11 @@ namespace Cysharp.Threading.TasksTests
|
|||
public int MyProperty { get; set; }
|
||||
}
|
||||
|
||||
class MyBooleanClass
|
||||
{
|
||||
public bool MyProperty { get; set; }
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitUntil() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
|
@ -159,6 +164,20 @@ namespace Cysharp.Threading.TasksTests
|
|||
diff.Should().Be(11);
|
||||
});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitUntilWithState() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var v = new MyBooleanClass { MyProperty = false };
|
||||
|
||||
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => v.MyProperty = true).Forget();
|
||||
|
||||
var startFrame = Time.frameCount;
|
||||
await UniTask.WaitUntil(v, static v => v.MyProperty, PlayerLoopTiming.EarlyUpdate);
|
||||
|
||||
var diff = Time.frameCount - startFrame;
|
||||
diff.Should().Be(11);
|
||||
});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitWhile() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
|
@ -173,6 +192,20 @@ namespace Cysharp.Threading.TasksTests
|
|||
diff.Should().Be(11);
|
||||
});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitWhileWithState() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
var v = new MyBooleanClass { MyProperty = true };
|
||||
|
||||
UniTask.DelayFrame(10, PlayerLoopTiming.PostLateUpdate).ContinueWith(() => v.MyProperty = false).Forget();
|
||||
|
||||
var startFrame = Time.frameCount;
|
||||
await UniTask.WaitWhile(v, static v => v.MyProperty, PlayerLoopTiming.EarlyUpdate);
|
||||
|
||||
var diff = Time.frameCount - startFrame;
|
||||
diff.Should().Be(11);
|
||||
});
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator WaitUntilValueChanged() => UniTask.ToCoroutine(async () =>
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue