Closed slime73 closed 11 years ago
Original comment by Alex Szpakowski (Bitbucket: slime73, GitHub: slime73).
As you've already noticed, you already have the option because you can replace the run loop. Are you suggesting the default run loop should be changed to do this? If so, is there really much point considering you can change it anyway?
The fixed update step loop you've posted doesn't seem to deal with drawing interpolation, as discussed here: http://gafferongames.com/game-physics/fix-your-timestep/
Original comment by John Kaniarz (Bitbucket: jkaniarz, GitHub: jkaniarz).
It's my understanding that microsoft included it in XNA because fixed time steps are easier for beginners to understand. On a fixed time step you don't need to multiply everything by dt. Timers can just be increment by 1 every update call. Positions can be updated as x=x+1. I much prefer fixed time steps when prototyping because of the simplicity.
If I were forced to pick just one method, I would have it default to fixed timesteps because it's the better default for beginners. Beginners don't know it's possible to override the run loop (or what changes to make if they do know it's possible).
If you don't want to make it the default it would still make a good example on the wiki. Let me know if you'd prefer this and I'll rewrite it with that target in mind.
Original comment by Alex Szpakowski (Bitbucket: slime73, GitHub: slime73).
Even with a fixed timestep it's good to put your units in terms of seconds rather than frames. It requires a bit more understanding at the start, but it becomes much more intuitive down the road ("the bullet moves at 8.3 pixels per frame" versus "the bullet moves at 500 pixels per second").
The fixed update method can either be simple to use but frame / update rate will not actually be consistent (as in your code), or it can be complex to implement due to rendering interpolation but it will look and behave very well (as in the final method discussed in the article I linked.)
In my opinion, neither of those two ways is particularly satisfactory as a default, and while the variable update model is a tiny bit tougher than a naive fixed update model to figure out at first (and the simulation will vary a tiny bit at very different framerates), it's smooth and not extremely complex, so it seems like a good default to me.
Original comment by John Kaniarz (Bitbucket: jkaniarz, GitHub: jkaniarz).
Your call. My intent was to mimic XNA for my own prototyping needs. I just thought I'd share. Though, it shouldn't be stuttering... does love.graphics.present() block until the buffer swap?
An an aside: that guys solution isn't the end all. By blending between two frames it either simulates physics one frame in the future without the knowing the future controller input, or the current frame lags behind as it gets interpolated in (I didn't take the time to figure out which). The better solution is to "guess" the future with dead reckoning. That way you don't have to write custom state interpolation code; you can just use the same dead reckoning code you wrote for network synchronization.
Either way, none of these techniques would generalize well for love.
Original comment by Alex Szpakowski (Bitbucket: slime73, GitHub: slime73).
Drawing interpolation and extrapolation both have their own downsides, neither is better than the other in all ways. Which one is better for an individual game probably depends on a lot of factors specific to the game.
Calling a swap buffers function (love.graphics.present in LÖVE) won't always block. What happens is very dependent on the GPU, the driver, the driver settings, whether vsync is enabled in LÖVE, and how many frames are queued up.
With your code and with vsync enabled (on a 60Hz monitor) and targetElapsedTime set to 60, it'll probably bounce in between over- and under-shooting the target update rate, especially with that love.timer.sleep in there.
It makes it really easy for the 'perfect case' of updating and drawing at the target rate to not actually happen, since there are so many ways to make things take longer or shorter than you want, and some of them are completely out of the hands of the person writing the game.
Original comment by John Kaniarz (Bitbucket: jkaniarz, GitHub: jkaniarz).
I've been fiddling with this a bit, and I noticed that when vsync is on and the game is running at a solid 60hz the dt passed to update isn't consistent. Do you think it might be worthwhile to fudge the dt to be a constant 1/60 (or whatever the framerate is) as long as the game is vsync limited? If an object is moving at 1 pixel per frame (x=x+dt*60) there is an occasional stutter due to rounding error.
Also, have you considered exposing XXX_EXT_swap_control_tear.
Original comment by Alex Szpakowski (Bitbucket: slime73, GitHub: slime73).
Do you think it might be worthwhile to fudge the dt to be a constant 1/60 (or whatever the framerate is) as long as the game is vsync limited?
No. vsync will not always be 60 (one of my monitors has a 60Hz refresh, another has 75Hz - I have both connected at once), and framerate can and will drop below the monitor's refresh rate, especially on lower end systems and with unoptimized code.
If an object is moving at 1 pixel per frame (x=x+dt*60) there is an occasional stutter due to rounding error.
Have you tried comparing fullscreen versus windowed? Window managers can often cause those sorts of stutters, it seems more likely than rounding error.
Also, have you considered exposing XXX_EXT_swap_control_tear.
That's used whenever available when vsync is enabled, as of earlier today. :)
Original comment by John Kaniarz (Bitbucket: jkaniarz, GitHub: jkaniarz).
Watch how the 10ths digit slowly creeps up and oscillates between two values. When it oscillates between .9 and 0 you get the stutter. (I'm still running love 0.8 if that makes a difference)
#!lua
local x=0
function love.update(dt)
x=x+dt*60
end
function love.draw()
love.graphics.print(x, 10, 320)
end
fullscreen =true vsync = true
Original comment by John Kaniarz (Bitbucket: jkaniarz, GitHub: jkaniarz).
Fixed the issue with 3 things:
It looks like 0.9 moved the precision of getMicroTime() into getTime() which should fix everything.
Original report by John Kaniarz (Bitbucket: jkaniarz, GitHub: jkaniarz).
I tweaked love.run() in boot.lua to offer fixed update time steps. I didn't know where to put the configuration variables so I just hardcoded them in. This code will throttle to a specified frame rate, or allow multiple updates per frame to keep a target update rate if draw calls are slow. This works like XNA does. I even named the variables the same.
Here is Microsoft's reference: http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.game.isfixedtimestep.aspx