// 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
/************************************************************************************************************************/
}
}