@fuwu-yuan/bgew
Version:
Baguette Game Engine Web is a french 2D Web game engine
109 lines (108 loc) • 4.85 kB
JavaScript
"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;