UNPKG

@fuwu-yuan/bgew

Version:

Baguette Game Engine Web is a french 2D Web game engine

109 lines (108 loc) 4.85 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Gameloop = void 0; const browser_process_hrtime_1 = __importDefault(require("browser-process-hrtime")); const workerTimers = __importStar(require("worker-timers")); class Gameloop { constructor() { // Taken and modified from https://github.com/timetocode/node-game-loop // Thanks to https://github.com/norlin/node-gameloop for making this faster this.activeLoops = []; this.getLoopId = (function () { let staticLoopId = 0; return function () { return staticLoopId++; }; })(); this.s2nano = 1e9; this.nano2s = 1 / this.s2nano; // avoid a divide later, although maybe not nessecary this.ms2nano = 1e6; /** * Create a game loop that will attempt to update at some target `tickLengthMs`. * * `tickLengthMs` defaults to 30fps (~33.33ms). * * Internally, the `gameLoop` function created has two mechanisms to update itself. * One for coarse-grained updates (with `setTimeout`) and one for fine-grained * updates (with `setImmediate`). * * On each tick, we set a target time for the next tick. We attempt to use the coarse- * grained "long wait" to get most of the way to our target tick time, then use the * fine-grained wait to wait the remaining time. */ this.setGameLoop = (update, tickLengthMs = 1000 / 30) => { let loopId = this.getLoopId(); this.activeLoops.push(loopId); // expected tick length const tickLengthNano = tickLengthMs * this.ms2nano; // We pick the floor of `tickLengthMs - 1` because the `setImmediate` below runs // around 16ms later and if our coarse-grained 'long wait' is too long, we tend // to miss our target framerate by a little bit const longwaitMs = Math.floor(tickLengthMs - 1); const longwaitNano = longwaitMs * this.ms2nano; let prev = this.getNano(); let target = this.getNano(); let frame = 0; const gameLoop = () => { frame++; const now = this.getNano(); if (now >= target) { const delta = now - prev; prev = now; target = now + tickLengthNano; // actually run user code update(delta * this.nano2s); } // do not go on to renew loop if no longer active if (this.activeLoops.indexOf(loopId) === -1) { return; } // re-grab the current time in case we ran update and it took a long time const remainingInTick = target - this.getNano(); if (remainingInTick > longwaitNano) { // unfortunately it seems our code/node leaks memory if setTimeout is // called with a value less than 16, so we give up our accuracy for // memory stability workerTimers.setTimeout(gameLoop, Math.max(longwaitMs, 16)); } else { workerTimers.setTimeout(gameLoop, 0); } }; // begin the loop! gameLoop(); return loopId; }; this.clearGameLoop = (loopId) => { // remove the loop id from the active loops this.activeLoops.splice(this.activeLoops.indexOf(loopId), 1); }; } getNano() { var hrtime = browser_process_hrtime_1.default(); return (+hrtime[0]) * this.s2nano + (+hrtime[1]); } } exports.Gameloop = Gameloop;