Event that fires regularly per second

Seemingly my game has a few continous loops that run at intervals no less than once a second. I was hoping to create a centralized solution for these loops by creating an event based system where the returned event would be fired at a desired interval. (e.g. once every x seconds)

I am aware I could accomplish this with Heartbeat/Stepped/RenderStepped in combination with tick() but I was wondering whether I missed there being an event that fired less often and thus would put less stress on the server.

3 Likes

You have HeartBeat for every frame and wait() for every other frame (courtesy of Fracticality_Alt):

In order to be as responsive as possible, what I suggest is

local RunService = game:GetService("RunService")
local desiredInterval = 2.5 --fire every 2.5 seconds
local counter = 0
RunService.Heartbeat:Connect(function(step)
	counter = counter + step  --step is time in seconds since the previous frame
	if counter >= desiredInterval then
	    counter = counter - desiredInterval
	    print("Do your loop code here")
    end
end)

Keeping track of time this way will incur virtually zero cost per frame. Don’t be afraid to use Heartbeat if you’re doing very few calculations to keep track of time (which is the case here). If your server is unresponsive or jams up, it’s not because it had to add two numbers and store the result 60 times per second. That operation takes less than a microsecond to occur. If it becomes unresponsive, it’s because of the code you want to perform every X seconds is not performant, and no amount of time management/event-trickery will save you from that.

15 Likes

If you want to avoid any time drift, you can also write it like this:

local INTERVAL = 1
local nextStep = tick() + INTERVAL

runService.Heartbeat:Connect(function(dt)
	if (tick() >= nextStep) then
		nextStep = nextStep + INTERVAL
		-- Do something
	end
end)

There will be a little drift due to floating point precision. To avoid any drift caused via precision error, you would have to count the iterations and use it as a multiplier instead. This is just an example, and is a bit overkill for most scenarios:

-- Again, this is a bit overkill. I would almost always prefer the first example above instead.

local INTERVAL = 1

local start = tick()
local nextStep = start + INTERVAL
local iter = 1

runService.Heartbeat:Connect(function(dt)
	if (tick() >= nextStep) then
		iter = iter + 1
		nextStep = start + (iter * INTERVAL)
		-- Do something
	end
end)
10 Likes