Skip to content

Instantly share code, notes, and snippets.

@dwaard
Created November 9, 2020 14:26
Show Gist options
  • Save dwaard/34654139d1b2d74b09f4d2acc830d5af to your computer and use it in GitHub Desktop.
Save dwaard/34654139d1b2d74b09f4d2acc830d5af to your computer and use it in GitHub Desktop.
Example of an implementation of a Game Loop design pattern with "play catch up".
/**
* This class implements a game loop using the "Play catch up" method. It will
* update the game using a fixed time step because that makes everything simpler
* and more stable for physics and AI. But it will allow flexibility in when we
* render in order to free up some processor time.
*
* It goes like this: A certain amount of real time has elapsed since the last
* turn of the game loop. This is how much game time we need to simulate for the
* game’s “now” to catch up with the player’s. We do that using a series of
* fixed time steps.
*
* It uses the `window.requestAnimationFrame()` method to keep the rendering
* interval equal to the browsers refresh rate. This is usually 60 times per
* second.
*
* @see https://gameprogrammingpatterns.com/game-loop.html#play-catch-up
*/
abstract class GameLoop {
/**
* The timestamp according `performance.now()` at the start of the previous
* animation frame or the moment of instatntiation of this object.
*/
private previous: number;
/**
* The amount of ms that the game's "now" is behind the player's.
*/
private lag: number;
/**
* The size of the fixed time steps that the game's "now" will be updated.
*/
public readonly ms_per_update;
/**
* Construct a new instance of this class.
*
* @param ms_per_update the value of the `ms_per_update` setting
*/
public constructor(ms_per_update: number = 1) {
this.ms_per_update = ms_per_update;
this.previous = performance.now();
this.lag = 0;
requestAnimationFrame(this.animate);
}
/**
* The callback method that must be called at each animation interval.
*
* This must be an arrow function because it will lose the `this` scope.
*/
private animate = () => {
const current : number = performance.now();
const elapsed = current - this.previous;
this.previous = current;
this.lag += elapsed;
this.processInput();
while (this.lag >= this.ms_per_update)
{
this.update();
this.lag -= this.ms_per_update;
}
this.render();
requestAnimationFrame(this.animate);
}
/**
* Process the user input like keyboard, mouse, touch and gyro.
*/
public abstract processInput(): void;
/**
* Update the game's "now" with the fixed interval `ms_per_update`.
*/
public abstract update(): void;
/**
* Render the game's state to the screen.
*/
public abstract render(): void;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment