// Animancer // Copyright 2020 Kybernetik // using System; using System.Text; using UnityEngine; namespace Animancer { /// /// A delegate paired with a to determine when to invoke it. /// public partial struct AnimancerEvent { /************************************************************************************************************************/ #region Event /************************************************************************************************************************/ /// The at which to invoke the . public float normalizedTime; /// The delegate to invoke when the passes. public Action callback; /// The largest possible float value less than 1. public const float AlmostOne = 0.99999994f; /************************************************************************************************************************/ /// Constructs a new . public AnimancerEvent(float normalizedTime, Action callback) { this.normalizedTime = normalizedTime; this.callback = callback; } /************************************************************************************************************************/ /// Returns "AnimancerEvent(normalizedTime, callbackTarget.CallbackMethod)". public override string ToString() { var text = new StringBuilder() .Append("AnimancerEvent(") .Append(normalizedTime) .Append(", "); if (callback == null) { text.Append("null)"); } else if (callback.Target == null) { text.Append(callback.Method.Name) .Append(")"); } else { text.Append(callback.Target) .Append('.') .Append(callback.Method.Name) .Append(")"); } return text.ToString(); } /************************************************************************************************************************/ /// Appends the details of this event to the `text`. public void AppendDetails(StringBuilder text, string name, string delimiter = "\n") { text.Append(delimiter).Append(name).Append(".NormalizedTime=").Append(normalizedTime); if (callback != null) { text.Append(delimiter).Append(name).Append(".Target=").Append(callback.Target); text.Append(delimiter).Append(name).Append(".Method=").Append(callback.Method); } } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Invocation /************************************************************************************************************************/ /// The currently triggering an event using . public static AnimancerState CurrentState { get { return _CurrentState; } } private static AnimancerState _CurrentState; /************************************************************************************************************************/ /// The currently being triggered by . public static AnimancerEvent CurrentEvent { get { return _CurrentEvent; } } private static AnimancerEvent _CurrentEvent; /************************************************************************************************************************/ /// /// Sets the static and then invokes the . /// /// This method catches and logs any exception thrown by the . /// /// Thrown if the is null. public void Invoke(AnimancerState state) { var previousState = _CurrentState; var previousEvent = _CurrentEvent; _CurrentState = state; _CurrentEvent = this; try { callback(); } catch (Exception ex) { Debug.LogException(ex); } _CurrentState = previousState; _CurrentEvent = previousEvent; } /************************************************************************************************************************/ /// /// Returns either the `minDuration` or the of the /// state (whichever is higher). /// public static float GetFadeOutDuration(float minDuration = AnimancerPlayable.DefaultFadeDuration) { var state = CurrentState; if (state == null) return minDuration; var time = state.Time; var speed = state.EffectiveSpeed; float remainingDuration; if (state.IsLooping) { var previousTime = time - speed * Time.deltaTime; var inverseLength = 1f / state.Length; // If we just passed the end of the animation, the remaining duration would technically be the full // duration of the animation, so we most likely want to use the minimum duration instead. if (Math.Floor(time * inverseLength) != Math.Floor(previousTime * inverseLength)) return minDuration; } if (speed > 0) { remainingDuration = (state.Length - time) * speed; } else { remainingDuration = time * -speed; } return Math.Max(minDuration, remainingDuration); } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ } }