AnimatedSprite2D: frames, an atlas and playback time
Build SpriteFrames and distinguish pause, stop, looping and playback speed.
Separate data from playback
SpriteFrames is a resource containing named animations, frame textures and durations. AnimatedSprite2D holds playback state: Animation, Frame, FrameProgress and SpeedScale. Two entities can share frame data while retaining their own playback positions. Editing a shared SpriteFrames affects both; use separate resources when their frame sets must differ.
Use the four files and run steps from 2D movement for a runnable project. ActorFrames.cs below creates a 128×32 atlas, four 32×32 regions and idle/walk clips. No third-party textures, import workflow or manual slicing are required.
ActorFrames.cs
using Reckfell;
public static class ActorFrames
{
public static SpriteFrames Create()
{
using var image = Image.CreateEmpty(128, 32, false, Image.Format.Rgba8);
image.Fill(Colors.Transparent);
for (int i = 0; i < 4; i++)
{
int x = i * 32;
int stride = i == 1 ? 3 : i == 3 ? -3 : 0;
image.FillRect(new Rect2I(x + 9, 4, 14, 16), Colors.White);
image.FillRect(new Rect2I(x + 19, 8, 3, 3), Colors.Black);
image.FillRect(new Rect2I(x + 9 + stride, 20, 5, 8), new Color(0.25f, 0.45f, 1));
image.FillRect(new Rect2I(x + 18 - stride, 20, 5, 8), new Color(0.25f, 0.45f, 1));
}
var atlas = ImageTexture.CreateFromImage(image);
var frames = new SpriteFrames();
frames.RemoveAnimation("default");
frames.AddAnimation("idle");
frames.SetAnimationLoopMode("idle", SpriteFrames.LoopMode.Linear);
frames.SetAnimationSpeed("idle", 1);
frames.AddAnimation("walk");
frames.SetAnimationLoopMode("walk", SpriteFrames.LoopMode.Linear);
frames.SetAnimationSpeed("walk", 8);
for (int i = 0; i < 4; i++)
{
var region = new AtlasTexture { Atlas = atlas, Region = new Rect2(i * 32, 0, 32, 32) };
frames.AddFrame("walk", region, i == 2 ? 2 : 1);
if (i == 0) frames.AddFrame("idle", region);
}
return frames;
}
}Substitute your atlas
Replace Image/ImageTexture creation with loading your Texture2D and keep AtlasTexture slicing. Region uses source image pixels, not normalized 0..1 coordinates. Account for margins and spacing when calculating region origins. Keep the actor's pivot at a consistent position within frames to avoid visible jitter on a stationary entity.
TextureFilter=Nearest in TopDownActor preserves hard pixel edges. It does not configure image import, Viewport dimensions or overall game scaling. FlipH mirrors only the image; Offset and Centered determine placement relative to the entity. Configure the physics Shape independently when replacing artwork.
Calculate duration
For nonzero playback speed, frame duration in seconds is relativeDuration / (animationFps × abs(SpeedScale × customSpeed)). customSpeed is the Play argument. The sample walk clip has FPS=8 and weights 1,1,2,1. One linear cycle lasts 5/8=0.625 seconds with both multipliers at 1. Weight 2 doubles a frame's duration; it does not mean two seconds.
SpeedScale=0 freezes advancement, but IsPlaying remains true if playback has not stopped. GetPlayingSpeed returns the signed multiplier, excluding FPS and frame weight. IsPlaying alone does not mean frames are currently changing.
Control the cursor
| Operation | Result |
|---|---|
| Play("walk") | Start playback; after Pause, resume the same clip at its saved position |
| Pause() | Preserve Frame and FrameProgress and disable playback |
| Stop() | Stop and reset the cursor to the beginning |
| SetFrameAndProgress(frame, progress) | Explicitly set a frame and its fraction; normally use progress in [0,1] |
| PlayBackwards("walk") | Start reverse playback; use positive SpeedScale for the expected direction |
Changing clips with Play selects the start unless fromEnd is specified. To force a restart of the current clip, call Stop then Play. Do not set Frame=0 every _Process: that prevents advancement. The movement example calls Play only when the state name changes.
Choose a loop and await completion
Use the current SetAnimationLoopMode API: LoopMode.None for a one-shot, Linear for ordinary looping and Pingpong to reverse at endpoints. Linear and None were exercised natively for this example; Pingpong behavior is source-reviewed.
AnimationFinished signals completion of a one-shot clip. Use AnimationLooped for a looping clip. Waiting for AnimationFinished on a Linear walk clip does not complete at each cycle. FrameChanged indicates a frame change, not necessarily action completion or a gameplay hit.
Define a separate synchronization rule for attacks or other gameplay events. Frame animation does not create hitboxes or guarantee one gameplay effect for an input signal.