The correct way to do countdowns

I see a lot of code on the developer forum that implements timers like this:

local timerGui = gui.TimerText
for i = 60, 1, -1 do
  timerGui.Text = i
  task.wait(1)
end

At a basic glance this seems like very basic code that does the task that you want it to, for 60 times, counting down from 60 to 1, set the text of timer gui to the indexer and wait 1 second.

Well, in actuality, this is actually a really bad way of doing timers

task.wait and implicit delays

task.wait will never wait exactly the period it’s provided, there will always be a small amount of variance, and this is in a perfect example. Other Roblox systems such as Clone or a player joining can stop task.wait from resuming in a timely manner.

The timer from earlier that is meant to wait 60 seconds could wait much longer because the engine wont resume task.wait in 1 second because it got frozen up by a Clone operation, for example.

Even assuming a variance of 1ms, this will still build up to 60ms if we try to wait 60 times in the for loop. I get this is negliable, but on a factor of larger variance, it does eventually become noticable

The correct way of doing timers

This is one method, but it implements the example from above PROPERLY

task.wait returns the time it actually waited, and here’s how we’re going to fix the example posted above.

print(task.wait(1)) --> 1.00018481

Instead of iterating in a way where it updates a counter and calls task.wait to wait 1 second, we’re going to tell task.wait to try and wait in frame intervals, but instead of blindly trusting that it did wait exactly 1/60th of a second, we’ll use the time it actually waited

This is pretty simple, just create a variable at the top of your timer and count it down using task.wait

local delta = 60
while delta > 0 do
  delta -= task.wait()
end

delta = 0

We use task.wait with a frame delay since this makes the timer more precise over the 60 seconds that we want it to count down from. Make sure to round delta before trying to write it anywhere

timerGui.Text = math.round(delta)

Hopefully, you can use this to better implement timer logic.

3 Likes

This is in good spirits, but you ended up making your code more confusing. I wouldnt say this is the correct way to do countdowns. If you truly need that much precision it’d be better to just do a if statement check on some time offset using os.clock() and do the countdown for the GUI like normal but in a coroutine so it doesnt yield the thread. Then you run the code that needs so much precision in the coroutine that has that if statement check. You do realize that just setting the text property will take way more time than yielding with task.wait()?

1 Like

My countdowns involve me writing out every single line of code to subtract time off of a textlabel

local label = script.parent.parent.textlabel.text
label = 5
Wait(1) 
Label = 4 
Wait(1)
label = 3
Wait(1)
Label = 2
Wait(1)
Label = 1
Wait(1)
script.parent.parent.parent:Destroy()
3 Likes

That code is so ingenious it wouldn’t even work!

How precise is this method, really?

Implement tick() instead of faking deltas. It’s more reliable in my opinion, or just use the reverse for loop method everybody uses (even professionals like sleitnick).