A problem that I've had ever since I made my first Pong clone in SDL, is a phenomenon known as microstutter. If you've tried your hand at programming games from scratch, you've probably ran into this problem. And you've probably read articles such as these. If you haven't, do it now and come back when you're done.
Now my problem was that none of those solutions worked for me! I still got that annoying stutter, and so I decided to debug and draw some pretty graphs.
I found out that the issue was due to the game rendering at 15000 fps. This was a problem because the clock couldn't update fast enough for me to calculate a fresh interpolation value for each draw, which caused sprites to be drawn at the same position multiple times. Of course, in an actual game with 1000+ sprites and effects the framerate wouldn't be this high, and the clock would have a sufficient update frequency.
I needed to have the draw method spend a minimum amount of time to finish, and this minimum time must be greater than the clock's update frequency. Why don't I just use the built-in solution for limiting framerate in SFML you ask? Well, I did. And it made everything stutter again. My uneducated guess is that
SetFramerateLimit() sets the thread to sleep before drawing, inside the Window.Display() method. This would cause the interpolated render positions to not be in sync with the elapsed time between updates. But alas, some things are a mystery.
My solution was like this:
- Store the time before drawing
- Perform the drawing
- Find out how long it took to draw (
currenttime - time_before_drawing)
- Let the thread sleep a remainder duration, i.e. if the minimum time is 8 ms (125 fps), the sleep duration would be
8 - frametime.
So far it appears to work and gives sexy-looking graphs.
To make it better you could have a launcher for your game, where the user could set the framerate to a specified limit (or no limit), and then do 1000 / framerate to get the minimum time each draw call must take.
Here is a working example (in some build of SFML 2) with source code:smoothrendering.zip
I know, it's messy, but the code of interest is the gameloop in main.cpp and the interpolation method in renderentity.cpp