FixedUpdater

FixedUpdater

Simple utility class to make working with a fixed update a bit easier. This is extremely useful for things that can’t abide frame drops or are generally time-sensitive in that nature.


Usage

const updateRate = 1000 / 120; // 120 FPS
const myUpdater = new FixedUpdater(updateRate, (fixedDelta: number) => {
  // This function will always run 120 times per second, regardless of the actual framerate.
  // The `fixedDelta` here is the same as `updateRate` but is provided for convenience.
});

let now, last, delta;
function update() {
  now = Date.now();
  delta = now - last;
  last = now;

  myUpdater.fixedUpdate(delta);

  requestAnimationFrame(update);
}
update();

Implementation

export default class FixedUpdater {

  /**
   * How much time should pass between updates? (In milliseconds.)
   *
   * Setting this to a lower number (like 1000 / 120, or 120 FPS) will result in more calls.
   * Setting this to a higher number (like 1000 / 30, or 30 FPS) will result in fewer calls.
   */
  public fixedTimeStep = 1000 / 30;

  /**
   * The function to run during a fixed update.
   *
   * This might be called MANY times per second depending on your fixedTimeStep,
   * so try to keep the workload light.
   */
  private updateCallback: (fixedDeltaTime: number) => void = () => { };

  public constructor(fixedTimeStep: number, fn: (fixedDeltaTime: number) => void) {
    this.fixedTimeStep = fixedTimeStep;
    this.updateCallback = fn;
  }

  /**
   * Internal tracker to determine how much time has elapsed wherien a fixed update did NOT run.
   * This effectively allows us to ensure we run the fixed update as many times as necessary.
   */
  private stepDelta = 0;

  /**
   * If we haven't run in `frameCap` frames, we will limit the number of frames that
   * will be ran at one time. This ultimately can result in inaccurate simulations,
   * but otherwise is a precaution against tying up the browser thread.
   *
   * If you **absolutely can not afford to drop frames** then you will want to remove this.
   */
  private frameCap = 50;

  /**
   * Tracks how much time has passed and fires the fixed update handler accordingly until "caught up."
   * @param   {number}  deltaTimeMS  The time since this function was last called.
   */
  public fixedUpdate = (deltaTimeMS: number) => {
    this.stepDelta += deltaTimeMS;

    if (this.stepDelta > this.fixedTimeStep * this.frameCap) {
      this.stepDelta = this.fixedTimeStep * this.frameCap;
    }

    // While we have fixed steps to take, run the given function.
    while (this.stepDelta >= this.fixedTimeStep) {
      this.stepDelta -= this.fixedTimeStep;
      this.updateCallback(this.fixedTimeStep);
    }
  };
}

Related Posts

Any-Angle Pathfinding

Any-Angle Pathfinding

Simple post-processing effect to use in pathfinding which utilizies line-of-sight to produce more direct routes.

Read More
Building a Tiny Crowd Simulation for the Web

Building a Tiny Crowd Simulation for the Web

Introduction I recently added a fun new effect to my portfolio website that I’m excited to share with you all.

Read More
TypeScript Springs

TypeScript Springs

Nemo vel ad consectetur namut rutrum ex, venenatis sollicitudin urna. Aliquam erat volutpat.

Read More