Timer inside another repeating timer doesn't work when the parent timer is repeated

I have a power-up button that I want to appear every given seconds, stay for a while, and then disappear.

I created a repeating timer that makes the button appear and inside this timer another timer that triggers a fading gui scale animation.

The timer that triggers the animation only works one time. Every other time the first timer is repeated the inside timer is ignored and the animation is triggered immeadiately.

local position = vmath.vector3(480, 320, 0)
local size =  vmath.vector3(200, 100, 0)
local button = gui.new_box_node(position, size)

gui.set_enabled(button, false)
timer.delay(4, true, function()
	gui.set_enabled(button, true)
	timer.delay(4, false, function() -- This timer only works the first time
		gui.animate(button, "scale", vmath.vector3(0, 0, 0), gui.EASING_LINEAR, 2, 0, function()
			gui.set_enabled(button, false)
			gui.set_scale(button, vmath.vector3(1, 1, 1))
		end)
	end)
end)

Are you sure the inside timer doesn’t work? I think it should work.

I think the problem is that after the first 4 seconds the timers get in sync so they get triggered at the same time. So basically every 4 seconds you get both the callback of outside timer and the callback of the inside timer, no delay between them.

1 Like

The inside timer is not repeating. It only runs once. But it should be called again everytime the outside timer is repeated.

Actually it’s getting called again since it’s triggering the animation. But every time it’s getting called with 0 seconds delay. It only gets the 4 seconds it’s supposed to the first time it’s called.

The moment the outside timer creates the inside timer, the outside timer also restarts itself so both timers start at the same time and both timers have the same delay of 4 seconds therefore both timers end at the same time. This means the GUI gets enabled and starts scaling down at the same time. If you’ll change the delay of the inside timer to for example 1 second probably you will see a delay.

What I’m trying to say is that the inside timer triggers 4 seconds after you create it(so it works) but it triggers 0 seconds after the outside timer(so you don’t get the delay that you want).

1 Like

I tried that it still doesn’t work. What is happening is that the first time the inner timer is called it respects wathever delay I set. But every repeat after that it’s getting 0 delay time.

I did a test.

I used the following code:

timer.delay(4,true,function()
		print("outside : "..socket.gettime())
		timer.delay(4,false, function() print("inside : "..socket.gettime())  end)
		end)

I got the following result:

:blue_circle: DEBUG:SCRIPT: outside : 1774308915.8448
:brown_circle: DEBUG:SCRIPT: outside : 1774308919.8432
:blue_circle: DEBUG:SCRIPT: inside : 1774308919.8596
:green_circle: DEBUG:SCRIPT: outside : 1774308923.8424
:brown_circle: DEBUG:SCRIPT: inside : 1774308923.8585
:black_circle: DEBUG:SCRIPT: outside : 1774308927.8411
:green_circle: DEBUG:SCRIPT: inside : 1774308927.8581

You can see that between every “outside” and it’s corresponding “inside”(matching colors) there is a 4 seconds delay.

2 Likes

I see what you mean. When the outside timer repeats it cancels the inner timer but not the animation. If I give 6 seconds to the outside and 2 to the inside it works as intended. But somehow I was getting this error with 90 seconds on the outside and 2 on the inside. I changed the seconds for this post. Anyway it’s working now.

The outside timer doesn’t cancel anything, it enables the GUI and starts the inner timer

What I mean is, there has to be a period of time between the moment the outside timer expires and the moment the inside timer expires but there isn’t because of the timer periods you used (4s and 4s in the example). So in this example the delay is 4-4 = 0.

3 Likes

Anyway my original code is working now. Thanks

1 Like

Great.

2 Likes

I understand what you say and I understand that my example was wrong since I just changed the time without testing or giving it a thought. But I’m still having the same problem even when I have significant more time on the outside timer.

I recreated the issue. It works fine until I click the button. Once I click the button once, the inner timer stops working.

function init(self)
	msg.post(".", "acquire_input_focus")
	local position = vmath.vector3(480, 320, 0)
	local size =  vmath.vector3(200, 100, 0)
	self.button = gui.new_box_node(position, size)

	gui.set_enabled(self.button, false)
	timer.delay(10, true, function()
		gui.set_enabled(self.button, true)
		timer.delay(2, false, function()
			gui.animate(self.button, "scale", vmath.vector3(0, 0, 0), gui.EASING_LINEAR, 2, 0, function()
				gui.set_enabled(self.button, false)
				gui.set_scale(self.button, vmath.vector3(1, 1, 1))
			end)
		end)
	end)
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and gui.pick_node(self.button, action.x, action.y) then
		gui.set_enabled(self.button, false)
		--button logic
	end
