NamedAnimancerComponent.cs 7.6 KB

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