Stuttering & Fixed Timesteps

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.
User avatar
Bryant
Prole
Posts: 8
Joined: Wed Sep 28, 2011 12:12 am
Location: Vancouver, BC, Canada
Contact:

Stuttering & Fixed Timesteps

Post by Bryant »

Hey everyone :) I'm new to LÖVE and Lua, and I've been messing around with the examples you've all posted online. The level of community support this framework has is refreshing :)

I created a simple project that features a box you can move around with the arrow keys. I'm noticing, however, that the box stutters on occasion. It's minor, and you probably wouldn't notice it if a lot was happening on the screen, but it's definitely there.

I thought this might be because the update and draw functions were falling out of sync. But that shouldn't be the case because love.run calls draw() immediately after update() every time, right?

I tried a few different approaches to my update loop which you can see commented out in the .love file. I tried a fixed timestep without dt, a fixed timestep with dt, and a variable timestep with dt. They all have stuttering problems :(

Next I tried turning off vsync, but that slowed everything down to a crawl (shouldn't it have sped everything up? I might not have tested it with the three update loop configurations...).

Is there any way around this? I know a lot of people link to this article about fixing your timestep. Maybe I'm missing the step about integrating or interpolating my moving object(s)?

The default LÖVE app with the spinning logo and scrolling starfield runs so smoothly! What am I doing differently? :?

Thank you all for the help :)
Attachments
StutterTest.love
(7.6 KiB) Downloaded 431 times
Last edited by Bryant on Thu Sep 29, 2011 7:00 am, edited 1 time in total.
User avatar
T-Bone
Inner party member
Posts: 1492
Joined: Thu Jun 09, 2011 9:03 am

Re: Stuttering

Post by T-Bone »

The accumulator is wierd. Don't do it. The "variable timestep with dt" is better. Doesn't stutter at all for me.

draw() and update(dt) are run independently of each other. That's a good thing.
User avatar
Bryant
Prole
Posts: 8
Joined: Wed Sep 28, 2011 12:12 am
Location: Vancouver, BC, Canada
Contact:

Re: Stuttering

Post by Bryant »

I agree that the accumulator is weird :) But what other option is there for games that require a high level of precision like a competitive fighting game or a rhythm game? In those cases a single frame makes all the difference, and a variable timestep won't suffice.
User avatar
slime
Solid Snayke
Posts: 3132
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Stuttering & Fixed Timesteps

Post by slime »

Bryant wrote:Is there any way around this? I know a lot of people link to this article about fixing your timestep. Maybe I'm missing the step about integrating or interpolating my moving object(s)?

The default LÖVE app with the spinning logo and scrolling starfield runs so smoothly! What am I doing differently? :?

Thank you all for the help :)
Yeah, for a fixed update loop with a variable framerate (which is generally what you want), you'll need to rewrite the love.run function to have a love.update accumulator as well as a love.draw interpolator.
The default variable update and variable framerate loop shouldn't cause any problems usually though, as long as you multiply the appropriate things by the deltatime.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: Stuttering & Fixed Timesteps

Post by Taehl »

I use an accumulator technique in my game Underlife, and it runs beautifully. Here's basically what I do:

Code: Select all

function love.update(dt)
	local accum = dt
	while accum > 0 do		-- accumulator for physics! no more penetration!
		local dt = math.min( 1/50, accum )	-- use whatever max dt value works best for you
		accum = accum - dt
		
		-- now, do whatever it is you need to do with dt
	end
end
I hope that helps you.
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: Stuttering

Post by kikito »

Bryant wrote:I agree that the accumulator is weird :) But what other option is there for games that require a high level of precision like a competitive fighting game or a rhythm game? In those cases a single frame makes all the difference, and a variable timestep won't suffice.
I might be wrong, but it seems to me that the variable timestep actually gives you more control over those cases. I don't see what problems a fixed frame rate would solve that can't be done better with a variable one.
When I write def I mean function.
User avatar
slime
Solid Snayke
Posts: 3132
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Stuttering

Post by slime »

