AnimancerComponent.cs 40 KB

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