How to refresh an imagetable while keep pressing a button

Hi

I have a Class that contains an imagetable with "numbers" and I want to update the numbers while I keep pressing a button. However, the numbers change too fast... pressing the button just half a second will call the update method several times. I tried to play with timers but I couldn't improve this behaviour.

How could I do it? For example, for a screen in which you have to select a number.

main.lua

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

local pd <const> = playdate
local gfx <const> = pd.graphics

import "number"

local number = Number(pd.display.getWidth() / 4, pd.display.getHeight() / 1.75)

local function initialize()
    number:add()
end

initialize()

function pd.update()
    gfx.sprite.update()
    pd.timer.updateTimers()
end

number.lua

import "CoreLibs/sprites"

local pd <const> = playdate
local gfx <const> = pd.graphics

class('Number').extends(gfx.sprite)

function Number:init(x, y)

	Number.super.init(self)

	-- image table is 7 wide by 3 tall, each cell is 48x48 pixels
	self.swimImages = gfx.imagetable.new('images/numbers')
    self.tableWidth = 7
    self.tableColumn = 1
    self.tableRow = 0

    self.positionX = x
    self.positionY = y

    self:reset()
	self:addSprite()
end

function Number:reset()
    self:updateFrame();
    self:moveTo(self.positionX, self.positionY)
end

function Number:updateFrame()
	self:setImage(self.swimImages[self.tableColumn + self.tableWidth * self.tableRow])
end

function Number:update()
    if pd.buttonIsPressed(pd.kButtonUp) then
        self:increaseNumber()
    end
    pd.timer.updateTimers()
end

function Number:increaseNumber()
    -- local function timerCallback()
    --     print('increase number')
    --     if self.tableColumn + 1 <= 7 then
    --         self.tableColumn = self.tableColumn + 1
    --     else
    --         self.tableColumn = 1
    --     end
    -- print('increase number: ', self.tableColumn)
    --     self:updateFrame()
    -- end
    -- pd.timer.performAfterDelay(1000, timerCallback)
    if self.tableColumn + 1 <= 7 then
        self.tableColumn = self.tableColumn + 1
    else
        self.tableColumn = 1
    end
    print('increase number: ', self.tableColumn)
    self:updateFrame()
end

Thank you and sorry for my English as it's not my first language.

I just found the solution. I was mistaking adding delay for a timer.

local playTimer = nil
local playTime = 1000

local function resetTimer()
	playTimer = playdate.timer.new(playTime, playTime, 0, pd.easingFunctions.linear)
end

function Number:init(x, y)
	...
	...

    resetTimer()
end

function Number:update()
    if playTimer.value == 0 then
        if pd.buttonIsPressed(pd.kButtonUp) then
            self:increaseNumber()
            resetTimer()
        end
    end

    pd.timer.updateTimers()
end
1 Like

Depending on what behavior you want, you can also use pd.buttonJustPressed instead of pd.buttonIsPressed. That way, a single press of the button will only increase your number once, and you have to release the button and press it again. Here's the documentation.

And if you want to be able to keep the button pressed, but just want to slow things down, you can use a separate counter and only increase the number on every n'th frame.

For example, this allows you to press-and-hold, but it will only increase the number ever 15 frames (i.e. twice a second):

-- start at -1 so that the very first time causes an immediate number increase
local count = -1
function Number:update()
    if pd.buttonIsPressed(pd.kButtonUp) then
        count = (count + 1) % 15
        if count == 0 then self:increaseNumber() end
    else
        -- reset the count when the user releases the button
        count = -1
    end
    pd.timer.updateTimers()
end
1 Like

Thanks for your reply!

I want to be able to keep the button pressed.

What is your personal choice between your solution with a counter or using a timer?

I'd say it's mostly a question of preference.

Personally I think I'd go with a counter because to me it feels clearer (you have to do less mental juggling of timer behavior). And I guess technically your current implementation creates a lot of timer objects (but I assume it wouldn't be so many that GC pressure will become a major issue).

On the other hand, one advantage of your approach is that the speed is mostly independent of the framerate (but, if it matters, you could get the same from the counter approach if you don't increment by 1, but by a measure of "time passed since the last frame").

I would say: Use whatever works for you :grinning:

1 Like

Also consider keyRepeatTimer() which can optionally have a little longer delay after the first action, and then go into fast repeating (imitating what happens when you hold a key on a computer keyboard).

EDIT: I don't know why the forum keeps stripping the last part of the docs link:

https://sdk.play.date/inside-playdate/#_key_repeat_timers

1 Like