Best method to keep track of time for in-game timers?

Hey all, I’m working on a game and I have several systems that are all dependent on an in-game timer. It is important that the timer is accurate and consistent between all of them. As such, heartbeat will not be sufficient as the amount of times it fires is dependent on your framerate. Every time I hear about wait(), it’s always someone saying not to use it, so I suspect that won’t be sufficient either.

I am looking for my timer to increment every .1 seconds.

If you’ve got any suggestions for what I should use, please let me know.

I personally just use a value in replicatedstorage to control game time, but that only needs to be accurate to the second for me.

Perhaps you could use FireAllClients on a repeated basis to control game time for them. However, you are going to be at the mercy of network latency as some gamers might have high round trip times and so may not sync as accurately as those with lower times.

its never going to be in sync when considering players ping, just update their UI from the server directly. (or FireAllClients, the delay is the same), no matter what though when the time is over on the server just make them do whatever, even if their timer lags behind a bit.

this is normal for all games and there is no way around players latency. (besides some extra math on the players ping that can increase accuracy but it’ll never be fully in sync)

on a side note, you should look into os.time() and os.clock() for second based timers and millisecond based timers.

I forgot to mention this, but the clock will be exclusively running on the client’s side. I take it there isn’t an os equivalent to wait(). Would just looping through a wait() and updating the timer with the proper time passed between waits using the differences in os.clock make sense?

Use CloneTrooper1019’s Thread module.

Essentially a library of Lua schedulers and wait functions but they aren’t throttled by ROBLOX’s scheduler limit (because they are bound to a Heartbeat).

-- Example use-case.
local Fast = require(ReplicatedStorage.Fast)

local IMPORTANT_TASK_ENABLED = true

while IMPORTANT_TASK_ENABLED do
    -- code --
    Fast:Wait(0.1)
end

No, wait() can take multiple seconds to run and can be very unreliable (more information here).

At the beginning of the round you could use os.time() + roundDuration to get the time at which the round will end. Then every frame, you can update the timer to be the amount of seconds until that end time.

For example:

-- LocalScript
local RunService = game:GetService("RunService")

local roundDuration = 120 -- seconds

function startTimer()
    local roundEndTime = os.time() + roundDuration -- the unix time that the timer will hit 0

    RunService.RenderStepped:Connect(function() -- every frame do this:
        local secondsLeft = roundEndTime - os.time() -- game end time minus current time equals remaining time
        timer.Text = secondsLeft -- update timer to display correct amount of seconds
    end)
end

startTimer()

You could even have the server fire down an event to all the clients when it’s time to start the timer and they will all run it and always end no more than a second apart.

1 Like

@TalonMidnight there is no need for that, the operation he is wanting is basic logic.

(that is like finding a module to handle multiplication when the original way to handle multiplication is already very compact, a*b=c instead of c = module:Multiply(a,b))

also real threading doesn’t exist in lua, so there will be limitations no matter what even if they were bounded to Heartbeat, right?

connect to RunService events directly and use the delta time argument, or you can use the events :Wait() function in a loop and use your own way to handle time. (like os.time() or os.clock())

1 Like
2 Likes