Quick answer: When you assign sprite_index, GameMaker does not reset image_index. The old frame number carries over to the new sprite, often wrapping or showing the wrong frame. Always set image_index = 0 on state transitions, and only assign sprite_index when the state actually changes.

You build a state machine for your player character: idle, run, jump, attack. The sprite animations look fine individually. But transition from attack (12 frames) back to idle (4 frames), and the character flickers or shows a random frame before settling. The bug is mechanical: image_index was at frame 10 during the attack, and idle only has 4 frames.

The Root Cause

In GameMaker, sprite_index and image_index are independent variables. Changing sprite_index swaps the sprite sheet but does not touch image_index. If image_index is 10 and the new sprite has only 4 frames, GameMaker wraps it with modulo: 10 % 4 = 2. You see frame 2 of the new animation for one tick before image_speed advances it.

With non-looping animations or different frame counts, this wrap produces a visible glitch — a flash of the wrong frame on every transition.

The Fix: Reset on Transition

// Step event: state machine
switch (state) {
    case "idle":
        if (sprite_index != spr_player_idle) {
            sprite_index = spr_player_idle;
            image_index = 0;
        }
        // idle logic...
        break;

    case "run":
        if (sprite_index != spr_player_run) {
            sprite_index = spr_player_run;
            image_index = 0;
        }
        // run logic...
        break;

    case "attack":
        if (sprite_index != spr_player_attack) {
            sprite_index = spr_player_attack;
            image_index = 0;
            image_speed = 1;
        }
        // wait for animation to finish
        if (image_index >= sprite_get_number(sprite_index) - 1) {
            state = "idle";
        }
        break;
}

The if (sprite_index != ...) guard is critical. It prevents reassigning the sprite every frame, which would reset the animation to frame 0 on every tick and make the sprite appear frozen.

The Every-Frame Assignment Bug

A common pattern that looks correct but is wrong:

// BAD: resets animation every frame
if (state == "run") {
    sprite_index = spr_player_run;  // assigned every frame
    image_index = 0;                 // animation never advances past frame 0
}

This sets image_index = 0 sixty times per second. The animation never plays — it sits on frame 0 forever. The fix is the guard: only assign when the sprite actually changes.

Multiple Writers

If the player object inherits from a parent, and both the parent’s Step event and the child’s Step event assign sprite_index, the last one to run wins. The loser’s assignment is overwritten in the same frame, producing a one-frame flicker.

Search for every assignment to sprite_index on the object and its parents. There should be exactly one authority for the sprite at any given time. If you must have multiple writers, use a priority system or a single function that all writers call.

Animation End Detection

For one-shot animations (attack, hurt, death), you need to know when the animation is done so you can transition back to idle. Use the Animation End event or check image_index against sprite_get_number:

// Animation End event (fires when animation loops)
if (state == "attack") {
    state = "idle";
    image_speed = 1;
}

Set image_speed = 0 on the last frame if you want the animation to freeze at the end rather than loop. Then transition the state from a timer or an event.

Verifying the Fix

Add a Draw GUI event that shows state, sprite_index, image_index, and image_speed as text. Cycle through every state transition and watch the numbers. On a correct transition, image_index resets to 0 exactly once and then climbs. If it stays at 0, you are assigning every frame. If it jumps to a random number, you forgot to reset it.

“sprite_index and image_index are two independent knobs. GameMaker never touches one when you turn the other. That independence is by design — and it is the source of every sprite transition bug in the engine.”

Related Issues

For draw event visibility problems, see GameMaker draw event not firing. For state that resets across rooms, see GameMaker instance variables resetting on room change.

Every sprite_index assignment should be guarded with an if-check and paired with image_index = 0. Make it a habit and you will never ship a sprite flicker again.