From 896eef1ee400a7a5cef6187a07eaac26380e58ab Mon Sep 17 00:00:00 2001 From: neuecc Date: Sat, 23 May 2020 01:10:04 +0900 Subject: [PATCH] Add DoTween Extension --- .../External/DoTweenAsyncExtensions.cs | 241 ++++++++++++++++++ .../External/DoTweenAsyncExtensions.cs.meta | 11 + .../Plugins/UniTask/Runtime/UniTask.asmdef | 8 +- .../UniTask/Runtime/UnityAsyncExtensions.cs | 2 - src/UniTask/Assets/Scenes/SandboxMain.cs | 57 +++-- .../Tests/Editor/UniTask.Tests.Editor.asmdef | 6 +- src/UniTask/Assets/Tests/UniTask.Tests.asmdef | 6 +- 7 files changed, 297 insertions(+), 34 deletions(-) create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs.meta diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs new file mode 100644 index 0000000..1779289 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs @@ -0,0 +1,241 @@ +// asmdef Version Defines, enabled when com.demigiant.dotween is imported. + +#if UNITASK_DOTWEEN_SUPPORT + +using Cysharp.Threading.Tasks.Internal; +using DG.Tweening; +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Cysharp.Threading.Tasks +{ + // The idea of TweenCancelBehaviour is borrowed from https://www.shibuya24.info/entry/dotween_async_await + public enum TweenCancelBehaviour + { + Kill, + KillWithCompleteCallback, + Complete, + CompleteWithSeqeunceCallback, + CancelAwait, + + // AndCancelAwait + KillAndCancelAwait, + KillWithCompleteCallbackAndCancelAwait, + CompleteAndCancelAwait, + CompleteWithSeqeunceCallbackAndCancelAwait + } + + public static class DoTweenAsyncExtensions + { + public static TweenAwaiter GetAwaiter(this Tween tween) + { + return new TweenAwaiter(tween); + } + + public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, out var token), token); + } + + public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) + { + Error.ThrowArgumentNullException(tween, nameof(tween)); + return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, out var token), token); + } + + public struct TweenAwaiter : ICriticalNotifyCompletion + { + readonly Tween tween; + + // killed(non active) as completed. + public bool IsCompleted => !tween.IsActive(); + + public TweenAwaiter(Tween tween) + { + this.tween = tween; + } + + public TweenAwaiter GetAwaiter() + { + return this; + } + + public void GetResult() + { + } + + public void OnCompleted(System.Action continuation) + { + UnsafeOnCompleted(continuation); + } + + public void UnsafeOnCompleted(System.Action continuation) + { + // convert Action -> TweenCallback allocation. + // onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true). + tween.onKill = new TweenCallback(continuation); + } + } + + class TweenConfiguredSource : IUniTaskSource, IPromisePoolItem + { + static readonly PromisePool pool = new PromisePool(); + static Action CancellationCallbackDelegate = CancellationCallback; + + Tween tween; + TweenCancelBehaviour cancelBehaviour; + CancellationToken cancellationToken; + bool canceled; + + CancellationTokenRegistration cancellationTokenRegistration; + UniTaskCompletionSourceCore core; + + TweenConfiguredSource() + { + + } + + public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, out short token) + { + if (cancellationToken.IsCancellationRequested) + { + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); + } + + var result = pool.TryRent() ?? new TweenConfiguredSource(); + + result.tween = tween; + result.cancelBehaviour = cancelBehaviour; + result.cancellationToken = cancellationToken; + + TaskTracker.TrackActiveTask(result, 3); + + result.RegisterEvent(); + + token = result.core.Version; + return result; + } + + void RegisterEvent() + { + if (cancellationToken.CanBeCanceled) + { + cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this); + } + + // allocate delegate. + tween.OnKill(new TweenCallback(OnKill)); + } + + void OnKill() + { + cancellationTokenRegistration.Dispose(); + if (canceled) + { + if (cancelBehaviour == TweenCancelBehaviour.CancelAwait) + { + // already called TrySetCanceled, do nothing. + } + else + { + core.TrySetCanceled(cancellationToken); + } + } + else + { + core.TrySetResult(AsyncUnit.Default); + } + } + + static void CancellationCallback(object state) + { + var self = (TweenConfiguredSource)state; + + switch (self.cancelBehaviour) + { + case TweenCancelBehaviour.Kill: + default: + self.tween.Kill(false); + break; + case TweenCancelBehaviour.KillAndCancelAwait: + self.canceled = true; + self.tween.Kill(false); + break; + case TweenCancelBehaviour.KillWithCompleteCallback: + self.tween.Kill(true); + break; + case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait: + self.canceled = true; + self.tween.Kill(true); + break; + case TweenCancelBehaviour.Complete: + self.tween.Complete(false); + break; + case TweenCancelBehaviour.CompleteAndCancelAwait: + self.canceled = true; + self.tween.Complete(false); + break; + case TweenCancelBehaviour.CompleteWithSeqeunceCallback: + self.tween.Complete(true); + break; + case TweenCancelBehaviour.CompleteWithSeqeunceCallbackAndCancelAwait: + self.canceled = true; + self.tween.Complete(true); + break; + case TweenCancelBehaviour.CancelAwait: + self.canceled = true; + self.core.TrySetCanceled(self.cancellationToken); + break; + } + } + + public void GetResult(short token) + { + try + { + TaskTracker.RemoveTracking(this); + core.GetResult(token); + } + finally + { + pool.TryReturn(this); + } + } + + + public UniTaskStatus GetStatus(short token) + { + return core.GetStatus(token); + } + + public UniTaskStatus UnsafeGetStatus() + { + return core.UnsafeGetStatus(); + } + + public void OnCompleted(Action continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + public void Reset() + { + core.Reset(); + tween = default; + cancellationToken = default; + } + + ~TweenConfiguredSource() + { + if (pool.TryReturn(this)) + { + GC.ReRegisterForFinalize(this); + } + } + } + } +} + +#endif diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs.meta new file mode 100644 index 0000000..63131b0 --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/External/DoTweenAsyncExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f448d5bc5b232e4f98d89d5d1832e8e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef index 12c99ed..d1dbb64 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UniTask.asmdef @@ -2,7 +2,8 @@ "name": "UniTask", "references": [ "Unity.ResourceManager", - "Unity.TextMeshPro" + "Unity.TextMeshPro", + "DOTween.Modules" ], "includePlatforms": [], "excludePlatforms": [], @@ -21,6 +22,11 @@ "name": "com.unity.textmeshpro", "expression": "", "define": "UNITASK_TEXTMESHPRO_SUPPORT" + }, + { + "name": "com.demigiant.dotween", + "expression": "", + "define": "UNITASK_DOTWEEN_SUPPORT" } ], "noEngineReferences": false diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs index ef238f4..bfa6ee6 100644 --- a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.cs @@ -24,7 +24,6 @@ namespace Cysharp.Threading.Tasks public static UniTask ToUniTask(this AsyncOperation asyncOperation) { Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation)); - return new UniTask(AsyncOperationConfiguredSource.Create(asyncOperation, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token); } @@ -125,7 +124,6 @@ namespace Cysharp.Threading.Tasks } } - public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index 64a4e35..8900db5 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -12,6 +12,9 @@ using Unity.Jobs; using UnityEngine; using UnityEngine.UI; + +// using DG.Tweening; + public struct MyJob : IJob { public int loopCount; @@ -240,16 +243,9 @@ public class SandboxMain : MonoBehaviour public int MyProperty { get; set; } } - MyClass mcc; - void Start() + async UniTaskVoid Start() { - this.mcc = new MyClass(); - this.MyProperty = 999; - - CheckDest().Forget(); - - //UniTaskAsyncEnumerable.EveryValueChanged(mcc, x => x.MyProperty) // .Do(_ => { }, () => Debug.Log("COMPLETED")) // .ForEachAsync(x => @@ -260,22 +256,43 @@ public class SandboxMain : MonoBehaviour + // DG.Tweening.Core.TweenerCore + //okButton.GetComponent().DOMoveX(10.2f, 30); + // DOTween.To( + + var cts = new CancellationTokenSource(); + + //var tween = okButton.GetComponent().DOLocalMoveX(100, 5.0f); + + cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ => + { + cts.Cancel(); + }).Forget(); + + + // await tween.ToUniTask(TweenCancelBehaviour.KillAndCancelAwait, cts.Token); + + //tween.SetRecyclable(true); + + Debug.Log("END"); + + // tween.Play(); + + // DOTween. + + // DOVirtual.Float(0, 1, 1, x => { }).ToUniTask(); okButton.OnClickAsAsyncEnumerable().ForEachAsync(_ => { - mcc.MyProperty += 10; + }).Forget(); - cancelButton.OnClickAsAsyncEnumerable().ForEachAsync(_ => - { - this.mcc = null; - }); okButton.onClick.AddListener(UniTask.UnityAction(async () => await UniTask.Yield())); @@ -286,20 +303,6 @@ public class SandboxMain : MonoBehaviour await UniTask.Yield(); } - async UniTaskVoid CheckDest() - { - try - { - Debug.Log("WAIT"); - await UniTask.WaitUntilValueChanged(mcc, x => x.MyProperty); - Debug.Log("CHANGED?"); - } - finally - { - Debug.Log("END"); - } - } - async UniTaskVoid Running(CancellationToken ct) { Debug.Log("BEGIN"); diff --git a/src/UniTask/Assets/Tests/Editor/UniTask.Tests.Editor.asmdef b/src/UniTask/Assets/Tests/Editor/UniTask.Tests.Editor.asmdef index 23b0ccf..455e255 100644 --- a/src/UniTask/Assets/Tests/Editor/UniTask.Tests.Editor.asmdef +++ b/src/UniTask/Assets/Tests/Editor/UniTask.Tests.Editor.asmdef @@ -5,7 +5,8 @@ "UnityEditor.TestRunner", "UniTask.Tests", "UniTask", - "Unity.ResourceManager" + "Unity.ResourceManager", + "DOTween.Modules" ], "includePlatforms": [ "Editor" @@ -14,7 +15,8 @@ "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [ - "nunit.framework.dll" + "nunit.framework.dll", + "DOTween.dll" ], "autoReferenced": false, "defineConstraints": [ diff --git a/src/UniTask/Assets/Tests/UniTask.Tests.asmdef b/src/UniTask/Assets/Tests/UniTask.Tests.asmdef index 640851c..e54ef84 100644 --- a/src/UniTask/Assets/Tests/UniTask.Tests.asmdef +++ b/src/UniTask/Assets/Tests/UniTask.Tests.asmdef @@ -4,14 +4,16 @@ "UnityEngine.TestRunner", "UnityEditor.TestRunner", "UniTask", - "Unity.ResourceManager" + "Unity.ResourceManager", + "DOTween.Modules" ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": true, "precompiledReferences": [ - "nunit.framework.dll" + "nunit.framework.dll", + "DOTween.dll" ], "autoReferenced": false, "defineConstraints": [