Daniel Keast

Game loop frame timer

programming

I love video games, and partly started programming in the first place to learn how they work. Recently I’ve been learning OpenGL in an effort to try and build a simple game engine.

The core of a game engine is a loop in which you:

  1. Read in any input events.
  2. Use them to update the game state.
  3. Render the new state.

To have this happen smoothly, you need to detect the time elapsed since the last iteration of the loop and then use it in step 2. You need to move objects twice as far each frame when running at 30fps compared to 60fps.

This is my function to get the elapsed time:

unsigned lap(void)
{
        static struct timeval then;
        struct timeval now;
        unsigned ms;

        if (gettimeofday(&now, NULL) == -1)
                abort();

        if (!then.tv_sec)
                then = now;

        ms = now.tv_sec - then.tv_sec;
        ms *= 1000000;
        ms += now.tv_usec - then.tv_usec;

        then = now;

        return ms;
}

Functions with no parameters in C use the void keyword between the parentheses, like this:

unsigned lap(void)

It is not a syntax error to leave the keyword out, but it disables all checking of the type and count of function arguments. This is to support the older K&R syntax, in which there were no function prototypes. An empty list was presumed to mean a fixed, but undefined number of arguments.

The first local variable uses the static storage class:

static struct timeval then;

This means that the variable keeps it’s value between runs of the function (it lives in statically, rather than automatically allocated memory). This allows me to encapsulate the time of the previous frame inside the only place that actually uses it. The fact that this restricts me to using this function for a single purpose in the application is a benefit to me, as a I prefer specificity over abstractions unless there is an actual need for them. I find this improves clarity, and acts as documentation.

The gettimeofday function is a part of the standard library:

if (gettimeofday(&now, NULL) == -1)
        abort();

It’s first argument is a pointer to a timeval struct, and will be set to the number of seconds and microseconds since the UNIX epoch:

struct timeval {
        time_t      tv_sec;     /* seconds */
        suseconds_t tv_usec;    /* microseconds */
};

These two lines are the initialiser for the ‘then’ variable:

if (!then.tv_sec)
        then = now;

Static variables are initialised to zeroes, and gettimeofday will always set the number of seconds to be > 0 (until 2038 on 32bit machines). This check then ensures that the first call to lap returns 0ms, which is the only sensible value for it to return.

The next three lines set the ms variable to be the number of microseconds difference between ‘then’ and ‘now’:

ms = now.tv_sec - then.tv_sec;
ms *= 1000000;
ms += now.tv_usec - then.tv_usec;

I then set then to be ready for the next time around:

then = now;

And return ms as an unsigned int:

return ms;