Data types for sprite animations. Defines PixelData (position and color), FrameData (sparse pixel list, frame time, optional hitbox, and cached flipped variants), AnimationData (named animation with frames, size, hitbox and loop mode), and LoopMode enum.
namespace Sandbox;
/// <summary>
/// A single coloured pixel within a sprite frame.
/// Stores its position relative to the sprite origin (bottom-left) and its colour.
/// </summary>
public struct PixelData
{
public PixelPoint Position;
public Color32 Color;
public PixelData( PixelPoint position, Color32 color )
{
Position = position;
Color = color;
}
}
/// <summary>
/// One frame of a sprite animation.
/// Contains the pixel list and the duration (in seconds) before advancing to the next frame.
/// </summary>
public class FrameData
{
// Sparse pixel list — only non-transparent pixels are stored.
// Fully transparent pixels are stripped at load time (see SpriteManager.ParseFramePixels)
// so Draw loops only touch pixels that will actually write something.
public List<PixelData> Pixels { get; set; }
public float AnimTime { get; set; }
/// <summary>
/// Optional per-frame hitbox override. When null, the animation-level
/// <see cref="AnimationData.Hitbox"/> is used instead.
/// </summary>
public PixelRect? Hitbox { get; set; }
// Lazily-computed flipped variants. Pixel data is immutable after load,
// so these are computed once and reused on every subsequent Draw call.
private List<PixelData> _cachedFlipX;
private List<PixelData> _cachedFlipY;
private List<PixelData> _cachedFlipXY;
public FrameData() { }
public FrameData( List<PixelData> pixels, float animTime = 0f, PixelRect? hitbox = null )
{
Pixels = pixels;
AnimTime = animTime;
Hitbox = hitbox;
}
/// <summary>
/// Return a copy of the pixel list with X coordinates mirrored within the given width.
/// Result is cached after the first call.
/// </summary>
public List<PixelData> GetPixelsFlippedX( int width )
{
if ( _cachedFlipX is not null )
return _cachedFlipX;
_cachedFlipX = new List<PixelData>( Pixels.Count );
foreach ( PixelData p in Pixels )
_cachedFlipX.Add( new PixelData( new PixelPoint( width - 1 - p.Position.X, p.Position.Y ), p.Color ) );
return _cachedFlipX;
}
/// <summary>
/// Return a copy of the pixel list with Y coordinates mirrored within the given height.
/// Result is cached after the first call.
/// </summary>
public List<PixelData> GetPixelsFlippedY( int height )
{
if ( _cachedFlipY is not null )
return _cachedFlipY;
_cachedFlipY = new List<PixelData>( Pixels.Count );
foreach ( PixelData p in Pixels )
_cachedFlipY.Add( new PixelData( new PixelPoint( p.Position.X, height - 1 - p.Position.Y ), p.Color ) );
return _cachedFlipY;
}
/// <summary>
/// Return a copy of the pixel list mirrored on both axes.
/// Result is cached after the first call.
/// </summary>
public List<PixelData> GetPixelsFlippedXY( int width, int height )
{
if ( _cachedFlipXY is not null )
return _cachedFlipXY;
_cachedFlipXY = new List<PixelData>( Pixels.Count );
foreach ( PixelData p in Pixels )
_cachedFlipXY.Add( new PixelData( new PixelPoint( width - 1 - p.Position.X, height - 1 - p.Position.Y ), p.Color ) );
return _cachedFlipXY;
}
}
/// <summary>
/// Defines a single named animation for a sprite, including all frames,
/// the bounding size, optional hitbox, and loop mode.
/// </summary>
public class AnimationData
{
public string Name { get; set; }
public List<FrameData> Frames { get; set; }
public PixelPoint AnimSize { get; set; }
public PixelRect Hitbox { get; set; }
public LoopMode LoopMode { get; set; }
public AnimationData() { }
public AnimationData( string name, List<FrameData> frames, PixelPoint animSize, PixelRect hitbox, LoopMode loopMode )
{
Name = name;
Frames = frames;
AnimSize = animSize;
Hitbox = hitbox;
LoopMode = loopMode;
}
}
/// <summary>
/// How an animation plays back after reaching the last frame.
/// </summary>
public enum LoopMode
{
/// <summary>Restart from frame 0 after the last frame.</summary>
Loops,
/// <summary>Play once then stop on the last frame.</summary>
PlayOnce,
/// <summary>Alternate forward and backward through the frames.</summary>
PingPong,
/// <summary>Jump to a random frame each cycle.</summary>
RandomFrame
}