Timer Based Movement with Vsync Tweening
The aim of this example is to demonstrate an approach for decoupling your games logic from the host computers drawing performance, which is commonly referred to as timer based movement
. However in this approach, we're going to keep our frame based approach also. By doing this, our game logic is the virtually the same as what it would be, except we're selectively executing the logic, and trying to tween (interpolate) the graphical output between logic refreshes. So if the host machine is running faster than we need, the player should see smoother motion (hopefully). But when the host is running slower, it'll start to step of course, since we're missing redraws. There's not really much that can be done about that. Regardless, by using such an approach it no longer matters how fast the host computer can draw the scene.
The concept is simple enough, our game has it's own Ideal refresh rate, this is the speed/rate that our game logic executes per second. Logic refers to the code that controls our game characters. Surprisingly these don't have to be updated every frame (here we're considering a frame to be a refresh of the graphical display), in fact a refresh of about 30 FPS or less, is more than adequate in most cases. So in other words, we're only polling the user input and moving the bad guys (or whatever character types are in your game) at this fixed 30fps period.
The test program does this by calculating the number of milliseconds between logic refreshes. If you want 30 logic updates per second and there's 1000 milliseconds per second. Then we calc the number of ticks between logic refreshes by dividing 1000 by our ideal rate. Eg (1000/30) - So each execution of our programs main loop, we're checking the current time (in milliseconds) against the expected time (in milliseconds) of the next logic update. If enough time has past, we refresh the logic to move our characters in our scene, if not, we fall through to and render the scene output. This is fine for machines that can run the game loop faster than the our required ideal logic rate, but on slower machine it's possible that more than one frame has past since we last processed logic. In this situation we calc the number of frames that have past, then simply refresh our logic this number of times. In this demo, i'm simply scaling the movement speeds by the number of the frames past, but it's the same thing.
By now, you've probably thinking that if the logic only refreshes intermittently (slower then the monitor refresh rate), wouldn't the graphical output look a bit choppy ? - Yeah possibly, so to smooth out the motion, this where interpolated movement (tweening) comes into the equation. Each logic refresh, we're storing the position that our character is moving to, as well keeping track of the position that it was last located.. If a redraw occurs before the logic refreshes, and it most certainly will, then we render the characters movement interpolated against the time past. So the player sees intermediate steps, which smooth out the motion.
One thing to note is this at the end of the drawing a frame, we're not waiting until the next frame is due to the start (and therefore giving this time back to the computer). So the speed of the display loop is only limited to how fast the machine can render the graphics on the screen.
Since our logic is decoupled from the graphics refresh, it doesn't really matter how frequently the screen is drawn. So we can use a Vsync'd screen mode or cap the sync rate manually. However, if you want your program to behave NICER on faster systems, it's generally a good idea to limit the refresh bellow a certain point, say 100 fps. Otherwise your program will hog all of the systems resources, which can really annoying on desktop games.
The example demonstrates the approach in the overview above, in the demo it gives you controls over the the logic FPS rate, ranging from 2fps to 30fps. You'll notice that regardless of how fast the logic executes the scene looks virtually the same. Download Source (login required) Controls:
Function Keys (f1 to f5) set logic FPS rate
Down Arrow = Change SETFPS method
ENTER = Cap FPs via SetFPS to 100
SPACE = Toggle VSYNC
ESC = EXIT Related Articles Timer Based Movement Vsync / Frame Rate Scaling Handling Multiple Animations using Timer() Consistent Game Timing Timer Based Movement Star Field Download