Consistent Animation

From GPWiki
Jump to: navigation, search

Maintaining a consistent speed regardless of FPS

Author: Twisted Matrix (camos85_NOSPAM_@gmail.com)

Overview

With all of the varying hardware types and speeds out there, it is important to have a way to regulate speed in your game. There are many methods for doing this but the best method involves using time scalars and high resolution timers. This may seem like tedious work, but it's not if you plan from the beginning. In this tutorial I plan to outline what I believe is the best method for obtaining the smoothest and most consistant game speed possible through the use of HiRes Timers. I will also cover some of the other methods I have seen used.

Hi-Resolution Timers. What are they? How are they used?

HiRes Timers are necessary for smooth animation, period. This is usually a variable in your code that is updated 1000 times a second, but usually you can use whatever frequency you need. 1000 is convenient but may be overkill for some applications. Since it is beyond the scope of this article to cover all of the varying syntax for creating timers in different API's I won't waste my time. There are thousands of tutorials explaining how to do this. (Some of them right here on GPWiki) Timers are usually a hardware dependent thing, but there may be (and probably is) a hardware independent Timer API out there. Google is your friend.

FPS (Frames per second) and how people abuse it.

FPS is simply that. A method for measuring the general speed of your rendering pipeline. FPS "capping" is not a good way to regulate program speed. Infact, your logic code and rendering code should be updated independently from each other using multi-threading or pseudo multi-threading. To obtain a FPS this is the general method.

  • initiate millisecond timer
  • initiate render loop
    • increment frame counter
    • test condition ( has a second elapsed? )
      • return FPS via frame counter
      • reset frame counter

Hi-Res Timers and Time scalars (The meat and potatoes)

Ok, now that you have your timer setup and churning along happily, you need to calculate the time it takes for your render loop to complete its cycle. We will call this variable <tt>fTime</tt> (frame time) which should be a floating point integer. Also note that in the example below we are dividing the scalar <tt>fTime</tt> by 1000. This is to convert <tt>fTime</tt> from milliseconds to seconds. You may decide to use a different value here. For instance, if you are anticipating your game running at 30 FPS at "normal" operating speed then instead of 1000 use: (1000/30.0). This will "normalize" your time scalar. <code type="vbp">

fTime = (timer - oldTimer) / 1000.0
oldTimer = timer

</code>

There it is. Stick this someware in your render loop for a very accurate time-scalar. Now all you have to do, is add this scalar to all of your movement/animation code. The concept is simple really. The longer it takes to render your frame, the larger <tt>fTime</tt> will be and thus the more your movement and animation will be scaled. For example, say you want to move a pixel across the screen by incrementing the x coordinate. This is how you would do it. <code type="vb">

x = x + 1.0 * fTime

</code>

Getting smoother movement and animation

If you have written your FPS code and setup a display somewhere on-screen, you may have noticed that the FPS will fluctuate quite a bit. Why is this? Well unfortunately we live in a multi-tasking world where resources are shared and processor time is "sliced" so the OS and all of the programs in memory get their slice of the pie so to speak. Back in the days of DOS, you could have setup your <tt>fTime</tt> variable and stopped there. Anyways, the constant slowing down and speeding up of your rendering code will produce really choppy animation. Using this method your game will look good running in framerates as low as 15 FPS. So here is my solution.

  • setup a queue to continuously track the last 60 frame times
  • accumulate the times and average them
  • average with the current <tt>fTime</tt>
  • store in a variable such as "<tt>avgTime</tt>"

This will give you a average frame time that is responsive yet smooth. It will logically be 50% average of the last 60 frame speeds and 50% this frame speed. Before I go, I thought I would give you a bit of real code so here it is. My avgTime code straight out of Zero-G. (Written in VB6)

<code type="vb">

'*** Init ***********************************
Dim timelist As New Collection
'*** Update avgTime *************************
Dim v As New variable
v = fTime
timelist.Add v
If timelist.Count > 60 Then timelist.Remove 1
avgTime = 0
For Each v In timelist
   avgTime = avgTime + v
Next v
avgTime = ((avgTime / timelist.Count) + fTime) / 2

</code>


<b>Speed optimization for above method: (in c++)</b>

Using this method, we can create an average for an arbitrary number of frames by adding only one more variable to the equation.

<code type="cpp">

 #define NUMBER_OF_FRAMES_TO_AVERAGE 60.0f
 float total_time = 0;
 std::queue<float> timelist;
 float GetAverageRate(float ftime)
 {
   timelist.push(ftime);
   total_time += ftime;
   while (timelist.size() > NUMBER_OF_FRAMES_TO_AVERAGE)
   {
     total_time -= timelist.front();
     timelist.pop();
   }
  return total_time / timelist.size();
 }

</code> --Kidmosey 03:32, 15 February 2007 (EST)

Further Reading