Jordan Savant # Software Engineer

Fixed Timestep Loops

Fixed Timestep game play loops allow us to run update for game or application logic in fixed timesteps such as 60 frames per second while running draw operations at an unlimited capacity. Running your updates in fixed intervals allows you to not tax the computation of the system and get a smooth update logic for things such as physics or AI operations.

Python Example

Fixed timestep example in Python. Supports an update() and draw() but does not pass delta time into the methods.

timer = 0
actualTime = pygame.time.get_ticks() # ms
FPS = 60
dt = int(1 / FPS * 1000) # 60 fps in ms
updateCounter = 0
drawCounter = 0
while True:

    # UPDATE at fixed intervals
    newTime = pygame.time.get_ticks() # ms
    frameTime = newTime - actualTime
    if frameTime > 250:
        fameTime = 250 # avoid spiral of death
    timer += frameTime
    while timer >= dt:
        # TODO pass delta time in seconds
        update()
        updateCounter += 1
        timer -= dt

    draw()
    drawCounter += 1

    actualTime = newTime # ms

C++ Example without Draw

Example C++ game class with a loop that does not have a Draw method. This is useful for a server processing game or application logic. This example has elements of SFML in it.

class Game
{
public:
    Game();
    void run();
    virtual void update(sf::Time &gameTime);

protected:
    sf::Clock clock;
    float FPS;
    float actualTime;
    float dt;
    float timer;
};
Game::Game()
{
    actualTime = clock.getElapsedTime().asSeconds();
    timer = 0.0f;
    FPS = 60.0f;
    dt = 1.0f / FPS;
}

void Game::run()
{
    registerStates();

    // Game loop
    running = true;
    while (running)
    {
        // UPDATE at fixed intervals
        float newTime = clock.getElapsedTime().asSeconds();
        float frameTime = newTime - actualTime;
        if(frameTime > 0.25f)
        {
            frameTime = 0.25f; // Avoid spiral of death
        }
        timer += frameTime;

        // Run update in dt sized chunks
        while(timer >= dt)
        {
            sf::Time gtu = sf::seconds(dt);
            update(gtu);
            timer -= dt;
        }

        actualTime = newTime;
    }
}

void Game::update(sf::Time &gameTime)
{
    // Update various managers and components
    for(unsigned int i=0; i<gameComponents.size(); i++)
    {
        gameComponents[i]->update(gameTime);
    }

    // Quit if no states
    if(stateStack->empty())
    {
        quit();
    }
}

C++ Example Extension with Draw Call

This is an extending class of the above example, adding in the draw routine logic.

class VideoGame : public Game
{
public:
    VideoGame();

    void run();

    virtual void update(sf::Time &gameTime);

    virtual void draw(sf::RenderWindow &window, sf::Time &gameTime);
};
bit::VideoGame::VideoGame()
    : bit::Game()
{
    // not a bad idea to take the window width, height, build the window here etc
}

void bit::VideoGame::run()
{
    // Game loop
    running = true;
    while (running)
    {
        // Freshen draw
        clearWindow();

        // UPDATE at fixed intervals
        float newTime = clock.getElapsedTime().asSeconds();
        float frameTime = newTime - actualTime;
        if(frameTime > 0.25f)
        {
            frameTime = 0.25f; // Avoid spiral of death
        }
        timer += frameTime;

        // Run update in dt sized chunks
        while(timer >= dt)
        {
            // Window listening for input events in this spot is a good idea

            sf::Time gtu = sf::seconds(dt);
            update(gtu);
            timer -= dt;
        }

        // draw in timechunks remaining
        draw(*renderWindow, gtd);

        actualTime = newTime;
    }
}

void bit::VideoGame::update(sf::Time &gameTime)
{
    bit::Game::update(gameTime);
}

void bit::VideoGame::draw(sf::RenderWindow &window, sf::Time &gameTime)
{
    // Draw logic
}