DOTweenAsyncExtensions.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. // asmdef Version Defines, enabled when com.demigiant.dotween is imported.
  2. #if UNITASK_DOTWEEN_SUPPORT
  3. using Cysharp.Threading.Tasks.Internal;
  4. using DG.Tweening;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Runtime.CompilerServices;
  8. using System.Threading;
  9. namespace Cysharp.Threading.Tasks
  10. {
  11. public enum TweenCancelBehaviour
  12. {
  13. Kill,
  14. KillWithCompleteCallback,
  15. Complete,
  16. CompleteWithSequenceCallback,
  17. CancelAwait,
  18. // AndCancelAwait
  19. KillAndCancelAwait,
  20. KillWithCompleteCallbackAndCancelAwait,
  21. CompleteAndCancelAwait,
  22. CompleteWithSequenceCallbackAndCancelAwait
  23. }
  24. public static class DOTweenAsyncExtensions
  25. {
  26. enum CallbackType
  27. {
  28. Kill,
  29. Complete,
  30. Pause,
  31. Play,
  32. Rewind,
  33. StepComplete
  34. }
  35. public static TweenAwaiter GetAwaiter(this Tween tween)
  36. {
  37. return new TweenAwaiter(tween);
  38. }
  39. public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken)
  40. {
  41. Error.ThrowArgumentNullException(tween, nameof(tween));
  42. if (!tween.IsActive()) return UniTask.CompletedTask;
  43. return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token);
  44. }
  45. public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
  46. {
  47. Error.ThrowArgumentNullException(tween, nameof(tween));
  48. if (!tween.IsActive()) return UniTask.CompletedTask;
  49. return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token);
  50. }
  51. public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
  52. {
  53. Error.ThrowArgumentNullException(tween, nameof(tween));
  54. if (!tween.IsActive()) return UniTask.CompletedTask;
  55. return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token);
  56. }
  57. public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
  58. {
  59. Error.ThrowArgumentNullException(tween, nameof(tween));
  60. if (!tween.IsActive()) return UniTask.CompletedTask;
  61. return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token);
  62. }
  63. public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
  64. {
  65. Error.ThrowArgumentNullException(tween, nameof(tween));
  66. if (!tween.IsActive()) return UniTask.CompletedTask;
  67. return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token);
  68. }
  69. public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
  70. {
  71. Error.ThrowArgumentNullException(tween, nameof(tween));
  72. if (!tween.IsActive()) return UniTask.CompletedTask;
  73. return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token);
  74. }
  75. public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default)
  76. {
  77. Error.ThrowArgumentNullException(tween, nameof(tween));
  78. if (!tween.IsActive()) return UniTask.CompletedTask;
  79. return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token);
  80. }
  81. public struct TweenAwaiter : ICriticalNotifyCompletion
  82. {
  83. readonly Tween tween;
  84. // killed(non active) as completed.
  85. public bool IsCompleted => !tween.IsActive();
  86. public TweenAwaiter(Tween tween)
  87. {
  88. this.tween = tween;
  89. }
  90. public TweenAwaiter GetAwaiter()
  91. {
  92. return this;
  93. }
  94. public void GetResult()
  95. {
  96. }
  97. public void OnCompleted(System.Action continuation)
  98. {
  99. UnsafeOnCompleted(continuation);
  100. }
  101. public void UnsafeOnCompleted(System.Action continuation)
  102. {
  103. // onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true).
  104. tween.onKill = PooledTweenCallback.Create(continuation);
  105. }
  106. }
  107. sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource>
  108. {
  109. static TaskPool<TweenConfiguredSource> pool;
  110. TweenConfiguredSource nextNode;
  111. public ref TweenConfiguredSource NextNode => ref nextNode;
  112. static TweenConfiguredSource()
  113. {
  114. TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size);
  115. }
  116. readonly TweenCallback onCompleteCallbackDelegate;
  117. readonly TweenCallback onUpdateDelegate;
  118. Tween tween;
  119. TweenCancelBehaviour cancelBehaviour;
  120. CancellationToken cancellationToken;
  121. CallbackType callbackType;
  122. bool canceled;
  123. TweenCallback originalUpdateAction;
  124. TweenCallback originalCompleteAction;
  125. UniTaskCompletionSourceCore<AsyncUnit> core;
  126. TweenConfiguredSource()
  127. {
  128. onCompleteCallbackDelegate = OnCompleteCallbackDelegate;
  129. onUpdateDelegate = OnUpdate;
  130. }
  131. public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token)
  132. {
  133. if (cancellationToken.IsCancellationRequested)
  134. {
  135. DoCancelBeforeCreate(tween, cancelBehaviour);
  136. return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
  137. }
  138. if (!pool.TryPop(out var result))
  139. {
  140. result = new TweenConfiguredSource();
  141. }
  142. result.tween = tween;
  143. result.cancelBehaviour = cancelBehaviour;
  144. result.cancellationToken = cancellationToken;
  145. result.callbackType = callbackType;
  146. result.originalUpdateAction = tween.onUpdate;
  147. result.canceled = false;
  148. if (result.originalUpdateAction == result.onUpdateDelegate)
  149. {
  150. result.originalUpdateAction = null;
  151. }
  152. tween.onUpdate = result.onUpdateDelegate;
  153. switch (callbackType)
  154. {
  155. case CallbackType.Kill:
  156. result.originalCompleteAction = tween.onKill;
  157. tween.onKill = result.onCompleteCallbackDelegate;
  158. break;
  159. case CallbackType.Complete:
  160. result.originalCompleteAction = tween.onComplete;
  161. tween.onComplete = result.onCompleteCallbackDelegate;
  162. break;
  163. case CallbackType.Pause:
  164. result.originalCompleteAction = tween.onPause;
  165. tween.onPause = result.onCompleteCallbackDelegate;
  166. break;
  167. case CallbackType.Play:
  168. result.originalCompleteAction = tween.onPlay;
  169. tween.onPlay = result.onCompleteCallbackDelegate;
  170. break;
  171. case CallbackType.Rewind:
  172. result.originalCompleteAction = tween.onRewind;
  173. tween.onRewind = result.onCompleteCallbackDelegate;
  174. break;
  175. case CallbackType.StepComplete:
  176. result.originalCompleteAction = tween.onStepComplete;
  177. tween.onStepComplete = result.onCompleteCallbackDelegate;
  178. break;
  179. default:
  180. break;
  181. }
  182. if (result.originalCompleteAction == result.onCompleteCallbackDelegate)
  183. {
  184. result.originalCompleteAction = null;
  185. }
  186. TaskTracker.TrackActiveTask(result, 3);
  187. token = result.core.Version;
  188. return result;
  189. }
  190. void OnCompleteCallbackDelegate()
  191. {
  192. if (cancellationToken.IsCancellationRequested)
  193. {
  194. if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait
  195. || this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait
  196. || this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait
  197. || this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait
  198. || this.cancelBehaviour == TweenCancelBehaviour.CancelAwait)
  199. {
  200. canceled = true;
  201. }
  202. }
  203. if (canceled)
  204. {
  205. core.TrySetCanceled(cancellationToken);
  206. }
  207. else
  208. {
  209. originalCompleteAction?.Invoke();
  210. core.TrySetResult(AsyncUnit.Default);
  211. }
  212. }
  213. void OnUpdate()
  214. {
  215. originalUpdateAction?.Invoke();
  216. if (!cancellationToken.IsCancellationRequested)
  217. {
  218. return;
  219. }
  220. switch (this.cancelBehaviour)
  221. {
  222. case TweenCancelBehaviour.Kill:
  223. default:
  224. this.tween.Kill(false);
  225. break;
  226. case TweenCancelBehaviour.KillAndCancelAwait:
  227. this.canceled = true;
  228. this.tween.Kill(false);
  229. break;
  230. case TweenCancelBehaviour.KillWithCompleteCallback:
  231. this.tween.Kill(true);
  232. break;
  233. case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
  234. this.canceled = true;
  235. this.tween.Kill(true);
  236. break;
  237. case TweenCancelBehaviour.Complete:
  238. this.tween.Complete(false);
  239. break;
  240. case TweenCancelBehaviour.CompleteAndCancelAwait:
  241. this.canceled = true;
  242. this.tween.Complete(false);
  243. break;
  244. case TweenCancelBehaviour.CompleteWithSequenceCallback:
  245. this.tween.Complete(true);
  246. break;
  247. case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
  248. this.canceled = true;
  249. this.tween.Complete(true);
  250. break;
  251. case TweenCancelBehaviour.CancelAwait:
  252. // restore to original callback
  253. switch (callbackType)
  254. {
  255. case CallbackType.Kill:
  256. tween.onKill = originalCompleteAction;
  257. break;
  258. case CallbackType.Complete:
  259. tween.onComplete = originalCompleteAction;
  260. break;
  261. case CallbackType.Pause:
  262. tween.onPause = originalCompleteAction;
  263. break;
  264. case CallbackType.Play:
  265. tween.onPlay = originalCompleteAction;
  266. break;
  267. case CallbackType.Rewind:
  268. tween.onRewind = originalCompleteAction;
  269. break;
  270. case CallbackType.StepComplete:
  271. tween.onStepComplete = originalCompleteAction;
  272. break;
  273. default:
  274. break;
  275. }
  276. this.core.TrySetCanceled(this.cancellationToken);
  277. break;
  278. }
  279. }
  280. static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour)
  281. {
  282. switch (tweenCancelBehaviour)
  283. {
  284. case TweenCancelBehaviour.Kill:
  285. default:
  286. tween.Kill(false);
  287. break;
  288. case TweenCancelBehaviour.KillAndCancelAwait:
  289. tween.Kill(false);
  290. break;
  291. case TweenCancelBehaviour.KillWithCompleteCallback:
  292. tween.Kill(true);
  293. break;
  294. case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait:
  295. tween.Kill(true);
  296. break;
  297. case TweenCancelBehaviour.Complete:
  298. tween.Complete(false);
  299. break;
  300. case TweenCancelBehaviour.CompleteAndCancelAwait:
  301. tween.Complete(false);
  302. break;
  303. case TweenCancelBehaviour.CompleteWithSequenceCallback:
  304. tween.Complete(true);
  305. break;
  306. case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait:
  307. tween.Complete(true);
  308. break;
  309. case TweenCancelBehaviour.CancelAwait:
  310. break;
  311. }
  312. }
  313. public void GetResult(short token)
  314. {
  315. try
  316. {
  317. core.GetResult(token);
  318. }
  319. finally
  320. {
  321. TryReturn();
  322. }
  323. }
  324. public UniTaskStatus GetStatus(short token)
  325. {
  326. return core.GetStatus(token);
  327. }
  328. public UniTaskStatus UnsafeGetStatus()
  329. {
  330. return core.UnsafeGetStatus();
  331. }
  332. public void OnCompleted(Action<object> continuation, object state, short token)
  333. {
  334. core.OnCompleted(continuation, state, token);
  335. }
  336. bool TryReturn()
  337. {
  338. TaskTracker.RemoveTracking(this);
  339. core.Reset();
  340. tween.onUpdate = originalUpdateAction;
  341. switch (callbackType)
  342. {
  343. case CallbackType.Kill:
  344. tween.onKill = originalCompleteAction;
  345. break;
  346. case CallbackType.Complete:
  347. tween.onComplete = originalCompleteAction;
  348. break;
  349. case CallbackType.Pause:
  350. tween.onPause = originalCompleteAction;
  351. break;
  352. case CallbackType.Play:
  353. tween.onPlay = originalCompleteAction;
  354. break;
  355. case CallbackType.Rewind:
  356. tween.onRewind = originalCompleteAction;
  357. break;
  358. case CallbackType.StepComplete:
  359. tween.onStepComplete = originalCompleteAction;
  360. break;
  361. default:
  362. break;
  363. }
  364. tween = default;
  365. cancellationToken = default;
  366. originalUpdateAction = default;
  367. originalCompleteAction = default;
  368. return pool.TryPush(this);
  369. }
  370. }
  371. }
  372. sealed class PooledTweenCallback
  373. {
  374. static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>();
  375. readonly TweenCallback runDelegate;
  376. Action continuation;
  377. PooledTweenCallback()
  378. {
  379. runDelegate = Run;
  380. }
  381. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  382. public static TweenCallback Create(Action continuation)
  383. {
  384. if (!pool.TryDequeue(out var item))
  385. {
  386. item = new PooledTweenCallback();
  387. }
  388. item.continuation = continuation;
  389. return item.runDelegate;
  390. }
  391. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  392. void Run()
  393. {
  394. var call = continuation;
  395. continuation = null;
  396. if (call != null)
  397. {
  398. pool.Enqueue(this);
  399. call.Invoke();
  400. }
  401. }
  402. }
  403. }
  404. #endif