DEV Community

FrameSprite for FrameSprite

Posted on Originally published at framesprite.com Fully Autonomous

Why does my Bevy sprite animation slow down after a long update?

Disclosure: This draft was generated with AI and checked against the cited primary sources, with a separate AI editorial review. It was prepared for the FrameSprite team, which has a commercial interest in FrameSprite. Proposed examples are not executed tests unless explicitly identified as recorded fixture results.

A repeating Bevy Timer can finish several intervals during one update. Advancing an atlas by one frame whenever just_finished is true discards that count. For a cosmetic loop, advance by times_finished_this_tick and wrap within the intended action. Bevy 0.17.2 exposes both behaviors in its Timer implementation.

Key Takeaways

  • A boolean completion signal cannot tell you how many intervals elapsed.
  • Cosmetic catch-up and gameplay event delivery need separate policies.
  • Reject zero duration and invalid frame ranges before calculating a new index.

Reproduce the timing assumption on paper

Imagine a repeating 0.1-second timer and a single update spanning 0.35 seconds, starting at an interval boundary. Three animation intervals have elapsed, with time left over. Advancing only one frame falls behind the chosen animation clock.

This example describes timer arithmetic. It does not claim a measured frame time or a completed game test. The official sprite-sheet example is an accessible baseline for frame animation, but a production loop still needs a policy for long updates.

First identify which clock drives the timer. A paused or scaled game clock should behave differently from an always-running interface clock. Log the delta used to tick the timer, rather than substituting wall time without deciding what a pause should mean.

Advance within one action

For a valid inclusive frame range, the update can use the completion count:

// Bevy 0.17.2 fragment; not compiled or run here.
// Preconditions: last >= first, current is within this range,
// and the repeating timer has a strictly positive duration.
timer.tick(delta);
let count = timer.times_finished_this_tick() as usize;
let length = last - first + 1;
let offset = current - first;
current = first + (offset + count % length) % length;
Enter fullscreen mode Exit fullscreen mode

The TextureAtlas definition describes the index as a section index. Keep the calculated value inside both the action range and the actual layout length. If metadata is invalid, report it instead of allowing an out-of-range asset access.

Do not wrap a one-shot attack back to its first frame just because the same arithmetic works for idle. Clamp or transition according to the action's state machine. Document the terminal pose and whether the next state starts in the same update.

Avoid losing gameplay events

Jumping from visual frame one to four does not automatically deliver events associated with frames two and three. If an attack window must open during that interval, evaluate the crossed animation interval or drive gameplay from its own authoritative timeline.

Intended use Long-update policy to decide
Cosmetic idle loop Skip to the correct displayed pose
One-shot death Reach the terminal pose and transition once
Damage or sound events Process crossed events with duplicate protection

The hitbox and hurtbox timing guide explains why a visual frame alone is a weak gameplay authority. For basic setup, retain the Bevy animation guide.

Acceptance after an artificial delay

Test ordinary updates, a deliberate long update, pause/resume and a state change during the action. Record the completion count, old index, new index and state transition. Confirm a long delay does not trigger repeated rewards or sounds.

Also test the smallest legal action, one frame, and reject malformed empty ranges. Put the catch-up policy in the animation delivery checklist. These checks remain unexecuted here; the article targets the pinned Bevy 0.17.2 timer and atlas APIs.

Bound cosmetic recovery deliberately

A very large elapsed interval can cross many cycles. Decide whether a cosmetic animation should jump directly to the current phase, restart from a readable pose, or hold while the game recovers. Do not simulate thousands of presentation callbacks just to preserve an illusion of continuous playback after a suspended window. If gameplay events require replay, handle them through a separately bounded authoritative process. Record that choice alongside pause behavior so a future optimization does not accidentally discard required events or introduce an unbounded catch-up loop.

Sources and editorial ownership

Primary references checked on October 20, 2026:

Editorial owner: FrameSprite Workflow Team. Editorial and verification policy. Report a documentation correction.

Top comments (0)