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
}