TimeSynchronizer.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. namespace Animancer
  5. {
  6. /// <summary>
  7. /// A system for synchronizing the <see cref="AnimancerState.NormalizedTime"/>
  8. /// of animations within the same "group".
  9. /// </summary>
  10. ///
  11. /// <remarks>
  12. /// <list type="number">
  13. /// <item>Store a <see cref="TimeSynchronizer{T}"/> in a field.</item>
  14. /// <item>Call any of the <see cref="StoreTime(AnimancerState)"/> methods before playing a new animation.</item>
  15. /// <item>Then call any of the <see cref="SyncTime(AnimancerState, T, float)"/> methods after playing the animation.</item>
  16. /// </list>
  17. /// <strong>Sample:</strong>
  18. /// <see href="https://kybernetik.com.au/animancer/docs/samples/sprites/character#synchronization">
  19. /// Character Controller -> Synchronization</see>
  20. /// <code>
  21. /// // 1. Define your group type.
  22. /// // You could use strings or ints or whatever you want, but enums are often best.
  23. /// public enum AnimationGroup
  24. /// {
  25. /// None,
  26. /// Movement,
  27. /// }
  28. ///
  29. /// [SerializeField] private AnimancerComponent _Animancer;
  30. ///
  31. /// // 2. Store a TimeSynchronizer in a field.
  32. /// private readonly TimeSynchronizer&lt;AnimationGroup&gt;
  33. /// TimeSynchronizer = new();
  34. ///
  35. /// public AnimancerState Play(AnimationClip clip, AnimationGroup group)
  36. /// {
  37. /// // 3. Call one of the StoreTime methods before playing a new animation.
  38. /// TimeSynchronizer.StoreTime(_Animancer);
  39. ///
  40. /// // 4. Play an animation.
  41. /// var state = _Animancer.Play(clip);
  42. ///
  43. /// // 5. Call one of the SyncTime methods after playing the animation.
  44. /// // If the `group` was the same as the value from last time you called it,
  45. /// // then the state's NormalizedTime will be set to the stored value.
  46. /// TimeSynchronizer.SyncTime(state, group);
  47. ///
  48. /// return state;
  49. /// }
  50. /// </code></remarks>
  51. ///
  52. /// https://kybernetik.com.au/animancer/api/Animancer/TimeSynchronizer_1
  53. ///
  54. public class TimeSynchronizer<T>
  55. {
  56. /************************************************************************************************************************/
  57. /// <summary>The group that the current animation is in.</summary>
  58. public T CurrentGroup { get; set; }
  59. /// <summary>Should synchronization be applied when the <see cref="CurrentGroup"/> is at its default value?</summary>
  60. /// <remarks>This is false by default so that the <c>default</c> group represents "ungrouped".</remarks>
  61. public bool SynchronizeDefaultGroup { get; set; }
  62. /// <summary>The state which the <see cref="NormalizedTime"/> came from (to avoid syncing with itself).</summary>
  63. public AnimancerState State { get; set; }
  64. /// <summary>The stored <see cref="AnimancerState.NormalizedTimeD"/>.</summary>
  65. public double NormalizedTime { get; set; } = double.NaN;
  66. /************************************************************************************************************************/
  67. /// <summary>Creates a new <see cref="TimeSynchronizer{T}"/>.</summary>
  68. public TimeSynchronizer()
  69. { }
  70. /// <summary>Creates a new <see cref="TimeSynchronizer{T}"/>.</summary>
  71. public TimeSynchronizer(T group, bool synchronizeDefaultGroup = false)
  72. {
  73. CurrentGroup = group;
  74. SynchronizeDefaultGroup = synchronizeDefaultGroup;
  75. }
  76. /************************************************************************************************************************/
  77. /// <summary>
  78. /// Stores the <see cref="AnimancerState.NormalizedTimeD"/> of the <see cref="AnimancerLayer.CurrentState"/>.
  79. /// </summary>
  80. public void StoreTime(AnimancerLayer layer)
  81. => StoreTime(layer.CurrentState);
  82. /// <summary>Stores the <see cref="AnimancerState.NormalizedTimeD"/> of the `state`.</summary>
  83. public void StoreTime(AnimancerState state)
  84. => StoreTime(state, state != null ? state.NormalizedTimeD : double.NaN);
  85. /************************************************************************************************************************/
  86. /// <summary>Sets the <see cref="State"/> and <see cref="NormalizedTime"/>.</summary>
  87. public void StoreTime(AnimancerState state, double normalizedTime)
  88. {
  89. State = state;
  90. NormalizedTime = normalizedTime;
  91. }
  92. /************************************************************************************************************************/
  93. /// <summary>
  94. /// Applies the <see cref="NormalizedTime"/> to the <see cref="AnimancerLayer.CurrentState"/>
  95. /// if the `group` matches the <see cref="CurrentGroup"/>.
  96. /// </summary>
  97. public bool SyncTime(AnimancerLayer layer, T group)
  98. => SyncTime(layer.CurrentState, group, Time.deltaTime);
  99. /// <summary>
  100. /// Applies the <see cref="NormalizedTime"/> to the <see cref="AnimancerLayer.CurrentState"/>
  101. /// if the `group` matches the <see cref="CurrentGroup"/>.
  102. /// </summary>
  103. public bool SyncTime(AnimancerLayer layer, T group, float deltaTime)
  104. => SyncTime(layer.CurrentState, group, deltaTime);
  105. /// <summary>
  106. /// Applies the <see cref="NormalizedTime"/> to the `state`
  107. /// if the `group` matches the <see cref="CurrentGroup"/>.
  108. /// </summary>
  109. public bool SyncTime(AnimancerState state, T group)
  110. => SyncTime(state, group, Time.deltaTime);
  111. /// <summary>
  112. /// Applies the <see cref="NormalizedTime"/> to the `state`
  113. /// and returns true if the `group` matches the <see cref="CurrentGroup"/>.
  114. /// </summary>
  115. /// <remarks>
  116. /// If the `state` is the same one the time was stored from, this method does nothing and returns false.
  117. /// </remarks>
  118. public bool SyncTime(AnimancerState state, T group, float deltaTime)
  119. {
  120. if (state == null ||
  121. state == State ||
  122. double.IsNaN(NormalizedTime) ||
  123. !EqualityComparer<T>.Default.Equals(CurrentGroup, group) ||
  124. (!SynchronizeDefaultGroup && EqualityComparer<T>.Default.Equals(default, group)))
  125. {
  126. CurrentGroup = group;
  127. return false;
  128. }
  129. // Setting the Time forces it to stay at that value after the next animation update.
  130. // But we actually want it to keep playing, so we need to add deltaTime manually.
  131. state.MoveTime(NormalizedTime * state.Length + deltaTime * state.EffectiveSpeed, false);
  132. return true;
  133. }
  134. /************************************************************************************************************************/
  135. }
  136. }