To expand a bit on my comments in Shadows In Rain's answer:
If you're using variable timesteps, you shouldn't encounter this issue at all, unless you still cut it down to n
updates in some way.
If you're using fixed timesteps, then you might hit that "stuttering" problem purely based on timing.
The problem is that - based on timing - you might draw a scene right before updating or just after an update has happened. Due to this animations (or movements) might not appear as fluent as they should, because they're essentially not constant at all.
Imagine a simple update loop like the on in Shadows In Rain's answer:
tick_rate = 1.0 / 66; // 66hz
adt = 0.0; // accumulated delta time
while(!quit) {
adt += GetDeltaTime();
if(adt >= tick_rate) {
adt -= tick_rate;
Update(tick_rate);
}
Render(adt);
}
There's nothing wrong with this code and it's running perfectly fine. Also, based on framerates and the number of updates per second, this will be as smooth as it can get.
However, as you lower the rendering framerate, you might suddenly notice stuttering, even if you're rendering at 60 FPS.
For simplicity, let's assume your game updates its state 60 times per second and your rendering is limited to 60 FPS as well (slowing the loop down to this rate, either through vertical synchronization or by being busy doing the actual rendering).
So you've got a ball that is moving at 1 pixel per update. That means, in a perfect world, it should move 60 pixels per second, one pixel per frame.
So theoretically, every 16.67 ms the ball moves by 1 pixel. As long as this is true, the animation will appear as smooth. However, let's assume in one iteration your accumulated time is only 16.60 ms. This means there won't be any update.
The result: The ball won't move for one frame (since no update happened). The next frame there might be two updates happening at once (since the accumulated time is now 34.03 ms) and now the ball moves by two pixels between frames.
Things like this might appear as some kind of "micro-stuttering". This can be hard to notice, but depending on your actual game layout it might be pretty obviousy.
Can you fight it? Yes, but it's a bit tedious based on how your draw/update your game.
You'll have to keep the previous screen's state (just taking a screen capture won't be enough; this isn't about blending two images).
In our example, you always keep the position of the ball during the last calculated frame.
To determine the actual position for drawing, you'd then use something like this:
drawing_x = old_x + (current_x - old_x) * f;
Where f
is a factor between 0 and 1 based on how much time is in your accumulator:
f = adt / tick_rate;
So, the less time left till the next update, the further your ball's position will be drawn.
This will essentially introduce a one frame lag, which should be hardly noticeable. But at the same time it should help smoothen out your perceived framerate.
Keep in mind that there might be other factors causing minor stutters as well, e.g. the actual window manager presenting the window or some driver or hardware feature (like "adaptive vertical sync").
Before I finish, I'd like to get back to one statement from above: just taking a screen capture won't be enough; this isn't about blending two images
Actually, this isn't 100% true. If you can, you can also include some kind of motion/movement blur. This should also help with hiding slight stutters, especially for tiny movements, since it will be a lot harder to follow exact outlines or movements. Of course this is not an option for every use case, but it might be something to think about.
Update since you've added those graphs:
I think it's important to note that you'll have to identify where you're running slow and/or causing delays so you no longer update just-in-time.
In your second graph, you obviously take too much time to update your game logic, so you miss out one frame.
To fight this, you could drop a frame (usually described as frame dropping; pretty popular for emulators), or you try fighting this by constantly updating your accumulator, even while doing the updates (it's important to note that on very fast computers you'll have to ensure that you don't always just add 0 due to rounding or whatever):
tick_rate = 1.0 / 66; // 66hz
adt = 0.0; // accumulated delta time
while(!quit) {
adt += GetDeltaTime();
if(adt >= tick_rate) {
adt -= tick_rate;
Update(tick_rate);
adt += GetDeltaTime(); // this is an additional update of the accumulator
// here you'd want to count the iterations to ensure
// you don't update forever and can't catch up!
}
Render(adt);
}
Of course this isn't possible for dynamic timesteps, so there'll always be some slight delays or lags. Don't think there's any effective way to fight that in all possible situations.