AnimancerTransition.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using Animancer.Units;
  3. using System;
  4. using UnityEngine;
  5. using Object = UnityEngine.Object;
  6. namespace Animancer
  7. {
  8. /// <summary>
  9. /// A serializable <see cref="ITransition"/> which can create a particular type of <see cref="AnimancerState"/>
  10. /// when passed into <see cref="AnimancerPlayable.Play(ITransition)"/>.
  11. /// </summary>
  12. /// <remarks>
  13. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/transitions">Transitions</see>
  14. /// </remarks>
  15. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerTransition_1
  16. ///
  17. [Serializable]
  18. public abstract class AnimancerTransition<TState> :
  19. ITransition<TState>, ITransitionDetailed, ITransitionWithEvents, ICopyable<AnimancerTransition<TState>>
  20. where TState : AnimancerState
  21. {
  22. /************************************************************************************************************************/
  23. [SerializeField]
  24. [Tooltip(Strings.Tooltips.FadeDuration)]
  25. [AnimationTime(AnimationTimeAttribute.Units.Seconds, Rule = Validate.Value.IsNotNegative)]
  26. [DefaultFadeValue]
  27. private float _FadeDuration = AnimancerPlayable.DefaultFadeDuration;
  28. /// <inheritdoc/>
  29. /// <remarks>[<see cref="SerializeField"/>]</remarks>
  30. /// <exception cref="ArgumentOutOfRangeException">Thrown when setting the value to a negative number.</exception>
  31. public float FadeDuration
  32. {
  33. get => _FadeDuration;
  34. set
  35. {
  36. if (value < 0)
  37. throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FadeDuration)} must not be negative");
  38. _FadeDuration = value;
  39. }
  40. }
  41. /************************************************************************************************************************/
  42. /// <inheritdoc/>
  43. /// <remarks>Returns <c>false</c> unless overridden.</remarks>
  44. public virtual bool IsLooping => false;
  45. /// <inheritdoc/>
  46. public virtual float NormalizedStartTime
  47. {
  48. get => float.NaN;
  49. set { }
  50. }
  51. /// <inheritdoc/>
  52. /// <remarks>Returns <c>1</c> unless overridden.</remarks>
  53. public virtual float Speed
  54. {
  55. get => 1;
  56. set { }
  57. }
  58. /// <inheritdoc/>
  59. public abstract float MaximumDuration { get; }
  60. /************************************************************************************************************************/
  61. [SerializeField, Tooltip(Strings.ProOnlyTag + "Events which will be triggered as the animation plays")]
  62. private AnimancerEvent.Sequence.Serializable _Events;
  63. /// <inheritdoc/>
  64. /// <remarks>This property returns the <see cref="AnimancerEvent.Sequence.Serializable.Events"/>.</remarks>
  65. /// <exception cref="NullReferenceException">
  66. /// <see cref="SerializedEvents"/> is null. If this transition was created in code (using the <c>new</c>
  67. /// keyword rather than being deserialized by Unity) you will need to null-check and/or assign a new
  68. /// sequence to the <see cref="SerializedEvents"/> before accessing this property.
  69. /// </exception>
  70. public AnimancerEvent.Sequence Events
  71. {
  72. get
  73. {
  74. #if UNITY_ASSERTIONS
  75. if (_Events == null)
  76. throw new NullReferenceException(
  77. $"{nameof(AnimancerTransition<TState>)}.{nameof(SerializedEvents)} is null.");
  78. #endif
  79. return _Events.Events;
  80. }
  81. }
  82. /// <inheritdoc/>
  83. public ref AnimancerEvent.Sequence.Serializable SerializedEvents => ref _Events;
  84. /************************************************************************************************************************/
  85. /// <summary>
  86. /// The state that was created by this object. Specifically, this is the state that was most recently
  87. /// passed into <see cref="Apply"/> (usually by <see cref="AnimancerPlayable.Play(ITransition)"/>).
  88. /// <para></para>
  89. /// You can use <see cref="AnimancerPlayable.StateDictionary.GetOrCreate(ITransition)"/> or
  90. /// <see cref="AnimancerLayer.GetOrCreateState(ITransition)"/> to get or create the state for a
  91. /// specific object.
  92. /// <para></para>
  93. /// <see cref="State"/> is simply a shorthand for casting this to <typeparamref name="TState"/>.
  94. /// </summary>
  95. public AnimancerState BaseState { get; private set; }
  96. /************************************************************************************************************************/
  97. private TState _State;
  98. /// <summary>
  99. /// The state that was created by this object. Specifically, this is the state that was most recently
  100. /// passed into <see cref="Apply"/> (usually by <see cref="AnimancerPlayable.Play(ITransition)"/>).
  101. /// </summary>
  102. ///
  103. /// <remarks>
  104. /// You can use <see cref="AnimancerPlayable.StateDictionary.GetOrCreate(ITransition)"/> or
  105. /// <see cref="AnimancerLayer.GetOrCreateState(ITransition)"/> to get or create the state for a
  106. /// specific object.
  107. /// <para></para>
  108. /// This property is shorthand for casting the <see cref="BaseState"/> to <typeparamref name="TState"/>.
  109. /// </remarks>
  110. ///
  111. /// <exception cref="InvalidCastException">
  112. /// The <see cref="BaseState"/> is not actually a <typeparamref name="TState"/>. This should only
  113. /// happen if a different type of state was created by something else and registered using the
  114. /// <see cref="Key"/>, causing this <see cref="AnimancerPlayable.Play(ITransition)"/> to pass that
  115. /// state into <see cref="Apply"/> instead of calling <see cref="CreateState"/> to make the correct type of
  116. /// state.
  117. /// </exception>
  118. public TState State
  119. {
  120. get
  121. {
  122. if (_State == null)
  123. _State = (TState)BaseState;
  124. return _State;
  125. }
  126. protected set
  127. {
  128. BaseState = _State = value;
  129. }
  130. }
  131. /************************************************************************************************************************/
  132. /// <inheritdoc/>
  133. /// <remarks>Returns <c>true</c> unless overridden.</remarks>
  134. public virtual bool IsValid => true;
  135. /// <summary>The <see cref="AnimancerState.Key"/> which the created state will be registered with.</summary>
  136. /// <remarks>Returns <c>this</c> unless overridden.</remarks>
  137. public virtual object Key => this;
  138. /// <inheritdoc/>
  139. /// <remarks>Returns <see cref="FadeMode.FixedSpeed"/> unless overridden.</remarks>
  140. public virtual FadeMode FadeMode => FadeMode.FixedSpeed;
  141. /// <inheritdoc/>
  142. public abstract TState CreateState();
  143. /// <inheritdoc/>
  144. AnimancerState ITransition.CreateState() => CreateState();
  145. /************************************************************************************************************************/
  146. /// <inheritdoc/>
  147. public virtual void Apply(AnimancerState state)
  148. {
  149. state.Events = _Events;
  150. #if UNITY_ASSERTIONS
  151. if (state.HasEvents)
  152. state.Events.SetShouldNotModifyReason("it was created by a Transition." +
  153. $"\n\nTransitions give the played {nameof(AnimancerState)} a reference to their Events," +
  154. $" meaning that any modifications to the state.Events will also affect the transition.Events" +
  155. $" and persist when that Transition is played in the future. This is a common source of logic bugs" +
  156. $" so when a Transition is played it marks its Events as no longer expecting to be modified.");
  157. #endif
  158. BaseState = state;
  159. if (_State != state)
  160. _State = null;
  161. }
  162. /************************************************************************************************************************/
  163. /// <summary>The <see cref="AnimancerState.MainObject"/> that the created state will have.</summary>
  164. public virtual Object MainObject { get; }
  165. /// <summary>The display name of this transition.</summary>
  166. public virtual string Name
  167. {
  168. get
  169. {
  170. var mainObject = MainObject;
  171. return mainObject != null ? mainObject.name : null;
  172. }
  173. }
  174. /// <summary>Returns the <see cref="Name"/> and type of this transition.</summary>
  175. public override string ToString()
  176. {
  177. var type = GetType().FullName;
  178. var name = Name;
  179. if (name != null)
  180. return $"{name} ({type})";
  181. else
  182. return type;
  183. }
  184. /************************************************************************************************************************/
  185. /// <inheritdoc/>
  186. public virtual void CopyFrom(AnimancerTransition<TState> copyFrom)
  187. {
  188. if (copyFrom == null)
  189. {
  190. _FadeDuration = AnimancerPlayable.DefaultFadeDuration;
  191. _Events = default;
  192. return;
  193. }
  194. _FadeDuration = copyFrom._FadeDuration;
  195. _Events = copyFrom._Events.Clone();
  196. }
  197. /************************************************************************************************************************/
  198. /// <summary>Applies the given details to the `state`.</summary>
  199. public static void ApplyDetails(AnimancerState state, float speed, float normalizedStartTime)
  200. {
  201. if (!float.IsNaN(speed))
  202. state.Speed = speed;
  203. if (!float.IsNaN(normalizedStartTime))
  204. state.NormalizedTime = normalizedStartTime;
  205. else if (state.Weight == 0)
  206. state.NormalizedTime = AnimancerEvent.Sequence.GetDefaultNormalizedStartTime(speed);
  207. }
  208. /************************************************************************************************************************/
  209. }
  210. }