Achieving a built-in framerate cap for my 2D gui client-based game

Hey all, so I’ve been trying to make my custom game engine FPS unlocker proof, as the game currently relies on the client’s framerate to update the simulation, and if the user uses an FPS unlocker, the game will run above 60 Hz
(which will speed up the simulation and make the game appear sped up)

Current method:

-- Runs at 60 Hz (Assuming an FPS unlocker isn't being used)
RunService.RenderStepped:Connect(function()
	UpdateSimulation()
	Canvas:Update() -- Render the image
end)

Attempt at framerate cap:

-- With this code, the simulation now only seems to update at around 40 Hz??

local UpdateRate = 1 / 60 -- 60 FPS/Hz
local LastFrame = os.clock()

RunService.RenderStepped:Connect(function()
	if os.clock() > LastFrame + UpdateRate then
        LastFrame = os.clock()
		UpdateSimulation()
	end

	Canvas:Update() -- Render the image
end)

Can someone explain to me why this is causing unpredictable results and my game is now running below 60 Hz with this applied? It really shouldn’t be and i’m really confused as to why this is happening. I could also just be missing something entirely.

2 Likes

Does using task.wait(UpdateRate) work?

How would I implement task.wait() into my RenderStepped event?

1 Like

No but replacing the RenderStepped event with task.wait()

I need the RenderStepped event for other things outside the simulation. Such as rendering (which needs to be FPS based)

I think he mean while task.wait(UpdateRate) do !

1 Like

I can’t just use a while loop in this case. I still the need to game to use RenderStepped, but with a limit on the amount of times that event can fire (such as someone using an FPS unlocker). The update rate should be able to go below 60, but not above if that makes sense

2 Likes

Ok but why not do this :

while task.wait(1/60) do
    UpdateSimulation()
end

RunService.RenderStepped:Connect(Canvas:Update())
RunService.RenderStepped:Connect(function()
	if os.clock() > LastFrame and os.clock() == LastFrame + UpdateRate then
        LastFrame = os.clock()
		UpdateSimulation()
	end

	Canvas:Update() -- Render the image
end)

I think that should work but I’m not sure,
Or maybe using 2 RenderStepped connections but using :ConnectParallel()

It’s 100% sure that the os.clock() will be greater than LastFrame and you can’t check if it will be equal cause there’s litteraly no chance that it would be equal the script of @Ethanthegrand14 seems to be good, I don’t really get why it does not work …

1 Like

Oh right.
Maybe it’s just lag and he needs to use ConnectParallel?

1 Like

I don’t know what is ConnectParallel :man_shrugging:
EDIT : i searched up and I think that’s not it !

1 Like

ConnectParallel is like Connect but if you have 2 Connections and you use ConnectParallel so that it’ll run both of the code simultaniously.

Sorry for my broken english there.

This looks weird to me… I don’t know why, but have you tried

local now = os.clock()
if ((now - LastFrame) >= UpdateRate) then
   LastFrame = now
   UpdateSimulation()
end

Exact same affect as my code. Still seems to run at around 40-50 Hz

I tried out the while true loop with the task.wait, it is even slower than before now. it looks like it’s only calling the function at around 40 Hz

If you want your game to run at a fixed timestep you should do something like this.

local Timestep = 1/60
local Accumulator = 0

RunService.RenderStepped:Connect(function(dt)
    Accumulator += dt

    while Accumulator >= Timestep do
        UpdateSimulation(Timestep)
        Accumulator -= Timestep
    end

    Canvas:Update() -- Render new game state.
end)

But you should really be writing your UpdateSimulation() function to handle a variable delta time anyway.

1 Like
local UpdateRate = 1 / 60 -- 60 FPS/Hz
local LastFrame = os.clock()

RunService.RenderStepped:Connect(function()
    local sum = LastFrame + UpdateRate
    local difference = os.clock() - sum
	if difference  > 0 then
        LastFrame = os.clock() - difference
		UpdateSimulation()
	end

	Canvas:Update() -- Render the image
end)

you need to take into account the time error i think? which you have not done.

1 Like
local FPS = 60
local clock = tick()


while true do
    while clock + 1 / FPS > tick() do end
    wait()
     --update here
    clock = tick()
end

Weird, honestly. I think you are much better off adjusting your simulation code to be FPS independent and scale it according to the deltaTime between each frame, it may be easier than you think!

1 Like