1
0
Fork 0
tsgames/src/common/game.ts

68 lines
2.1 KiB
TypeScript

import { formatError, formatErrorMessage } from "./errors";
import Input from "./input";
import { nextFrame } from "./utils";
interface FrameMeta {
fps: number;
now: number;
}
type Awaitable<T> = PromiseLike<T> | T;
type Setup<T> = () => Awaitable<T>;
type Frame<T> = (dt: number, state: T, meta: FrameMeta) => Awaitable<T | void>;
export function gameLoop<T>(frame: Frame<T>): RunGame;
export function gameLoop<T>(setup: Setup<T>, frame: Frame<T>): RunGame;
export function gameLoop<T>(setupOrFrame: Setup<T> | Frame<T>, frame?: Frame<T>): RunGame {
return async () => {
let state: T;
try {
if (frame) {
state = await (setupOrFrame as Setup<T>)();
} else {
frame = setupOrFrame as Frame<T>;
state = {} as T;
}
} catch (e) {
console.error(formatError(e, 'Error in game setup'));
alert(formatErrorMessage(e));
return;
}
try {
let prevFrame = performance.now();
let fpsCounter = 0;
let fpsTimer = 0;
const meta: FrameMeta = { fps: 0, now: 0 };
while (true) {
await nextFrame();
Input.updateKeys();
const now = performance.now();
const dt = (now - prevFrame) / 1000;
if (dt < 1) { // skip long pause to avoid blowing up values
meta.now += dt;
const newState = await frame(dt, state, meta);
if (newState) {
state = newState;
}
}
fpsCounter += 1;
if (now - fpsTimer > 500) {
meta.fps = fpsCounter * 2;
fpsCounter = 0;
fpsTimer = now;
}
prevFrame = performance.now();
}
} catch (e) {
console.error(formatError(e, 'Error in game loop'));
alert(formatErrorMessage(e));
return;
}
}
};