Most efficient way to create a simple countdown?

Hello! I want to make a simple countdown that counts down from 10 to 0 using 0.1 intervals, however I don’t know what is the most efficient way to do it.

The countdown is being stored in a NumberValue.

I have tried using wait(0.1) in a loop but it takes longer than my desired 10 seconds (as shown in the gif below).
https://i.imgur.com/tZ8FvoH.gif

Source code
local gui = script.Parent
local timer = gui:WaitForChild("Timer") -- TextLabel that shows the timer being updated by the while loop
local passed = gui:WaitForChild("10SecondsHavePassed") -- the TextLabel with the text "10 Seconds Have Passed" (as shown in the gif above)
local x = gui.Value -- the value with the countdown

function roundNumber(num, numDecimalPlaces)
	return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num))
end

spawn(function() -- this function enables a gui after 10 seconds have passed
	wait(10)
	passed.Visible = true
end)

while x.Value > 0 do
	wait(.1)
	x.Value = x.Value-.1
	timer.Text = roundNumber(x, 1)
end

I have also tried to round using TweenService, which works but instead of incrementing by 0.1 it increments by a much smaller number, updating the NumberValue multiple times per second which halts performance.
https://i.imgur.com/rRcAKC9.gif

Source code
local TweenService = game:GetService("TweenService")
local t_info = TweenInfo.new(10, Enum.EasingStyle.Linear)

local gui = script.Parent
local timer = gui:WaitForChild("Timer") -- TextLabel that shows the rounded timer based on the tween
local passed = gui:WaitForChild("10SecondsHavePassed") -- the TextLabel with the text "10 Seconds Have Passed" (as shown in the gif above)
local TimesRan = gui:WaitForChild("TimesRan") -- the TextLabel that shows the amount of times the value has been updated (as shown in the gif above)
local x = gui:WaitForChild("Value") -- the value with the countdown

local times_ran = 0

function roundNumber(num, numDecimalPlaces)
	return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num))
end

spawn(function()
	wait(10)
	passed.Visible = true
end)

local function test()
	timer.Text = roundNumber(x.Value, 1)
end

x.Value = 10
TweenService:Create(x, t_info, {Value = 0}):Play()

x.Changed:Connect(function()
	times_ran = times_ran + 1
	TimesRan.Text = "Times ran - ".. tostring(times_ran)
	test()
end)

As you can see, it updated 450 times when it should’ve updated 100 times, and when you have multiple of these countdowns in your game it starts to add up.

Any way I can make this update 100 times per second while still being exactly 10 seconds?

2 Likes
local function timer(t)
	for i = t, 0, -task.wait() do
		print(i)
	end
end

Most implementations will have a similar efficiency but this is likely the shortest. Note this timer is dynamic as well (can be initiated from any desired start point).

3 Likes

I believe the minimum wait time is 0.3 (and a bunch of change) seconds, meaning it is replacing 0.1 with 0.3

Instead, I would do the following:

local Val = 10
local t
while Val>0 do
    if not t then t=tick() end
    wait()
    t=tick()
    Val-=t
end
print(math.ceil(Val))

They want to go in intervals of 0.1 however.

From here, you can make sure you find the tenths place. Let me know if you need help with that.

Oh thanks, I wasn’t aware, I just read the thread’s initial prompt about a “most efficient timer”.

1 Like
local Time	= 10
local Delta	= tick() + Time

repeat
	game["Run Service"].Heartbeat:Wait() --RenderStepped on the client for better results
	warn(math.floor((Delta - tick()) * 100)/100)
until Delta <= tick()

Thank you for your response! Can you please explain a bit what the code is doing? I tried timer(10) and it spammed my output as shown below.
https://i.imgur.com/DHfdhHy.gif

task.wait() returns the length of time elapsed during its wait, this returned value is subtracted from the timer in the for loop until the timer reaches 0.

I experimented a bit and found out that you can tween Integer values which does exactly what I want.

Here is the source code with an example for any one interested or anyone in the future who has the same problem as me:
https://i.imgur.com/4mmd1QM.gif

local tmp = game:GetService("ReplicatedStorage"):WaitForChild("$temp") -- temporary folder
local TweenService = game:GetService("TweenService")
local instance = Instance.new("IntValue", tmp)
local countdown = nil


local function StartCoundown(timer)
	warn("Starting! cooldown")
	local t_info = TweenInfo.new(timer, Enum.EasingStyle.Linear)
	instance.Value = timer*10
	countdown = TweenService:Create(instance, t_info, {Value = 0})
	countdown:Play()
end

local function PauseCountdown()
	warn("Pausing cooldown")
	if countdown then countdown:Pause() end
end

local function ResumeCountdown()
	warn("Resuming cooldown")
	if countdown then countdown:Play() end
end

local function StopCountdown()
	warn("Stopping cooldown")
	if countdown then countdown:Pause() end
	countdown = nil
	instance.Value = 0
end

local function PrintTimer()
	print(tonumber(instance.Value)/10)
end

instance.Changed:Connect(PrintTimer)


StartCoundown(10)
wait(10)
print("10 seconds have passed!")

And yes, I added methods so you can resume/pause/stop the countdown as you wish. :grinning:

3 Likes

Does using intValue or boolValue cause the same problem?
When I play a game with 20 players, the count sometimes disappears. This method seems to be using the client. Are you exposed to the same time as others?