NamedAnimancerComponent.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. using Object = UnityEngine.Object;
  8. namespace Animancer
  9. {
  10. /// <summary>
  11. /// An <see cref="AnimancerComponent"/> which uses the <see cref="Object.name"/>s of <see cref="AnimationClip"/>s
  12. /// so they can be referenced using strings as well as the clips themselves.
  13. /// </summary>
  14. ///
  15. /// <remarks>
  16. /// It also has fields to automatically register animations on startup and play the first one automatically without
  17. /// needing another script to control it, much like Unity's Legacy <see cref="Animation"/> component.
  18. /// <para></para>
  19. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/playing/component-types">Component Types</see>
  20. /// </remarks>
  21. ///
  22. /// <example><see href="https://kybernetik.com.au/animancer/docs/examples/basics/named-animations">Named Animations</see></example>
  23. ///
  24. /// https://kybernetik.com.au/animancer/api/Animancer/NamedAnimancerComponent
  25. ///
  26. [AddComponentMenu(Strings.MenuPrefix + "Named Animancer Component")]
  27. [HelpURL(Strings.DocsURLs.APIDocumentation + "/" + nameof(NamedAnimancerComponent))]
  28. public class NamedAnimancerComponent : AnimancerComponent
  29. {
  30. /************************************************************************************************************************/
  31. #region Fields and Properties
  32. /************************************************************************************************************************/
  33. [SerializeField, Tooltip("If true, the 'Default Animation' will be automatically played by " + nameof(OnEnable))]
  34. private bool _PlayAutomatically = true;
  35. /// <summary>[<see cref="SerializeField"/>]
  36. /// If true, the first clip in the <see cref="Animations"/> array will be automatically played by
  37. /// <see cref="OnEnable"/>.
  38. /// </summary>
  39. public ref bool PlayAutomatically => ref _PlayAutomatically;
  40. /************************************************************************************************************************/
  41. [SerializeField, Tooltip("Animations in this array will be automatically registered by " + nameof(Awake) +
  42. " as states that can be retrieved using their name")]
  43. private AnimationClip[] _Animations;
  44. /// <summary>[<see cref="SerializeField"/>]
  45. /// Animations in this array will be automatically registered by <see cref="Awake"/> as states that can be
  46. /// retrieved using their name and the first element will be played by <see cref="OnEnable"/> if
  47. /// <see cref="PlayAutomatically"/> is true.
  48. /// </summary>
  49. public AnimationClip[] Animations
  50. {
  51. get => _Animations;
  52. set
  53. {
  54. _Animations = value;
  55. States.CreateIfNew(value);
  56. }
  57. }
  58. /************************************************************************************************************************/
  59. /// <summary>
  60. /// The first element in the <see cref="Animations"/> array. It will be automatically played by
  61. /// <see cref="OnEnable"/> if <see cref="PlayAutomatically"/> is true.
  62. /// </summary>
  63. public AnimationClip DefaultAnimation
  64. {
  65. get => _Animations.IsNullOrEmpty() ? null : _Animations[0];
  66. set
  67. {
  68. if (_Animations.IsNullOrEmpty())
  69. _Animations = new AnimationClip[] { value };
  70. else
  71. _Animations[0] = value;
  72. }
  73. }
  74. /************************************************************************************************************************/
  75. #endregion
  76. /************************************************************************************************************************/
  77. #region Methods
  78. /************************************************************************************************************************/
  79. #if UNITY_EDITOR
  80. /// <summary>[Editor-Only]
  81. /// Uses <see cref="ClipState.ValidateClip"/> to ensure that all of the clips in the <see cref="Animations"/>
  82. /// array are supported by the <see cref="Animancer"/> system and removes any others.
  83. /// </summary>
  84. /// <remarks>Called in Edit Mode whenever this script is loaded or a value is changed in the Inspector.</remarks>
  85. protected virtual void OnValidate()
  86. {
  87. if (_Animations == null)
  88. return;
  89. for (int i = 0; i < _Animations.Length; i++)
  90. {
  91. var clip = _Animations[i];
  92. if (clip == null)
  93. continue;
  94. try
  95. {
  96. Validate.AssertNotLegacy(clip);
  97. continue;
  98. }
  99. catch (Exception exception)
  100. {
  101. Debug.LogException(exception, clip);
  102. }
  103. Array.Copy(_Animations, i + 1, _Animations, i, _Animations.Length - (i + 1));
  104. Array.Resize(ref _Animations, _Animations.Length - 1);
  105. i--;
  106. }
  107. }
  108. #endif
  109. /************************************************************************************************************************/
  110. /// <summary>Creates a state for each clip in the <see cref="Animations"/> array.</summary>
  111. protected virtual void Awake()
  112. {
  113. States.CreateIfNew(_Animations);
  114. }
  115. /************************************************************************************************************************/
  116. /// <summary>
  117. /// Plays the first clip in the <see cref="Animations"/> array if <see cref="PlayAutomatically"/> is true.
  118. /// <para></para>
  119. /// Also ensures that the <see cref="PlayableGraph"/> is playing.
  120. /// </summary>
  121. protected override void OnEnable()
  122. {
  123. base.OnEnable();
  124. if (_PlayAutomatically && !_Animations.IsNullOrEmpty())
  125. {
  126. var clip = _Animations[0];
  127. if (clip != null)
  128. Play(clip);
  129. }
  130. }
  131. /************************************************************************************************************************/
  132. /// <summary>Returns the clip's name.</summary>
  133. /// <remarks>
  134. /// This method is used to determine the dictionary key to use for an animation when none is specified by the
  135. /// caller, such as in <see cref="AnimancerComponent.Play(AnimationClip)"/>.
  136. /// </remarks>
  137. public override object GetKey(AnimationClip clip) => clip.name;
  138. /************************************************************************************************************************/
  139. /// <inheritdoc/>
  140. public override void GatherAnimationClips(ICollection<AnimationClip> clips)
  141. {
  142. base.GatherAnimationClips(clips);
  143. clips.Gather(_Animations);
  144. }
  145. /************************************************************************************************************************/
  146. #endregion
  147. /************************************************************************************************************************/
  148. }
  149. }