Page 2 of 4
Re: Stuttering & Fixed Timesteps
Posted: Thu Sep 29, 2011 11:45 pm
by Bryant
Ha, good points Taehl. I've only ever worked with variable timesteps before, so I'm learning about this topic as I go along
One of the
XNA developers summed up the "Fix Your Timestep" article in a way I found helpful:
A few games use a hybrid approach (as in the link that was posted above). They use a variable timestep for Draw, calling it as fast as the GPU retrace will allow, but apply fixed timestep CPU logic to control the frequency of Update. To smooth out the visual results (since Draw may now be called half way in between two Update time locations, or there could even be more than one Draw per Update), they interpolate the game state some fraction between two different Update results when drawing at times that do not exactly align to a fixed timestep tick point. This allows them to maintain as good as possible monitor sync, while still being able to write their physics assuming a nice simple fixed timestep. The downside is the extra code for lining up the two timelines and doing the state interpolation, which can be a serious PITA. There's nothing built into the XNA Framework to help you if you want to use this mode: you'd have to start with our variable timestep and then implement your own logic on top of that.
The accumulator method takes care of the fixed update and variable draw timesteps. The missing link is the state interpolation to smooth out the end results. I have no idea what an implementation of that would look like, but I guess I just need to take a stab at it
(Unless someone here has already done it?
)
Re: Stuttering & Fixed Timesteps
Posted: Fri Sep 30, 2011 1:05 am
by Taehl
State interpolation sounds like an awful idea to me. Not only does it mean you need to keep the current state, but also the last state (meaning, twice as much memory usage); you need to do interpolation on all that (meaning eating more CPU); and the controls would always lag at greater than or equal to [the time it takes to update the state] + [the time you wait between state updates] (since the time the player touches a control is in the "future" of the game's state, since you have to wait for the next update before the control will have any affect).
So, you're using more RAM and CPU, and intentionally lagging the controls (a sin, in my mind!), all for what, exactly? I fail to see how this scheme would offer a smoother experience than without it (to say nothing of performance in general).
Re: Stuttering & Fixed Timesteps
Posted: Fri Sep 30, 2011 1:58 am
by slime
Taehl wrote:State interpolation sounds like an awful idea to me. Not only does it mean you need to keep the current state, but also the last state (meaning, twice as much memory usage); you need to do interpolation on all that (meaning eating more CPU); and the controls would always lag at greater than or equal to [the time it takes to update the state] + [the time you wait between state updates] (since the time the player touches a control is in the "future" of the game's state, since you have to wait for the next update before the control will have any affect).
So, you're using more RAM and CPU, and intentionally lagging the controls (a sin, in my mind!), all for what, exactly? I fail to see how this scheme would offer a smoother experience than without it (to say nothing of performance in general).
The linked article (titled "Fix your timestep!") explains it very nicely. The extra amount of CPU and RAM usage is negligible, and a fixed physics timestep is necessary in some situations - because of the way physics engines work, a deterministic simulation isn't really possible with a variable timestep.
A more optimal solution would be to separate rendering from updating entirely by putting everything except rendering into its own thread, but that's another topic.
Re: Stuttering & Fixed Timesteps
Posted: Fri Sep 30, 2011 3:20 am
by Xgoff
Bryant wrote: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?
no i've just been using the default setup for now. actually whether or not i even make this game in love depends on whether or not i decide on 3d graphics (technically it'd still have 2d gameplay, but adding all the relevant 3d code would probably be annoying)
although atm i'm working on other things
Re: Stuttering & Fixed Timesteps
Posted: Sat Oct 01, 2011 10:26 am
by ivan
First of, the box2d documentation says that b2worlds should be updated with a constant timestep.
If your timestep varies you may get unstable behavior with certain joints, etc.
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
That's not exactly how accumulators are supposed to work.
Your dt varies if accum is < 1/50.
Also, your accumulator doesn't really 'accumulate' anything since you set it to "dt" at each update.
If you are getting 'penetration' (usually called 'tunneling') with a particular body you might want to set its "bullet" flag to true.
The following code is from a short
tutorial that I wrote on box2D:
Code: Select all
accumulator = 0
function Update ( delta )
accumulator = accumulator + delta
while accumulator > 0.016 do
world:Step ( 0.016, 10 )
accumulator = accumulator - 0.016
end
end
Re: Stuttering & Fixed Timesteps
Posted: Sat Oct 01, 2011 11:12 am
by pancakepalace
Doesn't the stuttering occur because you are not moving the player the same pixel distance on every instance of love.draw(). Your pixel distance depends on many variables including some that are floating point like dt so that after being floored to the nearest x and y, your distance changes making a stutter.
Couldn't you simply set the player speed to an integer that doesn't change. This would mean that at every dt interval your player moves this speed (or distance) in pixels. If you then want to calculate the speed per second so that it doesn't depend on framerate your could then get the number of FPS from dt and adjust the speed accordingly.
I very well might be missing something.
Code: Select all
player = {}
function love.load()
player.image = love.graphics.newImage( 'content/textures/player.png' )
player.x = 0
player.y = 0
player.speed = 3
love.graphics.setBackgroundColor( 245, 245, 245 )
end
function love.update(dt)
if love.keyboard.isDown( "up" ) then
player.y = player.y - player.speed
end
if love.keyboard.isDown( "down" ) then
player.y = player.y + player.speed
end
if love.keyboard.isDown( "left" ) then
player.x = player.x - player.speed
end
if love.keyboard.isDown( "right" ) then
player.x = player.x + player.speed
end
end
function love.draw()
love.graphics.draw( player.image, player.x, player.y )
end
Re: Stuttering & Fixed Timesteps
Posted: Sat Oct 01, 2011 7:17 pm
by Taehl
ivan wrote:
That's not exactly how accumulators are supposed to work.
Your dt varies if accum is < 1/50.
Also, your accumulator doesn't really 'accumulate' anything since you set it to "dt" at each update.
If you are getting 'penetration' (usually called 'tunneling') with a particular body you might want to set its "bullet" flag to true.
All well and good. But I'm not using Box2D, so I don't have a "bullet" flag to set to true. My game works fine with variable timesteps, it just needs to make sure that things don't move more than half a meter in one update (hence, a (quite high) speed limit and the code I posted). Anyway, it doesn't matter if it doesn't work "exactly" like some article says it should - it works flawlessly. Underlife can be running at 2 FPS and still have no problems.
And who said anything about Box2D anyway?
Re: Stuttering & Fixed Timesteps
Posted: Mon Oct 03, 2011 10:42 pm
by Bryant
A friend of mine gave me another technique to try out. It's not a true fixed timestep update loop, so it might not work for my situation. But it produces relatively smooth, jitter-free movement. It's still not perfect, but it seems to produce, at least for me, smoother results than previous update loops we've discussed.
Here's the meat of it:
Code: Select all
function love.run()
if love.load then love.load(arg) end
local totalTime = 0.0
local deltaTime = 1 / 200
local currentTime = love.timer.getMicroTime()
local accumulator = 0.0
-- Main loop time.
while true do
local newTime = love.timer.getMicroTime()
local frameTime = newTime - currentTime
if frameTime > 0.25 then
frameTime = 0.25
elseif frameTime < 0.0 then -- Handle the case where getMicroTime() rolls over back to 0
frameTime = deltaTime
end
currentTime = newTime
accumulator = accumulator + frameTime
while( accumulator >= deltaTime ) do
if love.update then love.update(1.0) end
totalTime = totalTime + deltaTime
accumulator = accumulator - deltaTime
end
if love.update then love.update(accumulator / deltaTime) end
accumulator = 0.0
if love.graphics then
love.graphics.clear()
if love.draw then love.draw() end
love.graphics.present()
end
-- See the attached .love file for more
There are 2 major things to note. I'm running the update loop at 200 frames per second, passing a delta time of 1 to each update. If, at the end of those 200 updates, I still have some unaccounted for time, I'll run update() again with a fraction of my previous delta time (this step is what prevents it from being a perfect fixed timestep update loop).
How does this run on your machines? Jittery? Smooth?
Re: Stuttering & Fixed Timesteps
Posted: Mon Oct 03, 2011 11:03 pm
by kraftman
smoooooth
Re: Stuttering & Fixed Timesteps
Posted: Tue Oct 04, 2011 5:10 am
by ivan
My game works fine with variable timesteps, it just needs to make sure that things don't move more than half a meter in one update
Bryant wrote:at the end of those 200 updates, I still have some unaccounted for time, I'll run update() again with a fraction of my previous delta time (this step is what prevents it from being a perfect fixed timestep update loop)
I don't really understand why you would want to use accumulators if you delta is allowed to vary.
A simpler solution would be to simply clamp the delta value.