Animation Loop Timing Issues

I've been pulling my hair out for a few days trying to get an animation loop to work with timers in the way I expect. I feel like I'm probably just missing something basic and need some outside opinions.

DESIRED OUTCOME: Have a 16-frame image table animation loop from frame 1 all the way back around to frame 1 again for every "loop" in my "loop counter". The animation must line up perfectly on each iteration. Currently, it does not.

WHAT I'VE DONE:

  • Created animation.loop and a corresponding sprite. The animation.loop is paused until loop_counter > 1
  • Created a delay timer where the delay time is the same length as the full animation loop. When the delay is completed, the timer deletes itself, subtracts 1 from the loop counter, and pauses the animation.
  • While the loop_counter > 1, the sprite's image is updated with the current animator image.

Please see the gif below of the animation (it is the conveyor belt at the bottom of the screen underneath the eggs). The eggs are not supposed to move right now as they are my reference point. You can see that the belt cradles for the eggs are not lining up properly.
PlaydateSimulator_ep33pyV0pI

Code below:
Excerpt from sprites.lua, where the sprite and animator is made

  --belt
  local eggbeltbelt_image = gfx.image.new("images/eggbeltbelt");
  Eggbeltbelt_spr = gfx.sprite.new(eggbeltbelt_image);
  Eggbeltbelt_spr:setZIndex(Eggbeltframe_spr:getZIndex());
    --image table for animation
    local eggbeltbelt_imagetable = gfx.imagetable.new("images/eggbeltbelt-table-16-32");
    
    local animation_speed = 20;
    Eggbeltbelt_animator = gfx.animation.loop.new(animation_speed, eggbeltbelt_imagetable, true);
    Eggbeltbelt_animator.paused = true;
    Eggbeltbelt_spr:setImage(Eggbeltbelt_animator:image());

Excerpt from main.lua, specifically my "Update_eggbeltbelt_animation()" function:

--loop animation start/ongoing
  if eggbelt_table.loop_counter >= 1 then
    Eggbeltbelt_animator.paused = false;

    --KIND OF WORKING BUT MISALIGNED
    --create animation timer if it doesn't exist
    if eggbelt_table.eggbelt_animation_timer == nil then
      --settings for delay timer
      local delay = (Eggbeltbelt_animator.delay * Eggbeltbelt_animator.endFrame);
      local function eggbeltbelt_timer_callback()
        eggbelt_table.eggbelt_animation_timer = nil; --resets timer
        eggbelt_table.loop_counter -= 1;
        Eggbeltbelt_animator.paused = true;
      end

      --create delay timer
      eggbelt_table.eggbelt_animation_timer = pd.timer.new(delay, function();
      return eggbeltbelt_timer_callback();
      end)
    end--

    for i = 1, #eggbelt_table.belt, 1 do
      eggbelt_table.belt[i]:setImage(Eggbeltbelt_animator:image());
    end
end

Any ideas what the issue here might be? I've tried moving the update into the timer callback as well as adding an extra frame's worth of delay to the delay timer, but neither of these have solved it either.

I don't know exactly why your code is not working, but I would guess it has something to do with the difference between the actual refresh rate (usually 30fps) and your "velocity" (20ms that translates into 50fps)

What I would do if I were you is to have different approach, just forget about using a timer, set "shouldLoop" to false and check when "isValid()" returns false, and then execute the logic you want on each cycle.

In my experience is better to let the animation drive the logic instead of the logic drive the animation, unless your game needs some precise timings (like a music or rhythm game)

1 Like

Hey, thanks for the reply! I think what you're saying makes sense...I have been having the feeling that milliseconds are too precise and I must be getting knocked out of sync by a few milliseconds one way or another, which then causes knock-on effects that ruin the effect I'm going for.

A thought like this crossed my mind at some point but I wasn't sure if it would work because I was struggling with isValid on my first attempt. When isValid() hits false, can I just set the frame back to 1 and isValid back to true and run it like it's new? Or do I need to delete and recreate the entire animation.loop once it is no longer valid?

I checked the code in my games and unfortunately you have to recreate the animation.loop when shouldLoop is false.

1 Like

Ah, that makes sense! isValid() was one of the first things I tried a few days ago, but I couldn't get it to go back to true after becoming false. I think that's when I tried to solve the problem with timers.

Anyways, I tried your solution out in a test environment and it's consistently landing on the last frame :smile: I think all I have to do to fix it now is just duplicate the first frame into the last slot to make sure it's realigning where it started. Thank you for the help! This is also way less cumbersome than managing timers.
PlaydateSimulator_3hIAV7WCbU

1 Like