Add AsyncReactiveProperty

master
neuecc 2020-05-17 01:29:45 +09:00
parent 859eaa2278
commit ee58aab0a9
10 changed files with 672 additions and 400 deletions

View File

@ -28,6 +28,7 @@
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.uGUI.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.MonoBehaviour.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" />
</ItemGroup>

View File

@ -0,0 +1,120 @@
using Cysharp.Threading.Tasks.Linq;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public interface IAsyncReadOnlyReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
{
T Value { get; }
}
public interface IAsyncReactiveProperty<T> : IAsyncReadOnlyReactiveProperty<T>
{
new T Value { get; set; }
}
[Serializable]
public class AsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>, IDisposable
{
TriggerEvent<T> triggerEvent;
#if UNITY_2018_3_OR_NEWER
[UnityEngine.SerializeField]
#endif
T latestValue;
public T Value
{
get
{
return latestValue;
}
set
{
this.latestValue = value;
triggerEvent.SetResult(value);
}
}
public AsyncReactiveProperty(T value)
{
this.latestValue = value;
this.triggerEvent = default;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
{
return new Enumerator(this, cancellationToken);
}
public void Dispose()
{
triggerEvent.SetCanceled(CancellationToken.None);
}
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, IResolveCancelPromise<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly AsyncReactiveProperty<T> parent;
readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
T value;
bool isDisposed;
public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken)
{
this.parent = parent;
this.cancellationToken = cancellationToken;
parent.triggerEvent.Add(this);
TaskTracker.TrackActiveTask(this, 3);
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
public T Current => value;
public UniTask<bool> MoveNextAsync()
{
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
if (!isDisposed)
{
isDisposed = true;
TaskTracker.RemoveTracking(this);
completionSource.TrySetCanceled(cancellationToken);
parent.triggerEvent.Remove(this);
}
return default;
}
public bool TrySetResult(T value)
{
this.value = value;
completionSource.TrySetResult(true);
return true;
}
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
DisposeAsync().Forget();
return true;
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeAsync().Forget();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8ef320b87f537ee4fb2282e765dc6166
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,229 @@
using Cysharp.Threading.Tasks.Internal;
using System;
using System.Threading;
namespace Cysharp.Threading.Tasks
{
public interface ITriggerEvent<T>
{
void SetResult(T value);
void SetCanceled(CancellationToken cancellationToken);
void Add(IResolveCancelPromise<T> handler);
void Remove(IResolveCancelPromise<T> handler);
}
// be careful to use, itself is struct.
public struct TriggerEvent<T> : ITriggerEvent<T>
{
// optimize: many cases, handler is single.
IResolveCancelPromise<T> singleHandler;
IResolveCancelPromise<T>[] handlers;
// when running(in TrySetResult), does not add immediately.
bool isRunning;
IResolveCancelPromise<T> waitHandler;
MinimumQueue<IResolveCancelPromise<T>> waitQueue;
public void SetResult(T value)
{
isRunning = true;
if (singleHandler != null)
{
try
{
singleHandler.TrySetResult(value);
}
catch (Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
}
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] != null)
{
try
{
handlers[i].TrySetResult(value);
}
catch (Exception ex)
{
handlers[i] = null;
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
}
}
public void SetCanceled(CancellationToken cancellationToken)
{
isRunning = true;
if (singleHandler != null)
{
try
{
((ICancelPromise)singleHandler).TrySetCanceled(cancellationToken);
}
catch (Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
}
}
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] != null)
{
try
{
((ICancelPromise)handlers[i]).TrySetCanceled(cancellationToken);
}
catch (Exception ex)
{
#if UNITY_2018_3_OR_NEWER
UnityEngine.Debug.LogException(ex);
#else
Console.WriteLine(ex);
#endif
handlers[i] = null;
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
}
}
public void Add(IResolveCancelPromise<T> handler)
{
if (isRunning)
{
if (waitHandler == null)
{
waitHandler = handler;
return;
}
if (waitQueue == null)
{
waitQueue = new MinimumQueue<IResolveCancelPromise<T>>(4);
}
waitQueue.Enqueue(handler);
return;
}
if (singleHandler == null)
{
singleHandler = handler;
}
else
{
if (handlers == null)
{
handlers = new IResolveCancelPromise<T>[4];
}
// check empty
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] == null)
{
handlers[i] = handler;
return;
}
}
// full, ensure capacity
var last = handlers.Length;
{
EnsureCapacity(ref handlers);
handlers[last] = handler;
}
}
}
static void EnsureCapacity(ref IResolveCancelPromise<T>[] array)
{
var newSize = array.Length * 2;
var newArray = new IResolveCancelPromise<T>[newSize];
Array.Copy(array, 0, newArray, 0, array.Length);
array = newArray;
}
public void Remove(IResolveCancelPromise<T> handler)
{
if (singleHandler == handler)
{
singleHandler = null;
}
else
{
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] == handler)
{
// fill null.
handlers[i] = null;
return;
}
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f68b22bb8f66f5c4885f9bd3c4fc43ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,6 +1,5 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
using Cysharp.Threading.Tasks.Internal;
using Cysharp.Threading.Tasks.Linq;
using System;
using System.Threading;
@ -25,7 +24,7 @@ namespace Cysharp.Threading.Tasks.Triggers
if (calledDestroy) return;
calledDestroy = true;
triggerEvent.TrySetCanceled(CancellationToken.None);
triggerEvent.SetCanceled(CancellationToken.None);
}
internal void AddHandler(IResolveCancelPromise<T> handler)
@ -50,7 +49,7 @@ namespace Cysharp.Threading.Tasks.Triggers
protected void RaiseEvent(T value)
{
triggerEvent.TrySetResult(value);
triggerEvent.SetResult(value);
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
@ -284,207 +283,4 @@ namespace Cysharp.Threading.Tasks.Triggers
core.OnCompleted(continuation, state, token);
}
}
// be careful to use, itself is struct.
public struct TriggerEvent<T>
{
// optimize: many cases, handler is single.
IResolveCancelPromise<T> singleHandler;
IResolveCancelPromise<T>[] handlers;
// when running(in TrySetResult), does not add immediately.
bool isRunning;
IResolveCancelPromise<T> waitHandler;
MinimumQueue<IResolveCancelPromise<T>> waitQueue;
public bool TrySetResult(T value)
{
isRunning = true;
if (singleHandler != null)
{
try
{
singleHandler.TrySetResult(value);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] != null)
{
try
{
handlers[i].TrySetResult(value);
}
catch (Exception ex)
{
handlers[i] = null;
Debug.LogException(ex);
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
}
return true;
}
public bool TrySetCanceled(CancellationToken cancellationToken)
{
isRunning = true;
if (singleHandler != null)
{
try
{
((ICancelPromise)singleHandler).TrySetCanceled(cancellationToken);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] != null)
{
try
{
((ICancelPromise)handlers[i]).TrySetCanceled(cancellationToken);
}
catch (Exception ex)
{
Debug.LogException(ex);
handlers[i] = null;
}
}
}
}
isRunning = false;
if (waitHandler != null)
{
var h = waitHandler;
waitHandler = null;
Add(h);
}
if (waitQueue != null)
{
while (waitQueue.Count != 0)
{
Add(waitQueue.Dequeue());
}
}
return true;
}
public void Add(IResolveCancelPromise<T> handler)
{
if (isRunning)
{
if (waitHandler == null)
{
waitHandler = handler;
return;
}
if (waitQueue == null)
{
waitQueue = new MinimumQueue<IResolveCancelPromise<T>>(4);
}
waitQueue.Enqueue(handler);
return;
}
if (singleHandler == null)
{
singleHandler = handler;
}
else
{
if (handlers == null)
{
handlers = new IResolveCancelPromise<T>[4];
}
// check empty
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] == null)
{
handlers[i] = handler;
return;
}
}
// full, ensure capacity
var last = handlers.Length;
{
EnsureCapacity(ref handlers);
handlers[last] = handler;
}
}
}
static void EnsureCapacity(ref IResolveCancelPromise<T>[] array)
{
var newSize = array.Length * 2;
var newArray = new IResolveCancelPromise<T>[newSize];
Array.Copy(array, 0, newArray, 0, array.Length);
array = newArray;
}
public void Remove(IResolveCancelPromise<T> handler)
{
if (singleHandler == handler)
{
singleHandler = null;
}
else
{
if (handlers != null)
{
for (int i = 0; i < handlers.Length; i++)
{
if (handlers[i] == handler)
{
// fill null.
handlers[i] = null;
return;
}
}
}
}
}
}
}