kikito wrote:
Bryant wrote:I agree that the accumulator is weird :) But what other option is there for games that require a high level of precision like a competitive fighting game or a rhythm game? In those cases a single frame makes all the difference, and a variable timestep won't suffice.
I might be wrong, but it seems to me that the variable timestep actually gives you more control over those cases. I don't see what problems a fixed frame rate would solve that can't be done better with a variable one.
I suggest you read http://gafferongames.com/game-physics/f ... -timestep/ (linked in the OP), it makes excellent points.
However, a fixed update timestep is usually only needed in multiplayer games with high precision physics where it's much better to be deterministic.
User avatar
Xgoff
Party member
Posts: 211
Joined: Fri Nov 19, 2010 4:20 am

Re: Stuttering

Post by Xgoff »

slime wrote:I suggest you read http://gafferongames.com/game-physics/f ... -timestep/ (linked in the OP), it makes excellent points.
However, a fixed update timestep is usually only needed in multiplayer games with high precision physics where it's much better to be deterministic.
or, in my game (assuming i ever work on it), a timing system. if i call wait() i want an object to wait *exactly* some number of ticks, not plus or minus some number of milliseconds. this also means floats are out, unless they're limited to being exactly representable
User avatar
Bryant
Prole
Posts: 8
Joined: Wed Sep 28, 2011 12:12 am
Location: Vancouver, BC, Canada
Contact:

Re: Stuttering & Fixed Timesteps

Post by Bryant »

Thanks for all the great responses!

slime -- A fixed update loop with a variable framerate is exactly what I'm looking for. I'll try rewriting love.run() with the accumulator method. But interpolating any "leftover" time in the draw() function is where I get confused (in the "Fix Your Timestep" article, this is where the author uses a "blending factor" to linearly interpolate the simulation before rendering).

Also, maybe I'm misunderstanding something, but is LÖVE's rendering really decoupled from the update loop? Just looking at the default implementation of love.run(), it looks like update() and draw() are called on the same timestep. So draw() will never be called more frequently than update(). If anything, with the accumulator method, draw() might be called *less* frequently. If that's true, jittering/stuttering seems unavoidable :(

Xgoff -- Floating point precision is exactly the problem I have with a variable timestep :( Have you tried implementing your own fixed timestep in LÖVE yet?

Thanks again, everyone. As a side note, here's a good article explaining XNA's approach to the problem.

EDIT: Two more good XNA articles explaining the problem and potential solutions: Part 1 and Part 2.
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: Stuttering & Fixed Timesteps

Post by Taehl »

Bryant wrote:slime -- A fixed update loop with a variable framerate is exactly what I'm looking for. I'll try rewriting love.run() with the accumulator method. But interpolating any "leftover" time in the draw() function is where I get confused (in the "Fix Your Timestep" article, this is where the author uses a "blending factor" to linearly interpolate the simulation before rendering).
The accumulator code I posted above is aimed at running updates more times than it draws. Or as the article you linked to phrased it, "Call Update extra times (without calling Draw) until we catch up.".
Bryant wrote:Also, maybe I'm misunderstanding something, but is LÖVE's rendering really decoupled from the update loop? Just looking at the default implementation of love.run(), it looks like update() and draw() are called on the same timestep. So draw() will never be called more frequently than update(). If anything, with the accumulator method, draw() might be called *less* frequently. If that's true, jittering/stuttering seems unavoidable :(
You're right - with Love2D's default love.run(), you get exactly one love.update() and then one love.draw(). And with my accumulator code, you'll get more updates than drawn frames.

If you want to draw frames faster than you're updating the game, you'll need some sort of bizarre code which predicts object movement and integrates the prediction with reality once an update rolls around. And you'll need to do all this faster than it takes to just update the world. But honestly, this strikes me as patently crazy.

Also, in your .love file, FIXED TIMESTEP WITH DT and VARIABLE TIMESTEP WITH DT both run smoothly for me, while FIXED TIMESTEP WITHOUT DT is slightly less smooth.
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
Post Reply

Who is online

Users browsing this forum: No registered users and 53 guests