Playdate.graphics.animation.loop can they be started paused?

Hello, I'm a playdate newbie. I'm trying to create a simple three-frame explosion for a Bosconian clone (gotta start somewhere), using playdate.graphics.animation.loop

Initially, I was trying to start them paused, so I'd only animate them onscreen as required. However, it seems the timer is running from loop creation, resulting in an oddly looping animation. It's odd because the loop was created with loop = false as well, and the length of the repeated animation is obviously dependent on when the game started.

For example this code (where you can see me trying to manage the paused state):

import 'Corelibs/animation'
local gfx = playdate.graphics

Explosion = {}
Explosion.__index = Explosion

function Explosion.new()
local imgTable, err = gfx.imagetable.new("images/explosion-table-15-15.png")
assert(imgTable, err)

local self = gfx.animation.loop.new(120, imgTable, false)
self.paused = true
self.x = -100   -- Start offscreen
self.y = -100

function self:update()
    if self:isValid() then
        self:draw(self.x - 7, self.y - 7)
    else
        self.isPaused = true
    end
end

function self:explode(x, y)
    self.x = x
    self.y = y
    self.frame = 1
    self.paused = false
end

return self

end

Results in this animation. The first time 'explosion' is used, the animation loops, subsequent times it appears to play the three-frame loop without issue:

playdate-20240308-204706

Two questions:

  1. Does it even matter? Can I have a handful of these explosion animation loops that I can re-use? If I start them off-screen their first animation loop can run through, and subsequent times setting frame = 1 seems to work fine when they appear onscreen. Do I even need to care about trying to pause animations when not in use?

  2. Is this generally how people use animation.loop for non-looping animation? Create them, leave them offscreen or undrawn but leave the timers running? Much processor cost to doing this?

If I remove the clumsy attempts to manipulate self.paused, it works fine:

playdate-20240308-205924

That's with this code:

import 'Corelibs/animation'
local gfx = playdate.graphics

Explosion = {}
Explosion.__index = Explosion

function Explosion.new()
local imgTable, err = gfx.imagetable.new("images/explosion-table-15-15.png")
assert(imgTable, err)

local self = gfx.animation.loop.new(120, imgTable, false)
self.x = -100   -- Start offscreen
self.y = -100

function self:update()
    if self:isValid() then
        self:draw(self.x - 7, self.y - 7)
    end
end

function self:explode(x, y)
    self.x = x
    self.y = y
    self.frame = 1
end

return self

end

Any help greatly appreciated!

  1. You could have a handful of unpaused explosion animation loops to put on the screen when necessary.
  2. I usually use animation loops in tandem with sprites. In the sprite's update method, I update the sprite's image to be the animation loop's image. For one-shot visual effects, I remove the sprite once the animation is invalid.
    AFAIK, there is no timer that is actively running with animation loops. In your copy of the Playdate SDK, you'll find a CoreLibs directory that contains a file called animation.lua. There, you will see that a loop's frame is only updated when the frame property is accessed or assigned.

Putting the following code in a main.lua file results in the animation loop only being played once. I wonder if there's somewhere else in your code that is triggering multiple explosions? In your first gif, the explosion bug occurs when a bullet collides with a ship.

Also note that animation loops do not have a property called isPaused, which is in the update method in your first code block.

explosion

import 'Corelibs/animation'
local gfx = playdate.graphics

Explosion = {}
Explosion.__index = Explosion

function Explosion.new()
    local imgTable, err = gfx.imagetable.new("images/explosion-table-15-15.png")

    assert(imgTable, err)

    local self = gfx.animation.loop.new(120, imgTable, false)

    self.paused = true
    self.x = -100   -- Start offscreen
    self.y = -100

    function self:update()
        if self:isValid() then
            self:draw(self.x - 7, self.y - 7)
        else
            self.isPaused = true
        end
    end

    function self:explode(x, y)
        self.x = x
        self.y = y
        self.frame = 1
        self.paused = false
    end

    return self
end

local explosion <const> = Explosion.new()

explosion:explode(200, 120)

function playdate.update()
    gfx.clear()
    explosion:update()
end

Thanks for the reply, it's time I paid attention to the Corelibs lua code! I admit I haven't delved in there yet, but clearly need to.

I hadn't tied the explosion effects to sprites because they serve no purpose in a classic shooter other than hiding sprite removal, so I was re-using them.

Thanks again!

1 Like