View File

@ -1,7 +1,8 @@
{
"name": "UniTask",
"references": [
"Unity.ResourceManager"
"Unity.ResourceManager",
"Unity.TextMeshPro"
],
"includePlatforms": [],
"excludePlatforms": [],
@ -15,6 +16,11 @@
"name": "com.unity.addressables",
"expression": "",
"define": "UNITASK_ADDRESSABLE_SUPPORT"
},
{
"name": "com.unity.textmeshpro",
"expression": "",
"define": "UNITASK_TEXTMESHPRO_SUPPORT"
}
],
"noEngineReferences": false

View File

@ -0,0 +1,263 @@
using System;
using System.Threading;
using UnityEngine.UI;
namespace Cysharp.Threading.Tasks
{
public static class UnityBindingExtensions
{
// <string> -> Text
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
if (e != null)
{
await e.DisposeAsync();
}
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
text.text = e.Current;
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
// <T> -> Text
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
public static void BindTo<T>(this AsyncReactiveProperty<T> source, Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
if (e != null)
{
await e.DisposeAsync();
}
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
text.text = e.Current.ToString();
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
// <bool> -> Selectable
public static void BindTo(this IUniTaskAsyncEnumerable<bool> source, Selectable selectable, bool rebindOnError = true)
{
BindToCore(source, selectable, selectable.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<bool> source, Selectable selectable, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, selectable, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<bool> source, Selectable selectable, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
if (e != null)
{
await e.DisposeAsync();
}
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
selectable.interactable = e.Current;
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
// TMP
#if UNITASK_TEXTMESHPRO_SUPPORT
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMPro.TMP_Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMPro.TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, TMPro.TMP_Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
if (e != null)
{
await e.DisposeAsync();
}
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
text.text = e.Current;
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
#endif
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 090b20e3528552b4a8d751f7df525c2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -14,6 +14,7 @@ using Unity.Jobs;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using TMPro;
public struct MyJob : IJob
{
@ -39,187 +40,9 @@ public enum MyEnum
public interface IAsyncReadOnlyReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
{
T Value { get; }
}
public interface IAsyncReactiveProperty<T> : IAsyncReadOnlyReactiveProperty<T>
{
new T Value { get; set; }
}
[Serializable]
public struct AsyncValueReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
{
TriggerEvent<T> triggerEvent;
[SerializeField]
T latestValue;
public T Value
{
get
{
return latestValue;
}
set
{
this.latestValue = value;
triggerEvent.TrySetResult(value);
}
}
public AsyncValueReactiveProperty(T value)
{
this.latestValue = value;
this.triggerEvent = default;
}
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
{
return new Enumerator(triggerEvent, cancellationToken);
}
public class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, IResolveCancelPromise<T>
{
static Action<object> cancellationCallback = CancellationCallback;
readonly TriggerEvent<T> triggerEvent;
readonly CancellationToken cancellationToken;
readonly CancellationTokenRegistration cancellationTokenRegistration;
T value;
public Enumerator(TriggerEvent<T> triggerEvent, CancellationToken cancellationToken)
{
this.triggerEvent = triggerEvent;
this.cancellationToken = cancellationToken;
triggerEvent.Add(this);
if (cancellationToken.CanBeCanceled)
{
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
}
}
public T Current => value;
public UniTask<bool> MoveNextAsync()
{
completionSource.Reset();
return new UniTask<bool>(this, completionSource.Version);
}
public UniTask DisposeAsync()
{
triggerEvent.TrySetCanceled(cancellationToken);
triggerEvent.Remove(this);
return default;
}
public bool TrySetResult(T value)
{
this.value = value;
return triggerEvent.TrySetResult(value);
}
public bool TrySetCanceled(CancellationToken cancellationToken = default)
{
DisposeAsync().Forget();
return true;
}
static void CancellationCallback(object state)
{
var self = (Enumerator)state;
self.DisposeAsync().Forget();
}
}
}
public static partial class UnityUIComponentExtensions
{
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, bool rebindOnError = true)
{
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget();
}
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError = true)
{
BindToCore(source, text, cancellationToken, rebindOnError).Forget();
}
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, Text text, CancellationToken cancellationToken, bool rebindOnError)
{
var repeat = false;
BIND_AGAIN:
var e = source.GetAsyncEnumerator(cancellationToken);
try
{
while (true)
{
bool moveNext;
try
{
moveNext = await e.MoveNextAsync();
repeat = false;
}
catch (Exception ex)
{
if (ex is OperationCanceledException) return;
if (rebindOnError && !repeat)
{
repeat = true;
if (e != null)
{
await e.DisposeAsync();
}
goto BIND_AGAIN;
}
else
{
throw;
}
}
if (!moveNext) return;
text.text = e.Current;
}
}
finally
{
if (e != null)
{
await e.DisposeAsync();
}
}
}
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text)
//{
// return source.SubscribeWithState(text, (x, t) => t.text = x.ToString());
//}
//public static IDisposable SubscribeToText<T>(this IObservable<T> source, Text text, Func<T, string> selector)
//{
// return source.SubscribeWithState2(text, selector, (x, t, s) => t.text = s(x));
//}
//public static IDisposable SubscribeToInteractable(this IObservable<bool> source, Selectable selectable)
//{
// return source.SubscribeWithState(selectable, (x, s) => s.interactable = x);
//}
}
public static class MyClass
{
}
@ -230,7 +53,6 @@ public static class MyClass
public class SandboxMain : MonoBehaviour
{
public Button okButton;
@ -239,6 +61,9 @@ public class SandboxMain : MonoBehaviour
CancellationTokenSource cts;
public AsyncReactiveProperty<int> RP1;
UniTaskCompletionSource ucs;
async UniTask<int> FooAsync()
{
@ -300,7 +125,7 @@ public class SandboxMain : MonoBehaviour
}
async void Start()
void Start()
{
Application.SetStackTraceLogType(LogType.Error, StackTraceLogType.Full);
Application.SetStackTraceLogType(LogType.Exception, StackTraceLogType.Full);
@ -309,6 +134,13 @@ public class SandboxMain : MonoBehaviour
//ShowPlayerLoop.DumpPlayerLoop("Current", playerLoop);
RP1 = new AsyncReactiveProperty<int>(999);
RP1.Select(x => x * x).BindTo(text);
//Update2().Forget();
//RunStandardDelayAsync().Forget();
@ -342,17 +174,6 @@ public class SandboxMain : MonoBehaviour
// UniTaskAsyncEnumerable.EveryUpdate(PlayerLoopTiming.FixedUpdate)
this.GetAsyncUpdateTrigger().ForEachAsync(_ =>
{
UnityEngine.Debug.Log("Update Trigger 1");
}).Forget();
this.GetAsyncUpdateTrigger().ForEachAsync(_ =>
{
UnityEngine.Debug.Log("Update Trigger 2");
}).Forget();
//await UniTask.Yield(PlayerLoopTiming.Update);
//Debug.Log("Start:" + Time.frameCount);
@ -401,8 +222,8 @@ public class SandboxMain : MonoBehaviour
//await okButton.GetAsyncClickEventHandler().DisableAutoClose()
// .Select((_, clickCount) => clickCount + 1)
// .FirstAsync(x => x == 5);
// .Select((_, clickCount) => clickCount + 1)
// .FirstAsync(x => x == 5);
//Debug.Log("Click 5 times.");
@ -433,6 +254,9 @@ public class SandboxMain : MonoBehaviour
okButton.onClick.AddListener(() =>
{
// FooAsync().Forget();
RP1.Value += 3;
});
cancelButton.onClick.AddListener(() =>