Previous 20 Next

Game loop

Games usually have a main game loop.

function gameLoop() {
  while(true) {
    updateGame()
    drawGame()
  }
}
gameLoop()

This does not work in browsers since it freezes the application.

function gameLoop() {
  updateGame()
  drawGame()
  requestAnimationFrame(gameLoop)
}
gameLoop()

With requestAnimationFrame we can request that the gameLoop function should be called the next time the browser can redraw. This prevents our application from freezing since the browser will regain control and process pending events.

Update interval

The game is updated at a fixed timestep, however, when using requestAnimationFrame the time between each call is not constant.

src/GameUpdater.ts
import { gameUpdate, gameDraw } from "./main";

export class GameUpdater {
  private accumulatedTime = 0

  constructor(
    readonly timeStepSeconds: number,
    private maxUpdatesAllowed: number
  ) { }

  update(delta: number) {
    this.accumulatedTime += delta
    if (this.accumulatedTime > this.timeStepSeconds * this.maxUpdatesAllowed) {
      this.accumulatedTime = this.timeStepSeconds * this.maxUpdatesAllowed
    }

    while (this.accumulatedTime >= this.timeStepSeconds) {
      this.accumulatedTime -= this.timeStepSeconds
      gameUpdate(this.timeStepSeconds)
    }
    gameDraw()
  }
}

The GameUpdater class tries to update the game at the target timestep and will either skip updates if we are running too fast or try to catch up by doing multiple updates if we are running too slow.

src/gameLoop.ts
import { GameUpdater } from "./GameUpdater";

export function startGameLoop(gameUpdater: GameUpdater) {
    let lastTimeGameLoop = performance.now() - gameUpdater.timeStepSeconds

    const gameLoop = (time: number) => {
        const delta = (time - lastTimeGameLoop) / 1000
        lastTimeGameLoop = time
        gameUpdater.update(delta)
        requestAnimationFrame(gameLoop)
    }
    gameLoop(performance.now())
}

Links

Previous 20 Next