TransitionPreviewPlayer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. namespace Animancer.Editor.Previews
  7. {
  8. /// <summary>[Editor-Only] Utility for playing through transition previews.</summary>
  9. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/TransitionPreviewPlayer
  10. [Serializable]
  11. public class TransitionPreviewPlayer : IDisposable
  12. {
  13. /************************************************************************************************************************/
  14. [SerializeReference] private ITransition _FromTransition;
  15. [SerializeReference] private ITransition _ToTransition;
  16. /// <summary>The animation to play first.</summary>
  17. public ref ITransition FromTransition
  18. => ref _FromTransition;
  19. /// <summary>The animation to transition into.</summary>
  20. public ref ITransition ToTransition
  21. => ref _ToTransition;
  22. /************************************************************************************************************************/
  23. private AnimancerGraph _Graph;
  24. /// <summary>The graph used to play the animations.</summary>
  25. public AnimancerGraph Graph
  26. {
  27. get => _Graph;
  28. set
  29. {
  30. _Graph = value;
  31. Evaluate();
  32. }
  33. }
  34. /************************************************************************************************************************/
  35. /// <summary>
  36. /// The minimum amount of time to play the <see cref="FromTransition"/>
  37. /// before the <see cref="ToTransition"/> starts (in seconds).
  38. /// </summary>
  39. public float FromDuration { get; set; }
  40. /// <summary>
  41. /// The minimum amount of time to continue playing
  42. /// after the <see cref="ToTransition"/> started (in seconds).
  43. /// </summary>
  44. public float ToDuration { get; set; }
  45. /// <summary>The speed at which the preview plays.</summary>
  46. public float Speed { get; set; } = 1;
  47. /************************************************************************************************************************/
  48. private float _FadeDuration = float.NaN;
  49. /// <summary>The <see cref="ITransition.FadeDuration"/>.</summary>
  50. /// <remarks><see cref="float.NaN"/> uses the value from the <see cref="ToTransition"/>.</remarks>
  51. public float FadeDuration
  52. {
  53. get => float.IsNaN(_FadeDuration) && ToTransition.IsValid()
  54. ? ToTransition.FadeDuration
  55. : _FadeDuration;
  56. set => _FadeDuration = value;
  57. }
  58. /************************************************************************************************************************/
  59. /// <summary>The lowest allowed <see cref="CurrentTime"/>.</summary>
  60. /// <remarks>
  61. /// This is the lower of the negative <see cref="FromDuration"/>
  62. /// or the negative duration of the <see cref="FromTransition"/>.
  63. /// </remarks>
  64. public float MinTime { get; private set; }
  65. /// <summary>The highest allowed <see cref="CurrentTime"/>.</summary>
  66. /// <remarks>
  67. /// This is the higher of the <see cref="ToDuration"/> or <see cref="FadeDuration"/>
  68. /// or the duration of the <see cref="ToTransition"/>.
  69. /// </remarks>
  70. public float MaxTime { get; private set; }
  71. /************************************************************************************************************************/
  72. /// <summary>Recalculated the <see cref="MinTime"/> and <see cref="MaxTime"/>.</summary>
  73. public void RecalculateTimeBounds()
  74. {
  75. MinTime = -FromDuration;
  76. if (_FromTransition.IsValid() &&
  77. AnimancerUtilities.TryCalculateDuration(_FromTransition, out var duration))
  78. MinTime = Math.Min(MinTime, -duration);
  79. MaxTime = ToDuration;
  80. var fadeDuration = FadeDuration;
  81. if (!float.IsNaN(fadeDuration))
  82. MaxTime = Math.Max(ToDuration, fadeDuration);
  83. if (_ToTransition.IsValid() &&
  84. AnimancerUtilities.TryCalculateDuration(_ToTransition, out duration))
  85. MaxTime = Math.Max(MaxTime, duration);
  86. }
  87. /************************************************************************************************************************/
  88. /// <summary>Converts normalized time to seconds.</summary>
  89. public float LerpTimeUnclamped(float normalizedTime)
  90. => Mathf.LerpUnclamped(MinTime, MaxTime, normalizedTime);
  91. /// <summary>Converts seconds to normalized time.</summary>
  92. public float InverseLerpTimeUnclamped(float time)
  93. => AnimancerUtilities.InverseLerpUnclamped(MinTime, MaxTime, time);
  94. /************************************************************************************************************************/
  95. private float _CurrentTime = float.NaN;
  96. /// <summary>The amount of time that has passed since the <see cref="ToTransition"/> started (in seconds).</summary>
  97. /// <remarks>This value goes from the <see cref="MinTime"/> to the <see cref="MaxTime"/>.</remarks>
  98. public float CurrentTime
  99. {
  100. get => float.IsNaN(_CurrentTime)
  101. ? MinTime
  102. : _CurrentTime;
  103. set
  104. {
  105. _CurrentTime = value;
  106. Evaluate();
  107. }
  108. }
  109. /// <summary>The amount of time that has passed since the <see cref="ToTransition"/> started (normalized).</summary>
  110. /// <remarks>0 is at the <see cref="MinTime"/> and 1 is at the <see cref="MaxTime"/>.</remarks>
  111. public float NormalizedTime
  112. {
  113. get => InverseLerpTimeUnclamped(CurrentTime);
  114. set => CurrentTime = LerpTimeUnclamped(value);
  115. }
  116. /************************************************************************************************************************/
  117. private bool _IsPlaying;
  118. /// <summary>Is the preview currently playing?</summary>
  119. public bool IsPlaying
  120. {
  121. get => _IsPlaying;
  122. set
  123. {
  124. if (_IsPlaying == value)
  125. return;
  126. _IsPlaying = value;
  127. if (_IsPlaying)
  128. {
  129. _LastUpdateTime = TimeSinceStartup;
  130. EditorApplication.update += Update;
  131. }
  132. else
  133. {
  134. EditorApplication.update -= Update;
  135. }
  136. }
  137. }
  138. /// <summary>Cleans up this player.</summary>
  139. public void Dispose()
  140. => IsPlaying = false;
  141. /************************************************************************************************************************/
  142. /// <summary><see cref="EditorApplication.timeSinceStartup"/></summary>
  143. private static double TimeSinceStartup
  144. => EditorApplication.timeSinceStartup;
  145. private double _LastUpdateTime;
  146. /// <summary>Updates the preview time while playing.</summary>
  147. private void Update()
  148. {
  149. if (Graph == null || !Graph.IsValidOrDispose())
  150. {
  151. EditorApplication.update -= Update;
  152. return;
  153. }
  154. var time = TimeSinceStartup;
  155. var deltaTime = time - _LastUpdateTime;
  156. _LastUpdateTime = time;
  157. CurrentTime += ((float)deltaTime) * Speed;
  158. AnimancerGUI.RepaintEverything();
  159. }
  160. /************************************************************************************************************************/
  161. /// <summary>Applies the animations at the <see cref="CurrentTime"/>.</summary>
  162. private void Evaluate()
  163. {
  164. if (Graph == null)
  165. return;
  166. Graph.PauseGraph();
  167. Graph.Stop();
  168. if (_FromTransition.IsValid())
  169. {
  170. if (_ToTransition.IsValid())
  171. {
  172. Apply(CurrentTime, _FromTransition, _ToTransition);
  173. }
  174. else
  175. {
  176. var minTime = MinTime;
  177. Apply(CurrentTime - minTime, -minTime, _FromTransition);
  178. }
  179. }
  180. else
  181. {
  182. if (_ToTransition.IsValid())
  183. Apply(CurrentTime, MaxTime, _ToTransition);
  184. else
  185. return;
  186. }
  187. Graph.Evaluate();
  188. }
  189. /************************************************************************************************************************/
  190. /// <summary>Applies the animations at the `currentTime`.</summary>
  191. private void Apply(float currentTime, ITransition from, ITransition to)
  192. {
  193. var layer = Graph.Layers[0];
  194. // Playing From.
  195. if (currentTime < 0)
  196. {
  197. var state = layer.Play(from);
  198. state.Time += state.NormalizedEndTime * state.Length + currentTime;
  199. return;
  200. }
  201. var maxTime = MaxTime;
  202. // Fading.
  203. if (currentTime < maxTime)
  204. {
  205. var state = layer.Play(from);
  206. state.Time += state.NormalizedEndTime * state.Length + currentTime;
  207. state = layer.Play(to, FadeDuration, to.FadeMode);
  208. state.Time += currentTime;
  209. var fade = state.FadeGroup;
  210. if (fade != null)
  211. {
  212. fade.NormalizedTime += currentTime * fade.FadeSpeed;
  213. fade.ApplyWeights();
  214. }
  215. }
  216. // Playing To.
  217. else
  218. {
  219. var state = layer.Play(to);
  220. state.Time += currentTime;
  221. // Finished.
  222. if (currentTime >= maxTime)
  223. {
  224. _CurrentTime = MinTime;
  225. state.Time = _CurrentTime;
  226. }
  227. }
  228. }
  229. /************************************************************************************************************************/
  230. /// <summary>Applies the animation at the `currentTime`.</summary>
  231. private void Apply(float currentTime, float endTime, ITransition transition)
  232. {
  233. var state = Graph.Layers[0].Play(transition);
  234. if (currentTime < endTime)// Playing.
  235. {
  236. state.Time = currentTime;
  237. }
  238. else// Finished.
  239. {
  240. _CurrentTime = MinTime;
  241. state.Time = _CurrentTime;
  242. }
  243. }
  244. /************************************************************************************************************************/
  245. }
  246. }
  247. #endif