UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

154 lines (118 loc) 3.7 kB
import { assert } from "../../core/assert.js"; import Signal from "../../core/events/signal/Signal.js"; import Clock from '../Clock.js'; /** * Simulation tick generator * Dispatches "tick" every animation frame or "maxDelay" time, whichever happens to be soonest * This is essentially the timing bus that all the simulation systems subscribe to for their step advancement */ class Ticker { /** * @readonly * @type {Clock} */ clock = new Clock(); /** * @private * @type {boolean} */ #isRunning = false; #animationFrameHandle = -1; #timeoutHandle = -1; /** * Dispatches time delta in seconds since last tick * @readonly * @type {Signal<number>} */ onTick = new Signal(); /** * @deprecated */ get callbacks() { throw new Error('deprecated, use onTick signal instead'); } /** * * @constructor */ constructor() { this.clock.pause(); } /** * @deprecated use onTick signal directly instead * @param {function} callback * @param {*} [thisArg] */ subscribe(callback, thisArg) { console.warn('deprecated use onTick signal directly instead'); this.onTick.add(callback, thisArg); } /** * @deprecated use onTick signal directly instead * @param {function} callback * @param {*} [thisArg] */ unsubscribe(callback, thisArg) { console.warn('deprecated use onTick signal directly instead'); this.onTick.remove(callback, thisArg); } #isUpdateLoopActive() { return this.#timeoutHandle !== -1 && this.#animationFrameHandle !== -1; } /** * * @param {number} [maxTimeout] */ start({ maxTimeout = 100 } = {}) { assert.isNumber(maxTimeout, 'maxTimeout'); assert.greaterThan(maxTimeout, 0); if (this.#isUpdateLoopActive()) { throw new Error("Already running"); } this.#isRunning = true; const update = () => { if (!this.#isRunning) { return; } const delta = this.clock.getDelta(); this.onTick.send1(delta); } const timeoutCallback = () => { cancelAnimationFrame(this.#animationFrameHandle); animate(); } const animationFrameCallback = () => { clearTimeout(this.#timeoutHandle); //push tick beyond animation frame stack allowing draw to happen this.#timeoutHandle = setTimeout(animate, 0); } const animate = () => { this.#animationFrameHandle = requestAnimationFrame(animationFrameCallback); update(); this.#timeoutHandle = setTimeout(timeoutCallback, maxTimeout); } this.clock.getDelta(); //purge delta this.clock.start(); requestAnimationFrame(animationFrameCallback); } pause() { if (!this.#isUpdateLoopActive()) { throw new Error("Not currently running"); } this.#isRunning = false; } resume() { if (!this.#isUpdateLoopActive()) { throw new Error("Not currently running"); } this.#isRunning = true; } stop() { clearTimeout(this.#timeoutHandle); cancelAnimationFrame(this.#animationFrameHandle); this.#isRunning = false; this.#timeoutHandle = -1; this.#animationFrameHandle = -1; } } export default Ticker;