Created
November 9, 2020 14:26
-
-
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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