Key Ideas
1Frame Animation Fundamentals. Animation is just a list of frames swapped at a fixed rate. Always advance based on elapsed time (dt), never once per loop iteration, so speed stays consistent across machines.
2Movement Cycles. Walk, run, jump and dash each need their own frame list and speed. Group them in a dict keyed by state name so switching is one lookup, not if/elif chains.
3Attack, Death & Idle. Attack and death are one-shot (play once, don't loop); idle loops forever and is the default when nothing else is happening.
4Getting Timing Right. Advance by accumulated milliseconds, not per-frame ticks, and only reset frame_index when the animation NAME changes — not on every update.
5Reusable Animation Manager. Wrap timer, frame index and loop-flag logic in one class (play(), update(dt), get_frame()) that any sprite can own instead of duplicating timer code.