Limit framerate to exactly 60

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Nilxoc
Prole
Posts: 2
Joined: Wed Aug 31, 2016 5:37 pm

Limit framerate to exactly 60

Post by Nilxoc »

I'm developing a Fighting Game, meaning I actually need it to run at exactly 60 Frames per second.My problem is that if I try to achieve that using

Code: Select all

love.timer.sleep(1/60-frame_time)
I get 62 FPS instead of 60,presumably due to rounding errors? Obviously VSync limits FPS to 60 on most monitors but that's not something I want to rely on , and is also not the purpose of Vsync.

So to be clear I need graphics and logic to update 60 times per second, but can only get 62.I'm telling the FPS using love.timer.getFPS()

Here's my love.run:

Code: Select all

function love.run()
 
	if love.math then
		love.math.setRandomSeed(os.time())
	end
 
	if love.load then love.load(arg) end
 
	-- We don't want the first frame's dt to include time taken by love.load.
	if love.timer then love.timer.step() end
 
	local dt = 0
 
	-- Main loop time.
	while true do
		local start_time = love.timer.getTime()
		-- Process events.
		if love.event then
			love.event.pump()
			for name, a,b,c,d,e,f in love.event.poll() do
				if name == "quit" then
					if not love.quit or not love.quit() then
						return a
					end
				end
				love.handlers[name](a,b,c,d,e,f)
			end
		end
 
		-- Update dt, as we'll be passing it to update
		if love.timer then
			love.timer.step()
			dt = love.timer.getDelta()
		end
		
		-- Call update and draw
		if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
 
		if love.graphics and love.graphics.isActive() then
			love.graphics.clear(love.graphics.getBackgroundColor())
			love.graphics.origin()
			if love.draw then love.draw() end
			love.graphics.present()
		end
		local end_time = love.timer.getTime()
		local frame_time = end_time - start_time
		
		if love.timer then love.timer.sleep(1/60-frame_time) end
	end
 
end
The .love file is attached

Thanks for reading, any help would be appreciated ;)
Attachments
Fighter.love
(46.82 KiB) Downloaded 291 times
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Limit framerate to exactly 60

Post by zorg »

You want the logic to run at exactly 60 times per second, not the frames that are displayed.

Sadly, Löve has the graphics running in the main thread, so you can't really achieve perfect timings.

I personally don't use vsync (not only because two of my 6 screens force löve to run at 1/4 FPS), and instead, i just accumulate the deltatime until it's over the amount i want one tick to take, then i call love.update once, and subtract the tickrate. Nice and static tickrate of whatever i want. New problems do arise from this solution though, depending on whether you also call love.draw inside that "fixed" timestep loop, or not. In neither case will you be guaranteed to have draws exactly when the screen refreshes, so you might get tears. Not only that, if you separate out the rendering from the logic (via another, similar loop, but that running at the vsync rate of the screen/monitor, for example), then you'd need to interpolate positions to get a more smoother result.

In any case, the above was my solution, and it did work for me, for the most part.

Code: Select all

-- This is a different example code because touching the default love.run is not necessary (in the no vsync scenario), and mine is a bit more specifically tailored... so this is a bit more minimal.
local tickPeriod = 1/50 -- seconds per tick
local accumulator = 0.0
function love.update(dt)
  accumulator = accumulator + dt
  if accumulator >= tickPeriod then
    -- Here be your fixed timestep.
    acumulator = accumulator - tickPeriod
  end
end
-- love.draw's case is more complex.
You can edit this further to protect you against gigantic deltas introduced by dragging the window on some Os-es, or using a while loop instead of a for, so you can ensure it always tries to process all ticks as quickly as possible, but that may introduce more and more problems.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: Limit framerate to exactly 60

Post by KayleMaster »

What about LOVE's tutorial on capping fps?

Code: Select all

function love.load()
   min_dt = 1/30 --fps
   next_time = love.timer.getTime()
end
 
function love.update(dt)
   next_time = next_time + min_dt
 
   --rest of function here
end
 
function love.draw()
   --rest of function here
 
   local cur_time = love.timer.getTime()
   if next_time <= cur_time then
      next_time = cur_time
      return
   end
   love.timer.sleep(next_time - cur_time)
end
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Limit framerate to exactly 60

Post by zorg »

@KayleMaster: That caps frames per second, it does not give you a constant rate for logic, or anything Nilxoc wanted to do.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Nilxoc
Prole
Posts: 2
Joined: Wed Aug 31, 2016 5:37 pm

Re: Limit framerate to exactly 60

Post by Nilxoc »

Thanks, I tried zorg's suggestion and it got me as close as I will probably get. The tick rate now fluctuates around sixty by a maximum of 0.1 above or below.I appreciate your answers.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Limit framerate to exactly 60

Post by raidho36 »

I see you're not aware of this little thing, but monitors don't actually run at exactly 60 Hz, it's always something like 59.85 or 61.6, things like that. You could make them run at exactly right rate but that takes high precision calibrated oscillator with temperature compensation circuitry, those are expensive and are not found in regular cheap consumer monitors. So yeah you either gonna have to abandon approach of running at exactly specific frame rate, or emulate specific framerate within some degree of accuracy while letting the screen run at any frequency.
KayleMaster
Party member
Posts: 234
Joined: Mon Aug 29, 2016 8:51 am

Re: Limit framerate to exactly 60

Post by KayleMaster »

zorg wrote:@KayleMaster: That caps frames per second, it does not give you a constant rate for logic, or anything Nilxoc wanted to do.
Ugh, here I am again, with my "GML" knowledge where fps and game logic is the same thing. (well at least you could uncouple them tho)
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Limit framerate to exactly 60

Post by zorg »

It's "the same thing" by default in löve as well, except the question was how it could be decoupled.
Not saying my solution was the best or anything, i'm sure with enough mucking around, one could implement a stupendously better way of doing this. Like editing the C++ source, making all the graphics function on a separate thread, and sending messages across the main thread and the graphics thread, probably achieving Ori and the blind forest levels of smoothness. :)
For what it's worth, i started out with turtle graphics (logo), not that it has anything to do with lua or löve. :3
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Shadowblitz16
Citizen
Posts: 73
Joined: Wed Oct 28, 2015 11:18 pm

Re: Limit framerate to exactly 60

Post by Shadowblitz16 »

@zorg I would like to know how to safeguard against gigantic deltas.

I am getting dt's over 1 so your code isn't working for me

Code: Select all

local tickPeriod = 1/1 -- seconds per tick
local accumulator = 0.0
function love.update(dt)
  accumulator = accumulator + dt
  if accumulator >= tickPeriod then
    game:update()
    acumulator = accumulator - tickPeriod
  end
end
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Limit framerate to exactly 60

Post by zorg »

Shadowblitz16 wrote: Fri Mar 22, 2019 12:14 am @zorg I would like to know how to safeguard against gigantic deltas.

I am getting dt's over 1 so your code isn't working for me

Code: Select all

local tickPeriod = 1/1 -- seconds per tick
local accumulator = 0.0
function love.update(dt)
  accumulator = accumulator + dt
  if accumulator >= tickPeriod then
    game:update()
    acumulator = accumulator - tickPeriod
  end
end
Your tick period is 1 tick per second.
You also wrote "acumulator" with one 'c' instead of accumulator in the equation.
But even if you have it correct in your code, it might be an issue with the fact that i used an IF instead of a for loop, and if your dt is larger than the tickPeriod, you might be running into the fact that your game:update() function is too processing heavy...
or you could try replacing the if with a while loop.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests