Accurate Timer / Counter Handling in Roblox Scripting

I’ve been creating a precise timer and counter within Roblox, aiming for accuracy down to tenths of a second or even smaller intervals. I’ve explored three different methods so far and seek your insights on the best approach. I’d appreciate hearing if you have an alternative method to suggest.

Existing approaches I have encountered:

  1. Using os.time():
local LastTimePassedOsTime = os.time()

RunService.Heartbeat:Connect(function()
	local OSTimePassed = os.time() - LastTimePassedOsTime

	if OSTimePassed > 0 then
		LastTimePassedOsTime = os.time()
		-- do something
	end
end)
  1. Using os.clock():
local LastTimePassedOsClock = os.clock()

RunService.Heartbeat:Connect(function()
	local OsClockTimePassed = os.clock() - LastTimePassedOsClock

	if OsClockTimePassed > 0 then
		LastTimePassedOsClock = os.clock()
		-- Do Something
	end
end)
  1. Using workspace.DistributedGameTime:
local LastTimePassedWorkspace = workspace.DistributedGameTime

workspace:GetPropertyChangedSignal("DistributedGameTime"):Connect(function()
	local workspaceTimePassed = workspace.DistributedGameTime - LastTimePassedWorkspace

	if workspaceTimePassed > 0 then
		LastTimePassedWorkspace = workspace.DistributedGameTime

		-- Do something 
	end
end)

Code Used to Test:

local StartOsTime = os.time()
local StartOsClock = os.clock()

local LastTimePassedOsTime = os.time()
local LastTimePassedOsClock = os.clock()
local LastTimePassedWorkspace = workspace.DistributedGameTime



RunService.Heartbeat:Connect(function()
	local OSTimePassed = os.time() - LastTimePassedOsTime
	local OsClockTimePassed = os.clock() - LastTimePassedOsClock
	local workspaceTimePassed = workspace.DistributedGameTime - LastTimePassedWorkspace
	
	if OSTimePassed > 0 then
		LastTimePassedOsTime = os.time()
	end
	
	if OsClockTimePassed > 0 then
		LastTimePassedOsClock = os.clock()
	end
	
	if workspaceTimePassed > 0 then
		LastTimePassedWorkspace = workspace.DistributedGameTime
	end
	
	print("Distributed Workspace - Timer: ".. workspace.DistributedGameTime.. " - TimePassed: "..workspaceTimePassed)
	print("Os.Time - Timer: ".. os.time() - StartOsTime.. " - TimePassed: "..OSTimePassed)
	print("os.Clock - Timer: ".. os.clock() - StartOsClock.. " - TimePassed: "..OsClockTimePassed)
end)

RobloxStudioBeta_Vu0XdC3zVs

Observations:

After testing these methods, all three approaches yield almost identical results in accuracy. However, there are some nuances to consider:

  • os.time() does not provide decimal values.
  • os.clock() and workspace.DistributedGameTime can include decimals, but the speed of the script execution influences their accuracy.

Usage Examples:

Here’s how I would integrate these methods into my games:

  1. Using os.clock():
-- For os.time() replace all os.clock(), and you can remove all the string formatting, and the passed time check needs to be changed to whole numbers
local StartTime = os.clock() 
local StopWatchLastTime = os.clock()
local StopWatch = 0
local DeBounce = false

RunService.Heartbeat:Connect(function()
    local TimePassed = string.format("%0.2f", os.clock() - StartTime)
    local StopWatchTimePassed = tonumber(string.format("%0.2f", os.clock() - StopWatchLastTime))
    
    if StopWatchTimePassed > 11.68 then
        StopWatch += StopWatchTimePassed
        StopWatchLastTime = os.clock()
        DeBounce = false
    end
    
    print("StopWatchTimer: " .. string.format("%0.2f", StopWatch) .. " TimePassed: " .. TimePassed .. " Debounce: " .. tostring(DeBounce))
end)

RobloxStudioBeta_poNrwRX67A

Using workspace.DistributedGameTime:

local StartTime = workspace.DistributedGameTime
local StopWatchLastTime = StartTime
local StopWatch = 0
local DeBounce = false

workspace:GetPropertyChangedSignal("DistributedGameTime"):Connect(function()
    local TimePassed = string.format("%0.2f", workspace.DistributedGameTime - StartTime)
    local StopWatchTimePassed = tonumber(string.format("%0.2f", workspace.DistributedGameTime - StopWatchLastTime))
    
    if StopWatchTimePassed > 5.68 then
        StopWatch += StopWatchTimePassed
        StopWatchLastTime = workspace.DistributedGameTime
        DeBounce = false
    end
    
    print("StopWatchTimer: " .. string.format("%0.2f", StopWatch) .. " TimePassed: " .. TimePassed .. " Debounce: " .. tostring(DeBounce))
end)

RobloxStudioBeta_UDCJ5pKuhy

Conclusion
If you have any insight or input on how or what I should be doing, whether which of these three is better or an entirely different method, I would love to hear it, and it would be beneficial to be refining my approach.

(also I did use chatgpt for everything except the coding to help me organize my post due to my low self confidence)

Any timer you make is limited in accuracy to at most as accurate as the script scheduler. However, since events in your game also only happen on that interval, that is the most accuracy which is possible to observe.

1 Like

Do you know if i should be using these methods or a loop using task.wait()

They all will wait until the next cycle, but Heartbeat, Stepped, and RenderStepped each happen at a different time relative to other phases in each cycle. Task Scheduler | Documentation - Roblox Creator Hub

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.