Animations/AnimationBase.cs
using System;
namespace BetterUI.Animations;
/// <summary>
/// Base class for animations, providing common functionality for animating components.
/// </summary>
public abstract class AnimationBase : Component
{
private int _currentIteration;
/// <summary>
/// Gets the start position of the animation.
/// </summary>
protected Vector3 StartPosition { get; private set; }
/// <summary>
/// Gets the elapsed time since the animation started.
/// </summary>
protected float ElapsedTime { get; private set; }
/// <summary>
/// Gets the normalized time of the animation, calculated as elapsed time over duration.
/// </summary>
protected float NormalizedTime => ElapsedTime / Duration;
/// <summary>
/// Gets or sets the duration of the animation in seconds.
/// </summary>
[Property, Category( "Animation" )]
public float Duration { get; set; } = 1f;
/// <summary>
/// Gets or sets the axis of the animation.
/// </summary>
[Property, Category( "Animation" )]
public virtual Vector3 Axis { get; set; } = Vector3.Up;
/// <summary>
/// Gets or sets the easing mode of the animation.
/// </summary>
[Property, Category( "Animation" )]
public EasingMode Easing { get; set; } = EasingMode.Linear;
/// <summary>
/// Gets or sets the iteration type of the animation.
/// </summary>
[Property, Category( "Animation" )]
public IterationType Iteration { get; set; } = IterationType.Loop;
/// <summary>
/// Gets or sets the number of iterations for manual iteration type.
/// </summary>
[Property, Category( "Animation" ), ShowIf( nameof(Iteration), IterationType.Manual )]
public int Iterations { get; set; } = 1;
/// <summary>
/// Called when the animation starts, setting the initial position.
/// </summary>
protected override void OnStart()
{
StartPosition = WorldPosition;
}
/// <summary>
/// Updates the animation, progressing the animation state.
/// </summary>
protected override void OnUpdate()
{
Animate();
}
/// <summary>
/// Handles the animation logic, progressing time and calling OnAnimate.
/// </summary>
private void Animate()
{
if ( Iteration is IterationType.Once && _currentIteration > 0 ) return;
if ( Iteration is IterationType.Manual && _currentIteration >= Iterations ) return;
ElapsedTime += Time.Delta;
var t = ElapsedTime / Duration;
t = ApplyEasing( t, Easing );
OnAnimate( t );
if ( !(ElapsedTime >= Duration) )
return;
ElapsedTime = 0f;
if ( Iteration is not IterationType.Loop )
_currentIteration++;
}
/// <summary>
/// Abstract method to be implemented by derived classes for specific animation logic.
/// </summary>
/// <param name="t">The normalized time for the animation.</param>
protected abstract void OnAnimate( float t );
/// <summary>
/// Applies easing to the normalized time based on the specified easing mode.
/// </summary>
/// <param name="t">The normalized time to apply easing to.</param>
/// <param name="easing">The easing mode to apply.</param>
/// <returns>The eased time.</returns>
protected static float ApplyEasing( float t, EasingMode easing ) => easing switch
{
EasingMode.Linear => t,
EasingMode.EaseIn => t * t,
EasingMode.EaseOut => 1 - MathF.Pow( 1 - t, 2 ),
EasingMode.EaseInOut => t < 0.5f
? 2 * t * t
: 1 - MathF.Pow( -2 * t + 2, 2 ) / 2,
_ => t
};
}