Programming and Technology/Game Programming Patterns for Beginners/Game Loop

International Game Developers Association

Jump to: navigation, search

Introduction

At the heart of every game is a game loop. This is a very simple construct; it's simply a nearly-infinite loop hat does three things: process the player's input, update the game's state, and draw the frame.

while(!bDone)
{
  input();
  update();
  draw();
}

This would typically be encapsulated in a Game class like so:

class Game
{
  public:
    Game() : bDone(false) {}

    void input();
    void update();
    void draw();

    void loop()
    {
      while(!bDone)
      {
         input();
         update();
         draw();
      }
    }

  private:
    bool bDone;
};

Timing

There's a problem with our first game loop: we're not controlling how fast the game runs! If your character ran at a normal speed on a 1GHz processor, it would run three times too fast on a 3GHz machine! So what's a game programmer to do? Well, if you're a beginner, your first inclination would be to limit your game's frame rate.

class Game
{
  public:
    Game() : bDone(false) {}

    void input();
    void update();
    void draw();

    void loop()
    {
      while(!bDone)
      {
         // Get the start time that the frame started at.
         clock_t startTime = clock();

         input();
         update();
         draw();

         // Get the time that the frame ended at.
         clock_t endTime = clock();

         // Sleep until 1/30th of a second has passed, minus the time we spent in our game loop.
         int sleepTime = CLOCKS_PER_SEC / 30 - (endTime - startTime);
         if(sleepTime > 0)
           usleep(sleepTime);
      }
    }

  private:
    bool bDone;
};

That's better than nothing, now you know that the game will always run at 30 frames per second, as long as the player's computer can run that fast. However, if the player's computer is too slow to play your game at 30 frames per second, all the action in your game will happen in slow-motion. Still, this isn't an uncommon game loop for games which don't require much processing power and don't vary greatly in the amount of processing necessary per frame. This particularly suits 2D games which may benefit from having their animations sync to a regular frame rate. As an exercise left to the reader, this game loop could be redesigned to avoid the slow-motion problem by skipping a frame draw if the sleepTime exceeds the frame rate.

For any non-trivial game, locking the frame rate as above is inappropriate, particularly for 3D games which can take advantage of having a higher frame rate when available, and often varies greatly in frame rate depending on what's being drawn on screen. Even non-trivial 2D games will find the above game loop limiting for the same reasons. A far better solution is to base your calculate the updated state of your game based on how much time has passed since the last frame. Thus, we get to a game loop you're likely to see in most games.


class Game
{
  public:
    Game() : bDone(false), lastTime(clock()) {}

    void input(float elapsedSeconds);
    void update(float elapsedSeconds);
    void draw(float elapsedSeconds);

    void loop()
    {
      while(!bDone)
      {
         // Get the time when the frame started.
         clock_t frameTime = clock();

         // The elapsed seconds per frame will almost always be less than 1.0.
         float elapsedSeconds = float(frameTime - lastTime) / CLOCKS_PER_SEC;

         input(elapsedSeconds);
         update(elapsedSeconds);
         draw(elapsedSeconds);

         // Update the last time counter so that we can use it next frame.
         lastTime = frameTime;
      }
    }

  private:
    bool bDone;
    clock_t lastTime;
};
Personal tools