AngelCode.com > Developer resources

Timing and FPS
Andreas Jönsson, August 2001

I write this tutorial for two reasons. The first reason is that there are many beginners out there that don't know how to utilize time to move their game objects smoothly. The second reason is that I see all too often people talking about "locking the framerate". Now, I can be mistaken but I believe most of these people do this by adding some form of a sleep routine where the program waits each frame until a certain amount of time has gone by. This, in my opinion, is very bad coding because it is a waste of CPU cycles. There are some valid reasons for locking the framerate as I will mention later, but the reason I see most is not a valid one: "Because the game was too fast."

I will start by showing you all how to measure the time accurately in your projects. Then I will move on to talking about how to use the time to move your game objects smoothly. Last I will tell you about some good reasons for locking the frame rate, however I will not tell you how to do that as it is too advanced for this tutorial.

Measuring time

Measuring time on the PC can be done in quite a lot of different ways, but my favorite is QueryPerformanceCounter() which is a highly accurate timer function, with resolutions in microseconds. QueryPerformanceCounter() is depending on the availability of a high resolution performance counter in hardware, if such doesn't exist then the function will fail. But don't worry, all modern systems have such a counter. I believe these counters became available with advent of pentium class processors. To use the performance counter we need to know its resolution and that is retrieved by a call to QueryPerformanceFrequency(), which returns the number of ticks per second.

It never hurts to have a backup plan though. There is a less accurate timer, timeGetTime(), that returns the number of milliseconds since system start. I use this as a fallback in case the QueryPerformanceCounter() doesn't work.

Ok, enough of the babble. How exactly do we measure the time since start-up then? Like this:

// Some global variables used to measure the time
float  timeAtGameStart;
UINT64 ticksPerSecond;

// Called once at the start of the game
void InitGameTime()
{
  // We need to know how often the clock is updated
  if( !QueryPerformanceFrequency((LARGE_INTEGER *)&ticksPerSecond) )
    ticksPerSecond = 1000;

  // If timeAtGameStart is 0 then we get the time since
  // the start of the computer when we call GetGameTime()
  timeAtGameStart = 0;
  timeAtGameStart = GetGameTime();
}

// Called every time you need the current game time
float GetGameTime()
{
  UINT64 ticks;
  float time;

  // This is the number of clock ticks since start
  if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) )
    ticks = (UINT64)timeGetTime();

  // Divide by frequency to get the time in seconds
  time = (float)(__int64)ticks/(float)(__int64)ticksPerSecond;

  // Subtract the time at game start to get
  // the time since the game started
  time -= timeAtGameStart;

  return time;
}

Now for something that is closely related to measuring time. Measuring the number of rendered frames per second.

Frames per second

Since "frames per second" (FPS) is so commonly used to measure performance of games, I will show how to do that here. The name should tell you the theory of computing the FPS: Simply divide the number of frames rendered with the time it took to render them. You can choose to measure the FPS over as many frames as you want. If you measure over just the last frame you get very accurate FPS but it will variate too fast for most applications' need. If you instead choose to measure FPS over the last 10 frames you'll get a more stable number. The problem with this is that you have no control over the update intervals of the FPS counter as some frames can take a long time to render while others take a very short time. For most people this is not a problem, but I like it neat so I came up with the following code.

// Global variables for measuring fps
float lastUpdate        = 0;
float fpsUpdateInterval = 0.5f;
uint  numFrames         = 0;
float fps               = 0;

// Called once for every frame
void UpdateFPS()
{
  numFrames++;
  float currentUpdate = GetGameTime();
  if( currentUpdate - lastUpdate > fpsUpdateInterval )
  {
    fps = numFrames / (currentUpdate - lastUpdate);
    lastUpdate = currentUpdate;
    numFrames = 0;
  }
}

This code will update the variable fps approximately every 0.5 seconds. It will take a little more time than that between updates since it can't update until the frame is rendered, which will never happen at exactly 0.5 seconds (well practically never anyway). You can use the global variable fps anytime you want to see the current framerate of your game.

Moving objects with time

