// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
using System.Collections.Generic;
using UnityEngine;
namespace Animancer
{
///
/// A system for synchronizing the
/// of animations within the same "group".
///
///
///
///
/// - Store a in a field.
/// - Call any of the methods before playing a new animation.
/// - Then call any of the methods after playing the animation.
///
/// Sample:
///
/// Character Controller -> Synchronization
///
/// // 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;
/// }
///
///
/// https://kybernetik.com.au/animancer/api/Animancer/TimeSynchronizer_1
///
public class TimeSynchronizer
{
/************************************************************************************************************************/
/// The group that the current animation is in.
public T CurrentGroup { get; set; }
/// Should synchronization be applied when the is at its default value?
/// This is false by default so that the default group represents "ungrouped".
public bool SynchronizeDefaultGroup { get; set; }
/// The state which the came from (to avoid syncing with itself).
public AnimancerState State { get; set; }
/// The stored .
public double NormalizedTime { get; set; } = double.NaN;
/************************************************************************************************************************/
/// Creates a new .
public TimeSynchronizer()
{ }
/// Creates a new .
public TimeSynchronizer(T group, bool synchronizeDefaultGroup = false)
{
CurrentGroup = group;
SynchronizeDefaultGroup = synchronizeDefaultGroup;
}
/************************************************************************************************************************/
///
/// Stores the of the .
///
public void StoreTime(AnimancerLayer layer)
=> StoreTime(layer.CurrentState);
/// Stores the of the `state`.
public void StoreTime(AnimancerState state)
=> StoreTime(state, state != null ? state.NormalizedTimeD : double.NaN);
/************************************************************************************************************************/
/// Sets the and .
public void StoreTime(AnimancerState state, double normalizedTime)
{
State = state;
NormalizedTime = normalizedTime;
}
/************************************************************************************************************************/
///
/// Applies the to the
/// if the `group` matches the .
///
public bool SyncTime(AnimancerLayer layer, T group)
=> SyncTime(layer.CurrentState, group, Time.deltaTime);
///
/// Applies the to the
/// if the `group` matches the .
///
public bool SyncTime(AnimancerLayer layer, T group, float deltaTime)
=> SyncTime(layer.CurrentState, group, deltaTime);
///
/// Applies the to the `state`
/// if the `group` matches the .
///
public bool SyncTime(AnimancerState state, T group)
=> SyncTime(state, group, Time.deltaTime);
///
/// Applies the to the `state`
/// and returns true if the `group` matches the .
///
///
/// If the `state` is the same one the time was stored from, this method does nothing and returns false.
///
public bool SyncTime(AnimancerState state, T group, float deltaTime)
{
if (state == null ||
state == State ||
double.IsNaN(NormalizedTime) ||
!EqualityComparer.Default.Equals(CurrentGroup, group) ||
(!SynchronizeDefaultGroup && EqualityComparer.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;
}
/************************************************************************************************************************/
}
}