mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-25 14:49:28 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
160 lines
4.0 KiB
C#
160 lines
4.0 KiB
C#
namespace Sandbox;
|
|
|
|
public partial class Sprite
|
|
{
|
|
/// <summary>
|
|
/// Contains the state of a sprite instance's animation playback.
|
|
/// </summary>
|
|
public class AnimationState
|
|
{
|
|
/// <summary>
|
|
/// The current frame index in the animation.
|
|
/// </summary>
|
|
public int CurrentFrameIndex = 0;
|
|
|
|
/// <summary>
|
|
/// Whether or not the animation is currently ping-ponging. This is only relevant for animations that have <see cref="Sprite.LoopMode.PingPong" />
|
|
/// </summary>
|
|
public bool IsPingPonging = false;
|
|
|
|
/// <summary>
|
|
/// The time since the last frame was advanced.
|
|
/// </summary>
|
|
public float TimeSinceLastFrame = 0f;
|
|
|
|
/// <summary>
|
|
/// The speed at which the animation is playing back. A value of 1 means normal speed, 0.5 means half speed, and -1 means reverse playback.
|
|
/// </summary>
|
|
public float PlaybackSpeed = 1f;
|
|
|
|
/// <summary>
|
|
/// Returns true if the animation finished, looped, or ping-ponged after calling <see cref="TryAdvanceFrame"/>
|
|
/// </summary>
|
|
public bool JustFinished { get; private set; } = false;
|
|
|
|
Sprite.Animation _lastAnimation = null;
|
|
|
|
/// <summary>
|
|
/// Reset the animation playback state to the beginning (first frame, no ping-pong, zero time-since).
|
|
/// </summary>
|
|
public void ResetState()
|
|
{
|
|
CurrentFrameIndex = 0;
|
|
IsPingPonging = false;
|
|
TimeSinceLastFrame = 0f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to advance the frame of a given animation with a given delta time. Returns false if the frame did not advance.
|
|
/// </summary>
|
|
public bool TryAdvanceFrame( Sprite.Animation animation, float deltaTime )
|
|
{
|
|
if ( animation is null || PlaybackSpeed == 0 )
|
|
return false;
|
|
|
|
// Reset animation state when switching animations
|
|
if ( animation != _lastAnimation )
|
|
{
|
|
_lastAnimation = animation;
|
|
ResetState();
|
|
}
|
|
|
|
// Reset finished state
|
|
JustFinished = false;
|
|
|
|
// If there is only one frame, we don't need to animate
|
|
var frameCount = animation.Frames.Count;
|
|
if ( frameCount <= 1 )
|
|
{
|
|
CurrentFrameIndex = 0;
|
|
return false;
|
|
}
|
|
|
|
// Ensure no divide by zero
|
|
var currentPlayback = PlaybackSpeed * (IsPingPonging ? -1 : 1);
|
|
var currentFps = (currentPlayback == 0) ? 0 : (animation.FrameRate * Math.Abs( currentPlayback ));
|
|
if ( currentFps == 0 )
|
|
return false;
|
|
|
|
var frameRate = 1f / currentFps;
|
|
TimeSinceLastFrame += deltaTime;
|
|
if ( TimeSinceLastFrame < frameRate )
|
|
return false;
|
|
|
|
AdvanceFrame( animation, deltaTime );
|
|
return true;
|
|
}
|
|
|
|
private void AdvanceFrame( Sprite.Animation animation, float deltaTime )
|
|
{
|
|
var loopStart = 0;
|
|
var loopEnd = animation.Frames.Count - 1;
|
|
var frame = CurrentFrameIndex;
|
|
var lastFrame = frame;
|
|
var hasFinished = false;
|
|
var currentPlayback = PlaybackSpeed * (IsPingPonging ? -1 : 1);
|
|
|
|
if ( currentPlayback > 0 )
|
|
{
|
|
// Forward Playback
|
|
frame++;
|
|
|
|
if ( frame > loopEnd )
|
|
{
|
|
switch ( animation.LoopMode )
|
|
{
|
|
case Sprite.LoopMode.PingPong:
|
|
IsPingPonging = !IsPingPonging;
|
|
frame = Math.Max( loopEnd - 1, loopStart );
|
|
break;
|
|
case Sprite.LoopMode.Loop:
|
|
IsPingPonging = false;
|
|
frame = loopStart;
|
|
break;
|
|
case Sprite.LoopMode.None:
|
|
IsPingPonging = false;
|
|
frame = (PlaybackSpeed < 0) ? loopStart : loopEnd;
|
|
break;
|
|
}
|
|
if ( lastFrame <= loopEnd )
|
|
{
|
|
hasFinished = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Reverse Playback
|
|
frame--;
|
|
|
|
if ( frame < loopStart )
|
|
{
|
|
switch ( animation.LoopMode )
|
|
{
|
|
case Sprite.LoopMode.PingPong:
|
|
IsPingPonging = !IsPingPonging;
|
|
frame = Math.Min( loopStart + 1, loopEnd );
|
|
break;
|
|
case Sprite.LoopMode.Loop:
|
|
IsPingPonging = false;
|
|
frame = loopEnd;
|
|
break;
|
|
case Sprite.LoopMode.None:
|
|
IsPingPonging = false;
|
|
frame = (PlaybackSpeed < 0) ? loopEnd : loopStart;
|
|
break;
|
|
}
|
|
if ( lastFrame >= loopStart )
|
|
{
|
|
hasFinished = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
CurrentFrameIndex = frame;
|
|
TimeSinceLastFrame = 0;
|
|
JustFinished = hasFinished;
|
|
}
|
|
}
|
|
}
|