So how do we move objects using this time that we so accurately measured? To find the solution to that question we will take a look at some physics. If you're not that far in school yet, don't worry, it's not very difficult. Imagine that we have a ball in our game that we want to move with a certain speed. The useful physics formula for this situation is:

p(t) = p0 + v*t

p(t) is the current position of the ball at a given time t, p0 is the position of the ball at a time t = 0, and v is the speed of the ball.

It is a bit clumsy to compute the position of the ball from time t = 0 every time the ball is to be moved, especially if the ball has changed direction during the movement. Instead it is better to move the ball incrementally, i.e. in small steps that is added to the previous computed position.

pn = pn-1 + vn*dt

The speed of the ball at the current time, vn, multiplied with the time we wish to increment with, dt, is added to the previous position, pn-1, to get the new position, pn. As you can see the formulas are almost identical. The difference is that we don't start from scratch every time we want to move the ball, instead we start from the previous known position.

Now that we know a little about the formulas from the physics theory we should be able to implement this using our measured time above. The first thing we need to do is to decide upon a time we wish to increment with. The most logical is to use the time that has passed since our last update, and this is exactly what we will do. Here's some code for updating the position of an object.

// We'll need some starting values
float lastGameTime = 0;
float position = 0;
float velocity = 1;

// Here's where we compute how much 
// time has passed since last update
float GetDeltaTime()
{
  float dt = GetGameTime() - lastGameTime;

  // Increment with dt instead of calling 
  // GetGameTime() to avoid loosing time
  lastGameTime += dt;

  return dt;
}

// A function for moving the object
void MoveObject(float dt)
{
  position += velocity*dt;
}

// This is what should be done every frame
void EveryFrame()
{
  // First we'll render the frame
  RenderFrame();

  // Then we'll move our objects
  float dt = GetDeltaTime();

  MoveObject(dt);
}

Pretty simple, huh? Well, it's supposed to be simple. Something to take notice of is that GetGameTime() should only be called once per frame so that all objects are moving on the same time frame. The only reason for calling GetGameTime() more than once in a frame is if you want to measure how much time it takes to compute a specific task during the frame update.

Of course there are more to moving objects than just updating their position. After all, objects have a tendency to change speed by acceleration and deceleration. To know how to do this we again turn to physics.

v(t) = v0 + a*t

v is the velocity, a is the acceleration. We compute these the exact same way as with the position. Here's an updated version of MoveObject().

// We need a new variable for the acceleration
float acceleration = 0.1f;

// The new MoveObject function
void MoveObject(float dt)
{
  velocity += acceleration*dt;
  position += velocity*dt;
}

If you update the velocity before or after the position has a small and unimportant difference. Using incremental methods for computing continuous functions as those above always gives some small errors compared to using the functions directly. However these errors are hardly important in a game so it doesn't really matter.

Locking framerate

I mentioned earlier something about valid reasons for locking the framerate. When I said that I didn't mean the rate of screen updates. I meant the rate at which objects are updated, i.e. using a fixed time increment instead of the time since last update. The reason to use fixed time increment is that it gives a predictable movement of objects. With predictable movements of objects the movements will be exactly the same every time the movement is computed from the same starting point, which is important in for example physics simulations. This can also aid in minimizing apparent network lag in network games as it is easier to make the same movement predictions on all clients without need for server synchronization.

So why shouldn't we lock the rate of screen updates? Because it is not necessary and usually you'll want as fast framerate as possible to get smooth graphics. For example, if we are making a network game we might want to keep the time increment of the game objects to 100ms, i.e 10 fps, so that we don't need to send a lot of data to the clients, which in turn means that we can have more clients. If we were to lock the screen updates to this framerate too most people would stop playing the game for having slow graphics and sluggish response.

How exactly to go about doing this the right way is for a slightly more advanced tutorial than this one so I will not explain it here. Also I don't want to write about something in detail that I have never tried myself. Perhaps I will one day though, but I don't promise anything.

Conclusions

In this tutorial you have learned how to measure the time in your game, and how to compute fps. You have also learned how to utilize the time to move your objects smoothly without the need for locking the framerate to a fixed rate. I hope that you found the tutorial interesting and that you can use the information in some of your own projects.

As always, if you have any questions, you can e-mail them to me.