end

Could you please share a full minimal Defold project that I can download and try?

Here it is:
Button Timer.zip (78.9 KB)

I did more tests. The bug gets triggered when I call gui.set_enabled(self.button, false) on input. If I don’t disable the button on click the timers keep working.

I was also able to solve the issue by replacing all the gui.set_enable with gui.set_visible. If instead of enabling and disabling the button I toggle the visibility everything works as expected.

There’s also a secondary issue in the project I uploaded. I added a “button clicked” text. I normally create my own font files. But for this I used the default font from builtins and the default settings make the font blurry.

I honestly don’t see any problem. I added some prints to your code. And os.time() to see roughly at what time things happen. I also changed the 10 second repeating timer to 4 seconds so that I don’t have to wait as long:

function init(self)
	msg.post(".", "acquire_input_focus")
	local position = vmath.vector3(480, 320, 0)
	local size =  vmath.vector3(200, 100, 0)
	self.button = gui.new_box_node(position, size)

	print(os.time() .. " init")
	gui.set_enabled(self.button, false) -- (1)
	timer.delay(4, true, function()
		print(os.time() .. " outer repeating timer has triggered")
		gui.set_enabled(self.button, true) -- (2)
		timer.delay(2, false, function()
			print(os.time() .. " inner timer has triggered")
			gui.animate(self.button, "scale", vmath.vector3(0, 0, 0), gui.EASING_LINEAR, 2, 0, function()
				print(os.time() .. " inner animate done")
				gui.set_enabled(self.button, false) -- (3)
				gui.set_scale(self.button, vmath.vector3(1, 1, 1))
			end)
		end)
	end)
end

function on_input(self, action_id, action)
	if action_id == hash("touch") and gui.pick_node(self.button, action.x, action.y) then
		print(os.time() .. " click and hide")
		gui.set_enabled(self.button, false) -- (4) This is the line that triggers the bug. Removing this or replacing (1), (2), (3) and (4) with gui.set_visible fixes the problem
		gui.new_text_node(vmath.vector3(480, 150, 0), "Button clicked")
	end
end

Output:

DEBUG:SCRIPT: 1774939788 init
DEBUG:SCRIPT: 1774939792 outer repeating timer has triggered
DEBUG:SCRIPT: 1774939794 inner timer has triggered
DEBUG:SCRIPT: 1774939796 outer repeating timer has triggered
DEBUG:SCRIPT: 1774939796 inner animate done
DEBUG:SCRIPT: 1774939798 inner timer has triggered
DEBUG:SCRIPT: 1774939800 outer repeating timer has triggered
DEBUG:SCRIPT: 1774939802 inner animate done
DEBUG:SCRIPT: 1774939802 inner timer has triggered
DEBUG:SCRIPT: 1774939804 outer repeating timer has triggered
DEBUG:SCRIPT: 1774939805 click and hide
DEBUG:SCRIPT: 1774939806 inner timer has triggered
DEBUG:SCRIPT: 1774939808 outer repeating timer has triggered
DEBUG:SCRIPT: 1774939809 click and hide
DEBUG:SCRIPT: 1774939810 inner timer has triggered
DEBUG:SCRIPT: 1774939812 outer repeating timer has triggered
DEBUG:SCRIPT: 1774939814 inner animate done
DEBUG:SCRIPT: 1774939814 inner timer has triggered
DEBUG:SCRIPT: 1774939816 outer repeating timer has triggered

From the looks of it the inner timer keeps triggering as expected.

One thing to note is that you are not checking action.pressed which means that your button logic will run multiple times in on_input.

It’s not a timing issue, it just seems this way because when the outer timer(10s) triggers it enables the button which in turn continues the animation from where it was before the button was disabled. So it seems that the animation starts when the outer timer triggers but in fact it just continues the old animation.

Calling gui.set_enabled(self.button, false) just pauses the scaling animation, doesn’t cancel it. You have to cancel it yourself and also set the scale to 1, so next time the outer timer triggers the button has no animation running and has scale 1. It could look something like this:

	gui.set_enabled(self.button, false)
	gui.cancel_animations(self.button,"scale")
	gui.set_scale(self.button, vmath.vector3(1,1,1))

I made a video:


This is the projet from the video:
Button Test.zip (79.8 KB)

Also the blurry font issue doesn’t happen on Full HD monitors. It happens on 1280 x 720. Unfortunately for some reason a lot of laptops still use that resolution.