AnimancerComponent.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using UnityEngine;
  6. using UnityEngine.Playables;
  7. namespace Animancer
  8. {
  9. /// <summary>
  10. /// The main component through which other scripts can interact with <see cref="Animancer"/>. It allows you to play
  11. /// animations on an <see cref="UnityEngine.Animator"/> without using a <see cref="RuntimeAnimatorController"/>.
  12. /// </summary>
  13. /// <remarks>
  14. /// This class can be used as a custom yield instruction to wait until all animations finish playing.
  15. /// <para></para>
  16. /// This class is mostly just a wrapper that connects an <see cref="AnimancerPlayable"/> to an
  17. /// <see cref="UnityEngine.Animator"/>.
  18. /// <para></para>
  19. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/playing/component-types">Component Types</see>
  20. /// </remarks>
  21. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerComponent
  22. ///
  23. [AddComponentMenu(Strings.MenuPrefix + "Animancer Component")]
  24. [HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(AnimancerComponent))]
  25. [DefaultExecutionOrder(DefaultExecutionOrder)]
  26. public class AnimancerComponent : MonoBehaviour,
  27. IAnimancerComponent, IEnumerator, IAnimationClipSource, IAnimationClipCollection
  28. {
  29. /************************************************************************************************************************/
  30. #region Fields and Properties
  31. /************************************************************************************************************************/
  32. /// <summary>Initialize before anything else tries to use this component.</summary>
  33. public const int DefaultExecutionOrder = -5000;
  34. /************************************************************************************************************************/
  35. [SerializeField, Tooltip("The Animator component which this script controls")]
  36. private Animator _Animator;
  37. /// <summary>[<see cref="SerializeField"/>]
  38. /// The <see cref="UnityEngine.Animator"/> component which this script controls.
  39. /// </summary>
  40. public Animator Animator
  41. {
  42. get => _Animator;
  43. set
  44. {
  45. _Animator = value;
  46. if (IsPlayableInitialized)
  47. {
  48. _Playable.DestroyOutput();
  49. _Playable.CreateOutput(value, this);
  50. }
  51. }
  52. }
  53. #if UNITY_EDITOR
  54. /// <summary>[Editor-Only] The name of the serialized backing field for the <see cref="Animator"/> property.</summary>
  55. string IAnimancerComponent.AnimatorFieldName => nameof(_Animator);
  56. #endif
  57. /************************************************************************************************************************/
  58. private AnimancerPlayable _Playable;
  59. /// <summary>
  60. /// The internal system which manages the playing animations.
  61. /// Accessing this property will automatically initialize it.
  62. /// </summary>
  63. public AnimancerPlayable Playable
  64. {
  65. get
  66. {
  67. InitializePlayable();
  68. return _Playable;
  69. }
  70. }
  71. /// <summary>Indicates whether the <see cref="Playable"/> has been initialized.</summary>
  72. public bool IsPlayableInitialized => _Playable != null && _Playable.IsValid;
  73. /************************************************************************************************************************/
  74. /// <summary>The states managed by this component.</summary>
  75. public AnimancerPlayable.StateDictionary States => Playable.States;
  76. /// <summary>The layers which each manage their own set of animations.</summary>
  77. public AnimancerPlayable.LayerList Layers => Playable.Layers;
  78. /// <summary>Returns the <see cref="Playable"/>.</summary>
  79. public static implicit operator AnimancerPlayable(AnimancerComponent animancer) => animancer.Playable;
  80. /// <summary>Returns layer 0.</summary>
  81. public static implicit operator AnimancerLayer(AnimancerComponent animancer) => animancer.Playable.Layers[0];
  82. /************************************************************************************************************************/
  83. [SerializeField, Tooltip("Determines what happens when this component is disabled" +
  84. " or its " + nameof(GameObject) + " becomes inactive (i.e. in " + nameof(OnDisable) + "):" +
  85. "\n• " + nameof(DisableAction.Stop) + " all animations" +
  86. "\n• " + nameof(DisableAction.Pause) + " all animations" +
  87. "\n• " + nameof(DisableAction.Continue) + " playing" +
  88. "\n• " + nameof(DisableAction.Reset) + " to the original values" +
  89. "\n• " + nameof(DisableAction.Destroy) + " all layers and states")]
  90. private DisableAction _ActionOnDisable;
  91. #if UNITY_EDITOR
  92. /// <summary>[Editor-Only] The name of the serialized backing field for the <see cref="ActionOnDisable"/> property.</summary>
  93. string IAnimancerComponent.ActionOnDisableFieldName => nameof(_ActionOnDisable);
  94. #endif
  95. /// <summary>[<see cref="SerializeField"/>]
  96. /// Determines what happens when this component is disabled or its <see cref="GameObject"/> becomes inactive
  97. /// (i.e. in <see cref="OnDisable"/>).
  98. /// </summary>
  99. /// <remarks>The default value is <see cref="DisableAction.Stop"/>.</remarks>
  100. public ref DisableAction ActionOnDisable => ref _ActionOnDisable;
  101. /// <inheritdoc/>
  102. bool IAnimancerComponent.ResetOnDisable => _ActionOnDisable == DisableAction.Reset;
  103. /// <summary>
  104. /// An action to perform when disabling an <see cref="AnimancerComponent"/>. See <see cref="ActionOnDisable"/>.
  105. /// </summary>
  106. public enum DisableAction
  107. {
  108. /// <summary>
  109. /// Stop all animations and rewind them, but leave all animated values as they are (unlike
  110. /// <see cref="Reset"/>).
  111. /// </summary>
  112. /// <remarks>Calls <see cref="Stop()"/> and <see cref="AnimancerPlayable.PauseGraph"/>.</remarks>
  113. Stop,
  114. /// <summary>Pause all animations in their current state so they can resume later.</summary>
  115. /// <remarks>Calls <see cref="AnimancerPlayable.PauseGraph"/>.</remarks>
  116. Pause,
  117. /// <summary>Keep playing while inactive.</summary>
  118. Continue,
  119. /// <summary>
  120. /// Stop all animations, rewind them, and force the object back into its original state (often called the
  121. /// bind pose).
  122. /// </summary>
  123. /// <remarks>
  124. /// The <see cref="AnimancerComponent"/> must be either above the <see cref="UnityEngine.Animator"/> in
  125. /// the Inspector or on a child object so that so that this <see cref="OnDisable"/> gets called first.
  126. /// <para></para>
  127. /// Calls <see cref="Stop()"/>, <see cref="Animator.Rebind"/>, and <see cref="AnimancerPlayable.PauseGraph"/>.
  128. /// </remarks>
  129. Reset,
  130. /// <summary>
  131. /// Destroy the <see cref="PlayableGraph"/> and all its layers and states. This means that any layers or
  132. /// states referenced by other scripts will no longer be valid so they will need to be recreated if you
  133. /// want to use this object again.
  134. /// </summary>
  135. /// <remarks>Calls <see cref="AnimancerPlayable.DestroyGraph()"/>.</remarks>
  136. Destroy,
  137. }
  138. /************************************************************************************************************************/
  139. #region Update Mode
  140. /************************************************************************************************************************/
  141. /// <summary>
  142. /// Determines when animations are updated and which time source is used. This property is mainly a wrapper
  143. /// around the <see cref="Animator.updateMode"/>.
  144. /// </summary>
  145. /// <remarks>Note that changing to or from <see cref="AnimatorUpdateMode.AnimatePhysics"/> at runtime has no effect.</remarks>
  146. /// <exception cref="NullReferenceException">No <see cref="Animator"/> is assigned.</exception>
  147. public AnimatorUpdateMode UpdateMode
  148. {
  149. get => _Animator.updateMode;
  150. set
  151. {
  152. _Animator.updateMode = value;
  153. if (!IsPlayableInitialized)
  154. return;
  155. // UnscaledTime on the Animator is actually identical to Normal when using the Playables API so we need
  156. // to set the graph's DirectorUpdateMode to determine how it gets its delta time.
  157. _Playable.UpdateMode = value == AnimatorUpdateMode.UnscaledTime ?
  158. DirectorUpdateMode.UnscaledGameTime :
  159. DirectorUpdateMode.GameTime;
  160. #if UNITY_EDITOR
  161. if (InitialUpdateMode == null)
  162. {
  163. InitialUpdateMode = value;
  164. }
  165. else if (UnityEditor.EditorApplication.isPlaying)
  166. {
  167. if (AnimancerPlayable.HasChangedToOrFromAnimatePhysics(InitialUpdateMode, value))
  168. Debug.LogWarning($"Changing the {nameof(Animator)}.{nameof(Animator.updateMode)}" +
  169. $" to or from {nameof(AnimatorUpdateMode.AnimatePhysics)} at runtime will have no effect." +
  170. " You must set it in the Unity Editor or on startup.", this);
  171. }
  172. #endif
  173. }
  174. }
  175. /************************************************************************************************************************/
  176. #if UNITY_EDITOR
  177. /// <inheritdoc/>
  178. public AnimatorUpdateMode? InitialUpdateMode { get; private set; }
  179. #endif
  180. /************************************************************************************************************************/
  181. #endregion
  182. /************************************************************************************************************************/
  183. #endregion
  184. /************************************************************************************************************************/
  185. #region Initialization
  186. /************************************************************************************************************************/
  187. #if UNITY_EDITOR
  188. /// <summary>[Editor-Only]
  189. /// Destroys the <see cref="Playable"/> if it was initialized and searches for an <see cref="Animator"/> on
  190. /// this object, or it's children or parents.
  191. /// </summary>
  192. protected virtual void Reset()
  193. {
  194. OnDestroy();
  195. gameObject.GetComponentInParentOrChildren(ref _Animator);
  196. }
  197. #endif
  198. /************************************************************************************************************************/
  199. /// <summary>Ensures that the <see cref="PlayableGraph"/> is playing.</summary>
  200. protected virtual void OnEnable()
  201. {
  202. if (IsPlayableInitialized)
  203. _Playable.UnpauseGraph();
  204. }
  205. /// <summary>Acts according to the <see cref="ActionOnDisable"/>.</summary>
  206. protected virtual void OnDisable()
  207. {
  208. if (!IsPlayableInitialized)
  209. return;
  210. switch (_ActionOnDisable)
  211. {
  212. case DisableAction.Stop:
  213. Stop();
  214. _Playable.PauseGraph();
  215. break;
  216. case DisableAction.Pause:
  217. _Playable.PauseGraph();
  218. break;
  219. case DisableAction.Continue:
  220. break;
  221. case DisableAction.Reset:
  222. Debug.Assert(_Animator.isActiveAndEnabled,
  223. $"{nameof(DisableAction)}.{nameof(DisableAction.Reset)} failed because the {nameof(Animator)} is not enabled." +
  224. $" This most likely means you are disabling the {nameof(GameObject)} and the {nameof(Animator)} is above the" +
  225. $" {nameof(AnimancerComponent)} in the Inspector so it got disabled right before this method was called." +
  226. $" See the Inspector of {this} to fix the issue" +
  227. $" or use {nameof(DisableAction)}.{nameof(DisableAction.Stop)}" +
  228. $" and call {nameof(Animator)}.{nameof(Animator.Rebind)} manually" +
  229. $" before disabling the {nameof(GameObject)}.",
  230. this);
  231. Stop();
  232. _Animator.Rebind();
  233. _Playable.PauseGraph();
  234. break;
  235. case DisableAction.Destroy:
  236. _Playable.DestroyGraph();
  237. _Playable = null;
  238. break;
  239. default:
  240. throw new ArgumentOutOfRangeException(nameof(ActionOnDisable));
  241. }
  242. }
  243. /************************************************************************************************************************/
  244. /// <summary>Creates a new <see cref="AnimancerPlayable"/> if it doesn't already exist.</summary>
  245. public void InitializePlayable()
  246. {
  247. if (IsPlayableInitialized)
  248. return;
  249. if (_Animator == null)
  250. _Animator = GetComponent<Animator>();
  251. #if UNITY_ASSERTIONS
  252. ValidatePlayableInitialization();
  253. #endif
  254. AnimancerPlayable.SetNextGraphName(name + " (Animancer)");
  255. _Playable = AnimancerPlayable.Create();
  256. _Playable.CreateOutput(_Animator, this);
  257. #if UNITY_EDITOR
  258. if (_Animator != null)
  259. InitialUpdateMode = UpdateMode;
  260. #endif
  261. }
  262. /************************************************************************************************************************/
  263. /// <summary>Creates a new <see cref="AnimancerPlayable"/> in the specified `graph`.</summary>
  264. /// <exception cref="InvalidOperationException">
  265. /// The <see cref="AnimancerPlayable"/> is already initialized.
  266. /// You must call <see cref="AnimancerPlayable.DestroyGraph"/> before re-initializing it.
  267. /// </exception>
  268. public void InitializePlayable(PlayableGraph graph)
  269. {
  270. if (IsPlayableInitialized)
  271. throw new InvalidOperationException($"The {nameof(AnimancerPlayable)} is already initialized." +
  272. $" Either call this method before anything else uses it or call" +
  273. $" animancerComponent.{nameof(Playable)}.{nameof(AnimancerPlayable.DestroyGraph)} before re-initializing it.");
  274. if (_Animator == null)
  275. _Animator = GetComponent<Animator>();
  276. #if UNITY_ASSERTIONS
  277. ValidatePlayableInitialization();
  278. #endif
  279. _Playable = AnimancerPlayable.Create(graph);
  280. _Playable.CreateOutput(_Animator, this);
  281. #if UNITY_EDITOR
  282. if (_Animator != null)
  283. InitialUpdateMode = UpdateMode;
  284. #endif
  285. }
  286. /************************************************************************************************************************/
  287. #if UNITY_ASSERTIONS
  288. /// <summary>Validates various conditions relating to <see cref="AnimancerPlayable"/> initialization.</summary>
  289. private void ValidatePlayableInitialization()
  290. {
  291. #if UNITY_EDITOR
  292. if (OptionalWarning.CreateGraphDuringGuiEvent.IsEnabled())
  293. {
  294. var currentEvent = Event.current;
  295. if (currentEvent != null && (currentEvent.type == EventType.Layout || currentEvent.type == EventType.Repaint))
  296. OptionalWarning.CreateGraphDuringGuiEvent.Log(
  297. $"An {nameof(AnimancerPlayable)} is being created during a {currentEvent.type} event" +
  298. $" which is likely undesirable.", this);
  299. }
  300. if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  301. #endif
  302. {
  303. if (!gameObject.activeInHierarchy)
  304. OptionalWarning.CreateGraphWhileDisabled.Log($"An {nameof(AnimancerPlayable)} is being created for '{this}'" +
  305. $" which is attached to an inactive {nameof(GameObject)}." +
  306. $" If that object is never activated then Unity will not call {nameof(OnDestroy)}" +
  307. $" so {nameof(AnimancerPlayable)}.{nameof(AnimancerPlayable.DestroyGraph)} will need to be called manually.", this);
  308. }
  309. if (_Animator != null && _Animator.isHuman && _Animator.runtimeAnimatorController != null)
  310. OptionalWarning.NativeControllerHumanoid.Log($"An Animator Controller is assigned to the" +
  311. $" {nameof(Animator)} component but the Rig is Humanoid so it can't be blended with Animancer." +
  312. $" See the documentation for more information: {Strings.DocsURLs.AnimatorControllersNative}", this);
  313. }
  314. #endif
  315. /************************************************************************************************************************/
  316. /// <summary>Ensures that the <see cref="Playable"/> is properly cleaned up.</summary>
  317. protected virtual void OnDestroy()
  318. {
  319. if (IsPlayableInitialized)
  320. {
  321. _Playable.DestroyGraph();
  322. _Playable = null;
  323. }
  324. }
  325. /************************************************************************************************************************/
  326. #if UNITY_EDITOR
  327. /// <summary>[Editor-Only]
  328. /// Ensures that the <see cref="AnimancerPlayable"/> is destroyed in Edit Mode, but not in Play Mode since we want
  329. /// to let Unity complain if that happens.
  330. /// </summary>
  331. ~AnimancerComponent()
  332. {
  333. if (_Playable != null)
  334. {
  335. UnityEditor.EditorApplication.delayCall += () =>
  336. {
  337. if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
  338. OnDestroy();
  339. };
  340. }
  341. }
  342. #endif
  343. /************************************************************************************************************************/
  344. #endregion
  345. /************************************************************************************************************************/
  346. #region Play Management
  347. /************************************************************************************************************************/
  348. /// <summary>Returns the `clip` itself.</summary>
  349. /// <remarks>
  350. /// This method is used to determine the dictionary key to use for an animation when none is specified by the
  351. /// caller, such as in <see cref="Play(AnimationClip)"/>.
  352. /// </remarks>
  353. public virtual object GetKey(AnimationClip clip) => clip;
  354. /************************************************************************************************************************/
  355. // Play Immediately.
  356. /************************************************************************************************************************/
  357. /// <summary>Stops all other animations on the same layer, plays the `clip`, and returns its state.</summary>
  358. /// <remarks>
  359. /// The animation will continue playing from its current <see cref="AnimancerState.Time"/>.
  360. /// To restart it from the beginning you can use <c>...Play(clip).Time = 0;</c>.
  361. /// <para></para>
  362. /// This method is safe to call repeatedly without checking whether the `clip` was already playing.
  363. /// </remarks>
  364. public AnimancerState Play(AnimationClip clip)
  365. => Playable.Play(States.GetOrCreate(clip));
  366. /// <summary>Stops all other animations on the same layer, plays the `state`, and returns it.</summary>
  367. /// <remarks>
  368. /// The animation will continue playing from its current <see cref="AnimancerState.Time"/>.
  369. /// To restart it from the beginning you can use <c>...Play(state).Time = 0;</c>.
  370. /// <para></para>
  371. /// This method is safe to call repeatedly without checking whether the `state` was already playing.
  372. /// </remarks>
  373. public AnimancerState Play(AnimancerState state)
  374. => Playable.Play(state);
  375. /************************************************************************************************************************/
  376. // Cross Fade.
  377. /************************************************************************************************************************/
  378. /// <summary>
  379. /// Starts fading in the `clip` while fading out all other states in the same layer over the course of the
  380. /// `fadeDuration`. Returns its state.
  381. /// </summary>
  382. /// <remarks>
  383. /// If the `state` was already playing and fading in with less time remaining than the `fadeDuration`, this
  384. /// method will allow it to complete the existing fade rather than starting a slower one.
  385. /// <para></para>
  386. /// If the layer currently has 0 <see cref="AnimancerNode.Weight"/>, this method will fade in the layer itself
  387. /// and simply <see cref="AnimancerState.Play"/> the `state`.
  388. /// <para></para>
  389. /// This method is safe to call repeatedly without checking whether the `clip` was already playing.
  390. /// <para></para>
  391. /// <em>Animancer Lite only allows the default `fadeDuration` (0.25 seconds) in runtime builds.</em>
  392. /// </remarks>
  393. public AnimancerState Play(AnimationClip clip, float fadeDuration, FadeMode mode = default)
  394. => Playable.Play(States.GetOrCreate(clip), fadeDuration, mode);
  395. /// <summary>
  396. /// Starts fading in the `state` while fading out all others in the same layer over the course of the
  397. /// `fadeDuration`. Returns the `state`.
  398. /// </summary>
  399. /// <remarks>
  400. /// If the `state` was already playing and fading in with less time remaining than the `fadeDuration`, this
  401. /// method will allow it to complete the existing fade rather than starting a slower one.
  402. /// <para></para>
  403. /// If the layer currently has 0 <see cref="AnimancerNode.Weight"/>, this method will fade in the layer itself
  404. /// and simply <see cref="AnimancerState.Play"/> the `state`.
  405. /// <para></para>
  406. /// This method is safe to call repeatedly without checking whether the `state` was already playing.
  407. /// <para></para>
  408. /// <em>Animancer Lite only allows the default `fadeDuration` (0.25 seconds) in runtime builds.</em>
  409. /// </remarks>
  410. public AnimancerState Play(AnimancerState state, float fadeDuration, FadeMode mode = default)
  411. => Playable.Play(state, fadeDuration, mode);
  412. /************************************************************************************************************************/
  413. // Transition.
  414. /************************************************************************************************************************/
  415. /// <summary>
  416. /// Creates a state for the `transition` if it didn't already exist, then calls
  417. /// <see cref="Play(AnimancerState)"/> or <see cref="Play(AnimancerState, float, FadeMode)"/>
  418. /// depending on <see cref="ITransition.CrossFadeFromStart"/>.
  419. /// </summary>
  420. /// <remarks>
  421. /// This method is safe to call repeatedly without checking whether the `transition` was already playing.
  422. /// </remarks>
  423. public AnimancerState Play(ITransition transition)
  424. => Playable.Play(transition);
  425. /// <summary>
  426. /// Creates a state for the `transition` if it didn't already exist, then calls
  427. /// <see cref="Play(AnimancerState)"/> or <see cref="Play(AnimancerState, float, FadeMode)"/>
  428. /// depending on <see cref="ITransition.CrossFadeFromStart"/>.
  429. /// </summary>
  430. /// <remarks>
  431. /// This method is safe to call repeatedly without checking whether the `transition` was already playing.
  432. /// </remarks>
  433. public AnimancerState Play(ITransition transition, float fadeDuration, FadeMode mode = default)
  434. => Playable.Play(transition, fadeDuration, mode);
  435. /************************************************************************************************************************/
  436. // Try Play.
  437. /************************************************************************************************************************/
  438. /// <summary>
  439. /// Stops all other animations on the same layer, plays the animation registered with the `key`, and returns
  440. /// that state. Or if no state is registered with that `key`, this method does nothing and returns null.
  441. /// </summary>
  442. /// <remarks>
  443. /// The animation will continue playing from its current <see cref="AnimancerState.Time"/>.
  444. /// If you wish to force it back to the start, you can simply set the returned state's time to 0.
  445. /// <para></para>
  446. /// This method is safe to call repeatedly without checking whether the animation was already playing.
  447. /// </remarks>
  448. /// <exception cref="ArgumentNullException">The `key` is null.</exception>
  449. public AnimancerState TryPlay(object key)
  450. => Playable.TryPlay(key);
  451. /// <summary>
  452. /// Starts fading in the animation registered with the `key` while fading out all others in the same layer
  453. /// over the course of the `fadeDuration`. Or if no state is registered with that `key`, this method does
  454. /// nothing and returns null.
  455. /// </summary>
  456. /// <remarks>
  457. /// If the `state` was already playing and fading in with less time remaining than the `fadeDuration`, this
  458. /// method will allow it to complete the existing fade rather than starting a slower one.
  459. /// <para></para>
  460. /// If the layer currently has 0 <see cref="AnimancerNode.Weight"/>, this method will fade in the layer itself
  461. /// and simply <see cref="AnimancerState.Play"/> the `state`.
  462. /// <para></para>
  463. /// This method is safe to call repeatedly without checking whether the animation was already playing.
  464. /// <para></para>
  465. /// <em>Animancer Lite only allows the default `fadeDuration` (0.25 seconds) in runtime builds.</em>
  466. /// </remarks>
  467. /// <exception cref="ArgumentNullException">The `key` is null.</exception>
  468. public AnimancerState TryPlay(object key, float fadeDuration, FadeMode mode = default)
  469. => Playable.TryPlay(key, fadeDuration, mode);
  470. /************************************************************************************************************************/
  471. /// <summary>
  472. /// Gets the state associated with the `clip`, stops and rewinds it to the start, then returns it.
  473. /// </summary>
  474. public AnimancerState Stop(AnimationClip clip) => Stop(GetKey(clip));
  475. /// <summary>
  476. /// Gets the state registered with the <see cref="IHasKey.Key"/>, stops and rewinds it to the start, then
  477. /// returns it.
  478. /// </summary>
  479. public AnimancerState Stop(IHasKey hasKey) => _Playable?.Stop(hasKey);
  480. /// <summary>
  481. /// Gets the state associated with the `key`, stops and rewinds it to the start, then returns it.
  482. /// </summary>
  483. public AnimancerState Stop(object key) => _Playable?.Stop(key);
  484. /// <summary>
  485. /// Stops all animations and rewinds them to the start.
  486. /// </summary>
  487. public void Stop()
  488. {
  489. if (_Playable != null)
  490. _Playable.Stop();
  491. }
  492. /************************************************************************************************************************/
  493. /// <summary>
  494. /// Returns true if a state is registered for the `clip` and it is currently playing.
  495. /// <para></para>
  496. /// The actual dictionary key is determined using <see cref="GetKey"/>.
  497. /// </summary>
  498. public bool IsPlaying(AnimationClip clip) => IsPlaying(GetKey(clip));
  499. /// <summary>
  500. /// Returns true if a state is registered with the <see cref="IHasKey.Key"/> and it is currently playing.
  501. /// </summary>
  502. public bool IsPlaying(IHasKey hasKey) => _Playable != null && _Playable.IsPlaying(hasKey);
  503. /// <summary>
  504. /// Returns true if a state is registered with the `key` and it is currently playing.
  505. /// </summary>
  506. public bool IsPlaying(object key) => _Playable != null && _Playable.IsPlaying(key);
  507. /// <summary>
  508. /// Returns true if at least one animation is being played.
  509. /// </summary>
  510. public bool IsPlaying() => _Playable != null && _Playable.IsPlaying();
  511. /************************************************************************************************************************/
  512. /// <summary>
  513. /// Returns true if the `clip` is currently being played by at least one state.
  514. /// <para></para>
  515. /// This method is inefficient because it searches through every state to find any that are playing the `clip`,
  516. /// unlike <see cref="IsPlaying(AnimationClip)"/> which only checks the state registered using the `clip`s key.
  517. /// </summary>
  518. public bool IsPlayingClip(AnimationClip clip) => _Playable != null && _Playable.IsPlayingClip(clip);
  519. /************************************************************************************************************************/
  520. /// <summary>
  521. /// Evaluates all of the currently playing animations to apply their states to the animated objects.
  522. /// </summary>
  523. public void Evaluate() => Playable.Evaluate();
  524. /// <summary>
  525. /// Advances all currently playing animations by the specified amount of time (in seconds) and evaluates the
  526. /// graph to apply their states to the animated objects.
  527. /// </summary>
  528. public void Evaluate(float deltaTime) => Playable.Evaluate(deltaTime);
  529. /************************************************************************************************************************/
  530. #region Key Error Methods
  531. #if UNITY_EDITOR
  532. /************************************************************************************************************************/
  533. // These are overloads of other methods that take a System.Object key to ensure the user doesn't try to use an
  534. // AnimancerState as a key, since the whole point of a key is to identify a state in the first place.
  535. /************************************************************************************************************************/
  536. /// <summary>[Warning]
  537. /// You should not use an <see cref="AnimancerState"/> as a key.
  538. /// Just call <see cref="AnimancerState.Stop"/>.
  539. /// </summary>
  540. [Obsolete("You should not use an AnimancerState as a key. Just call AnimancerState.Stop().", true)]
  541. public AnimancerState Stop(AnimancerState key)
  542. {
  543. key.Stop();
  544. return key;
  545. }
  546. /// <summary>[Warning]
  547. /// You should not use an <see cref="AnimancerState"/> as a key.
  548. /// Just check <see cref="AnimancerState.IsPlaying"/>.
  549. /// </summary>
  550. [Obsolete("You should not use an AnimancerState as a key. Just check AnimancerState.IsPlaying.", true)]
  551. public bool IsPlaying(AnimancerState key) => key.IsPlaying;
  552. /************************************************************************************************************************/
  553. #endif
  554. #endregion
  555. /************************************************************************************************************************/
  556. #endregion
  557. /************************************************************************************************************************/
  558. #region Enumeration
  559. /************************************************************************************************************************/
  560. // IEnumerator for yielding in a coroutine to wait until all animations have stopped.
  561. /************************************************************************************************************************/
  562. /// <summary>
  563. /// Determines if any animations are still playing so this object can be used as a custom yield instruction.
  564. /// </summary>
  565. bool IEnumerator.MoveNext()
  566. {
  567. if (!IsPlayableInitialized)
  568. return false;
  569. return ((IEnumerator)_Playable).MoveNext();
  570. }
  571. /// <summary>Returns null.</summary>
  572. object IEnumerator.Current => null;
  573. /// <summary>Does nothing.</summary>
  574. void IEnumerator.Reset() { }
  575. /************************************************************************************************************************/
  576. /// <summary>[<see cref="IAnimationClipSource"/>]
  577. /// Calls <see cref="GatherAnimationClips(ICollection{AnimationClip})"/>.
  578. /// </summary>
  579. public void GetAnimationClips(List<AnimationClip> clips)
  580. {
  581. var set = ObjectPool.AcquireSet<AnimationClip>();
  582. set.UnionWith(clips);
  583. GatherAnimationClips(set);
  584. clips.Clear();
  585. clips.AddRange(set);
  586. ObjectPool.Release(set);
  587. }
  588. /************************************************************************************************************************/
  589. /// <summary>[<see cref="IAnimationClipCollection"/>]
  590. /// Gathers all the animations in the <see cref="Playable"/>.
  591. /// <para></para>
  592. /// In the Unity Editor this method also gathers animations from other components on parent and child objects.
  593. /// </summary>
  594. public virtual void GatherAnimationClips(ICollection<AnimationClip> clips)
  595. {
  596. if (IsPlayableInitialized)
  597. _Playable.GatherAnimationClips(clips);
  598. #if UNITY_EDITOR
  599. Editor.AnimationGatherer.GatherFromGameObject(gameObject, clips);
  600. if (_Animator != null && _Animator.gameObject != gameObject)
  601. Editor.AnimationGatherer.GatherFromGameObject(_Animator.gameObject, clips);
  602. #endif
  603. }
  604. /************************************************************************************************************************/
  605. #endregion
  606. /************************************************************************************************************************/
  607. }
  608. }