keyRepeatTimer fires one too many times?

,

It seems that when using the AButtonDown with a keyRepeat timer produces odd results when a limit is already reached. It means a code can be fired twice instead of once.
It may be hard to describe but obvious when you see it:
testKeyRepeatTimers

In this video, you see the PAUSE text being fired twice when the limit (10) is already reached.
Here is the github repository for reference: GitHub - Schyzophrenic/testKeyRepeatTimer
For information, this is run on SDK 2.0.1 on Windows Simulator

The code is as below:

local counter = 0
local keyTimer = nil

sprPause = nil

local sprCounter = gfx.sprite.spriteWithText("Counter - " .. counter, 300, 50)
sprCounter:moveTo(200, 120)
sprCounter:add()

function createPauseSpr()
	local spr = gfx.sprite.spriteWithText("PAUSE", 300, 50)
	spr:moveTo(math.random(350),math.random(220))
	spr:add()

	return spr
end

function pd.AButtonDown()
	print("In A Down, adding counter")

	local function incrementCounter()
		print(string.format("A Down, counter at %d", counter))
		print(keyTimer)

		if counter >= 10 then 
			-- Let's stop the repeat and show the pause sprite
			if keyTimer ~= nil then
				print("Kill Timer in Down")
				keyTimer:remove()
				keyTimer = nil
			end
			sprPause = createPauseSpr()
		else
			counter += 1
		end

		print ("End incrementCounter")
	end

	keyTimer = pd.timer.keyRepeatTimer(incrementCounter)
end

function pd.AButtonUp()
	if keyTimer ~= nil then
		print("Kill Timer in Up")
		keyTimer:remove()
		keyTimer = nil
	end
end

function pd.BButtonDown()
	-- We get out of pause
	-- counter = 0
	if sprPause ~= nil then 
		sprPause:remove()
		sprPause = nil
	end
end

function pd.update()
	local img = gfx.imageWithText("Counter - " .. counter, 300, 50)
    sprCounter:setImage(img)

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

I am not sure if this is a bug or me misunderstanding some of the SDK functions. Thank you

1 Like

I think it's working as expected. The reason it seems weird might just be because your keyRepeatTimer calls incrementCounter() once immediately, before it returns and assigns a value to your keyTimer variable. So in that first immediate call to incrementCounter() execution does not enter the if keyTimer ~= nil then block. On the first repeat call to incrementCounter() it does, so the timer is removed then.

Maybe it would make sense to add a counter check before calling keyTimer = pd.timer.keyRepeatTimer(incrementCounter) if you don't want it to fire in that case?

1 Like

Thanks @dan for looking into this.

That indeed works because it prevents the creation of the timer and then execution of the code inside the callback.

Doubling the limit test in AButtonDown does the trick:

if counter < 10 then 
	keyTimer = pd.timer.keyRepeatTimer(incrementCounter)
else
	sprPause = createPauseSpr()
end

That being said, when press A and my counter is already at the limit the callback is called once but the timer is not yet created. Wouldn't it make sense to have the callback called only once the timer is running?
I agree, it would still mean the code would then need to be executed in AButtonDown as well but it would seem more logical to have something like:

function pd.AButtonDown()
  doAction()

  keyTimer = pd.timer.keyRepeatTimer(doAction)
end

I see your point for sure... I almost want to say it used to work that way but my memories are foggy. Might be too late to change it now in any case, since people are already using it with the current behavior.

Since this is all implemented in CoreLibs/timer.lua you could always modify it to work that way if it makes more sense for your project!

I would rather not change the SDK lib, that would come and bite me later on:D
I totally get your point though, it really is a detail and we should not changed this behavior.

Maybe adding a note in the doc would be helpful? I spent quite a bit of time trying to understand what was wrong here haha

1 Like