123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using System.Collections.Generic;
- using UnityEngine;
- namespace Animancer
- {
- /// <summary>
- /// A system for synchronizing the <see cref="AnimancerState.NormalizedTime"/>
- /// of animations within the same "group".
- /// </summary>
- ///
- /// <remarks>
- /// <list type="number">
- /// <item>Store a <see cref="TimeSynchronizer{T}"/> in a field.</item>
- /// <item>Call any of the <see cref="StoreTime(AnimancerState)"/> methods before playing a new animation.</item>
- /// <item>Then call any of the <see cref="SyncTime(AnimancerState, T, float)"/> methods after playing the animation.</item>
- /// </list>
- /// <strong>Sample:</strong>
- /// <see href="https://kybernetik.com.au/animancer/docs/samples/sprites/character#synchronization">
- /// Character Controller -> Synchronization</see>
- /// <code>
- /// // 1. Define your group type.
- /// // You could use strings or ints or whatever you want, but enums are often best.
- /// public enum AnimationGroup
- /// {
- /// None,
- /// Movement,
- /// }
- ///
- /// [SerializeField] private AnimancerComponent _Animancer;
- ///
- /// // 2. Store a TimeSynchronizer in a field.
- /// private readonly TimeSynchronizer<AnimationGroup>
- /// TimeSynchronizer = new();
- ///
- /// public AnimancerState Play(AnimationClip clip, AnimationGroup group)
- /// {
- /// // 3. Call one of the StoreTime methods before playing a new animation.
- /// TimeSynchronizer.StoreTime(_Animancer);
- ///
- /// // 4. Play an animation.
- /// var state = _Animancer.Play(clip);
- ///
- /// // 5. Call one of the SyncTime methods after playing the animation.
- /// // If the `group` was the same as the value from last time you called it,
- /// // then the state's NormalizedTime will be set to the stored value.
- /// TimeSynchronizer.SyncTime(state, group);
- ///
- /// return state;
- /// }
- /// </code></remarks>
- ///
- /// https://kybernetik.com.au/animancer/api/Animancer/TimeSynchronizer_1
- ///
- public class TimeSynchronizer<T>
- {
- /************************************************************************************************************************/
- /// <summary>The group that the current animation is in.</summary>
- public T CurrentGroup { get; set; }
- /// <summary>Should synchronization be applied when the <see cref="CurrentGroup"/> is at its default value?</summary>
- /// <remarks>This is false by default so that the <c>default</c> group represents "ungrouped".</remarks>
- public bool SynchronizeDefaultGroup { get; set; }
- /// <summary>The state which the <see cref="NormalizedTime"/> came from (to avoid syncing with itself).</summary>
- public AnimancerState State { get; set; }
- /// <summary>The stored <see cref="AnimancerState.NormalizedTimeD"/>.</summary>
- public double NormalizedTime { get; set; } = double.NaN;
- /************************************************************************************************************************/
- /// <summary>Creates a new <see cref="TimeSynchronizer{T}"/>.</summary>
- public TimeSynchronizer()
- { }
- /// <summary>Creates a new <see cref="TimeSynchronizer{T}"/>.</summary>
- public TimeSynchronizer(T group, bool synchronizeDefaultGroup = false)
- {
- CurrentGroup = group;
- SynchronizeDefaultGroup = synchronizeDefaultGroup;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Stores the <see cref="AnimancerState.NormalizedTimeD"/> of the <see cref="AnimancerLayer.CurrentState"/>.
- /// </summary>
- public void StoreTime(AnimancerLayer layer)
- => StoreTime(layer.CurrentState);
- /// <summary>Stores the <see cref="AnimancerState.NormalizedTimeD"/> of the `state`.</summary>
- public void StoreTime(AnimancerState state)
- => StoreTime(state, state != null ? state.NormalizedTimeD : double.NaN);
- /************************************************************************************************************************/
- /// <summary>Sets the <see cref="State"/> and <see cref="NormalizedTime"/>.</summary>
- public void StoreTime(AnimancerState state, double normalizedTime)
- {
- State = state;
- NormalizedTime = normalizedTime;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Applies the <see cref="NormalizedTime"/> to the <see cref="AnimancerLayer.CurrentState"/>
- /// if the `group` matches the <see cref="CurrentGroup"/>.
- /// </summary>
- public bool SyncTime(AnimancerLayer layer, T group)
- => SyncTime(layer.CurrentState, group, Time.deltaTime);
- /// <summary>
- /// Applies the <see cref="NormalizedTime"/> to the <see cref="AnimancerLayer.CurrentState"/>
- /// if the `group` matches the <see cref="CurrentGroup"/>.
- /// </summary>
- public bool SyncTime(AnimancerLayer layer, T group, float deltaTime)
- => SyncTime(layer.CurrentState, group, deltaTime);
- /// <summary>
- /// Applies the <see cref="NormalizedTime"/> to the `state`
- /// if the `group` matches the <see cref="CurrentGroup"/>.
- /// </summary>
- public bool SyncTime(AnimancerState state, T group)
- => SyncTime(state, group, Time.deltaTime);
- /// <summary>
- /// Applies the <see cref="NormalizedTime"/> to the `state`
- /// and returns true if the `group` matches the <see cref="CurrentGroup"/>.
- /// </summary>
- /// <remarks>
- /// If the `state` is the same one the time was stored from, this method does nothing and returns false.
- /// </remarks>
- public bool SyncTime(AnimancerState state, T group, float deltaTime)
- {
- if (state == null ||
- state == State ||
- double.IsNaN(NormalizedTime) ||
- !EqualityComparer<T>.Default.Equals(CurrentGroup, group) ||
- (!SynchronizeDefaultGroup && EqualityComparer<T>.Default.Equals(default, group)))
- {
- CurrentGroup = group;
- return false;
- }
- // Setting the Time forces it to stay at that value after the next animation update.
- // But we actually want it to keep playing, so we need to add deltaTime manually.
- state.MoveTime(NormalizedTime * state.Length + deltaTime * state.EffectiveSpeed, false);
- return true;
- }
- /************************************************************************************************************************/
- }
- }
|