You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
6.8 KiB
C#
170 lines
6.8 KiB
C#
2 months ago
|
// Animancer // Copyright 2020 Kybernetik //
|
||
|
|
||
|
using System;
|
||
|
using System.Text;
|
||
|
using UnityEngine;
|
||
|
|
||
|
namespace Animancer
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// A <see cref="callback"/> delegate paired with a <see cref="normalizedTime"/> to determine when to invoke it.
|
||
|
/// </summary>
|
||
|
public partial struct AnimancerEvent
|
||
|
{
|
||
|
/************************************************************************************************************************/
|
||
|
#region Event
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>The <see cref="AnimancerState.NormalizedTime"/> at which to invoke the <see cref="callback"/>.</summary>
|
||
|
public float normalizedTime;
|
||
|
|
||
|
/// <summary>The delegate to invoke when the <see cref="normalizedTime"/> passes.</summary>
|
||
|
public Action callback;
|
||
|
|
||
|
/// <summary>The largest possible float value less than 1.</summary>
|
||
|
public const float AlmostOne = 0.99999994f;
|
||
|
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>Constructs a new <see cref="AnimancerEvent"/>.</summary>
|
||
|
public AnimancerEvent(float normalizedTime, Action callback)
|
||
|
{
|
||
|
this.normalizedTime = normalizedTime;
|
||
|
this.callback = callback;
|
||
|
}
|
||
|
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>Returns "AnimancerEvent(normalizedTime, callbackTarget.CallbackMethod)".</summary>
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>Appends the details of this event to the `text`.</summary>
|
||
|
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
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>The <see cref="AnimancerState"/> currently triggering an event using <see cref="Invoke"/>.</summary>
|
||
|
public static AnimancerState CurrentState { get { return _CurrentState; } }
|
||
|
private static AnimancerState _CurrentState;
|
||
|
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>The <see cref="AnimancerEvent"/> currently being triggered by <see cref="Invoke"/>.</summary>
|
||
|
public static AnimancerEvent CurrentEvent { get { return _CurrentEvent; } }
|
||
|
private static AnimancerEvent _CurrentEvent;
|
||
|
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the static <see cref="CurrentState"/> and <see cref="CurrentEvent"/> then invokes the <see cref="callback"/>.
|
||
|
/// <para></para>
|
||
|
/// This method catches and logs any exception thrown by the <see cref="callback"/>.
|
||
|
/// </summary>
|
||
|
/// <exception cref="NullReferenceException">Thrown if the <see cref="callback"/> is null.</exception>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/************************************************************************************************************************/
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns either the `minDuration` or the <see cref="AnimancerState.RemainingDuration"/> of the
|
||
|
/// <see cref="CurrentState"/> state (whichever is higher).
|
||
|
/// </summary>
|
||
|
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
|
||
|
/************************************************************************************************************************/
|
||
|
}
|
||
|
}
|
||
|
|