fixed fps tracking in game loop

This commit is contained in:
Joe Weaver 2020-10-21 06:38:03 -04:00
parent d9a87b2727
commit 15dd74f45e
5 changed files with 97 additions and 56 deletions

View File

@ -32,7 +32,7 @@ export default class BoidDemo extends Scene {
this.viewport.enableZoom();
// Create a bunch of boids
for(let i = 0; i < 100; i++){
for(let i = 0; i < 200; i++){
let boid = this.add.graphic(Boid, layer, new Vec2(this.worldSize.x*Math.random(), this.worldSize.y*Math.random()));
boid.fb = new FlockBehavior(this, boid, this.boids, 75, 50);
boid.setSize(5, 5);

View File

@ -45,12 +45,25 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
this.capacity = capacity ? capacity : 10;
// If we're at the bottom of the tree, don't set a max size
if(this.maxDepth === 0){
if(this.maxDepth === 1){
this.capacity = Infinity;
}
this.divided = false;
this.items = new Array();
// Create all of the children for this quadtree if there are any
if(this.maxDepth > 1){
let x = this.boundary.x;
let y = this.boundary.y;
let hw = this.boundary.hw;
let hh = this.boundary.hh;
this.nw = new QuadTree(new Vec2(x-hw/2, y-hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.ne = new QuadTree(new Vec2(x+hw/2, y-hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.sw = new QuadTree(new Vec2(x-hw/2, y+hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.se = new QuadTree(new Vec2(x+hw/2, y+hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
}
}
/**
@ -71,7 +84,6 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
// We aren't divided, but are at capacity - divide
this.subdivide();
this.deferInsert(item);
this.divided = true;
}
}
}
@ -173,16 +185,7 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
* Divides this quadtree up into 4 smaller ones - called through insert.
*/
protected subdivide(): void {
let x = this.boundary.x;
let y = this.boundary.y;
let hw = this.boundary.hw;
let hh = this.boundary.hh;
this.nw = new QuadTree(new Vec2(x-hw/2, y-hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.ne = new QuadTree(new Vec2(x+hw/2, y-hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.sw = new QuadTree(new Vec2(x-hw/2, y+hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.se = new QuadTree(new Vec2(x+hw/2, y+hh/2), new Vec2(hw/2, hh/2), this.maxDepth - 1, this.capacity);
this.divided = true;
this.distributeItems();
}
@ -239,11 +242,16 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
}
}
/**
* Clear the items in this quadtree
*/
clear(): void {
delete this.nw;
delete this.ne;
delete this.sw;
delete this.se;
if(this.nw){
this.nw.clear();
this.ne.clear();
this.sw.clear();
this.se.clear();
}
for(let item in this.items){
delete this.items[item];

View File

@ -9,26 +9,46 @@ import SceneManager from "../Scene/SceneManager";
import AudioManager from "../Sound/AudioManager";
export default class GameLoop {
// The amount of time to spend on a physics step
private maxFPS: number;
/** The max allowed update fps.*/
private maxUpdateFPS: number;
/** The timestep for each update. This is the deltaT passed to update calls. */
private simulationTimestep: number;
// The time when the last frame was drawn
private lastFrameTime: number;
/** The amount of time we are yet to simulate. */
private frameDelta: number;
// The current frame of the game
/** The time when the last frame was drawn. */
private lastFrameTime: number;
/** The minimum time we want to wait between game frames. */
private minFrameDelay: number;
/** The current frame of the game. */
private frame: number;
// Keeping track of the fps
private runningFrameSum: number;
private numFramesInSum: number;
private maxFramesInSum: number;
private fps: number;
/** The actual fps of the game. */
private fps: number;
/** The time between fps measurement updates. */
private fpsUpdateInterval: number;
private started: boolean;
private running: boolean;
private frameDelta: number;
/** The time of the last fps update. */
private lastFpsUpdate: number;
/** The number of frames since the last fps update was done. */
private framesSinceLastFpsUpdate: number;
/** The status of whether or not the game loop has started. */
private started: boolean;
/** The status of whether or not the game loop is currently running. */
private running: boolean;
/** The panic state of the game. True if we have too many update frames in a single render. */
private panic: boolean;
/** The number of update steps this iteration of the game loop. */
private numUpdateSteps: number;
// Game canvas and its width and height
@ -51,16 +71,23 @@ export default class GameLoop {
// Typecast the config object to a GameConfig object
let gameConfig = config ? <GameConfig>config : new GameConfig();
this.maxFPS = 60;
this.simulationTimestep = Math.floor(1000/this.maxFPS);
this.maxUpdateFPS = 60;
this.simulationTimestep = Math.floor(1000/this.maxUpdateFPS);
this.frameDelta = 0;
this.lastFrameTime = 0;
this.minFrameDelay = 0;
this.frame = 0;
this.runningFrameSum = 0;
this.numFramesInSum = 0;
this.maxFramesInSum = 30;
this.fps = this.maxFPS;
this.fps = this.maxUpdateFPS; // Initialize the fps to the max allowed fps
this.fpsUpdateInterval = 1000;
this.lastFpsUpdate = 0;
this.framesSinceLastFpsUpdate = 0;
this.started = false;
this.running = false;
this.panic = false;
this.numUpdateSteps = 0;
// Set the max fps to 60fps
// this.setMaxFPS(60);
// Get the game canvas and give it a background color
this.GAME_CANVAS = <HTMLCanvasElement>document.getElementById("game-canvas");
@ -103,9 +130,13 @@ export default class GameLoop {
* Changes the maximum allowed physics framerate of the game
* @param initMax
*/
setMaxFPS(initMax: number): void {
this.maxFPS = initMax;
this.simulationTimestep = Math.floor(1000/this.maxFPS);
setMaxUpdateFPS(initMax: number): void {
this.maxUpdateFPS = initMax;
this.simulationTimestep = Math.floor(1000/this.maxUpdateFPS);
}
setMaxFPS(maxFPS: number): void {
this.minFrameDelay = 1000/maxFPS;
}
getSceneManager(): SceneManager {
@ -116,15 +147,10 @@ export default class GameLoop {
* Updates the frame count and sum of time for the framerate of the game
* @param timestep
*/
private updateFrameCount(timestep: number): void {
this.frame += 1;
this.numFramesInSum += 1;
this.runningFrameSum += timestep;
if(this.numFramesInSum >= this.maxFramesInSum){
this.fps = 1000 * this.numFramesInSum / this.runningFrameSum;
this.numFramesInSum = 0;
this.runningFrameSum = 0;
}
private updateFPS(timestamp: number): void {
this.fps = 0.9 * this.framesSinceLastFpsUpdate * 1000 / (timestamp - this.lastFpsUpdate) +(1 - 0.9) * this.fps;
this.lastFpsUpdate = timestamp;
this.framesSinceLastFpsUpdate = 0;
Debug.log("fps", "FPS: " + this.fps.toFixed(1));
}
@ -150,6 +176,8 @@ export default class GameLoop {
this.render();
this.lastFrameTime = timestamp;
this.lastFpsUpdate = timestamp;
this.framesSinceLastFpsUpdate = 0;
window.requestAnimationFrame(this.doFrame);
}
@ -163,14 +191,22 @@ export default class GameLoop {
window.requestAnimationFrame(this.doFrame);
// If we are trying to update too soon, return and do nothing
if(timestamp < this.lastFrameTime + this.simulationTimestep){
if(timestamp < this.lastFrameTime + this.minFrameDelay){
return
}
// Currently, update and draw are synced - eventually it would probably be good to desync these
this.frameDelta = timestamp - this.lastFrameTime;
this.frameDelta += timestamp - this.lastFrameTime;
this.lastFrameTime = timestamp;
// Update the estimate of the framerate
if(timestamp > this.lastFpsUpdate + this.fpsUpdateInterval){
this.updateFPS(timestamp);
}
this.frame++;
this.framesSinceLastFpsUpdate++;
// Update while we can (This will present problems if we leave the window)
this.numUpdateSteps = 0;
while(this.frameDelta >= this.simulationTimestep){
@ -181,9 +217,6 @@ export default class GameLoop {
if(this.numUpdateSteps > 100){
this.panic = true;
}
// Update the frame of the game
this.updateFrameCount(this.simulationTimestep);
}
// Updates are done, draw

View File

@ -95,7 +95,7 @@ export default class MainScene extends Scene {
let i = 0;
let fps = [15, 30, 60];
cycleFramerateButton.onClick = () => {
this.game.setMaxFPS(fps[i]);
this.game.setMaxUpdateFPS(fps[i]);
i = (i + 1) % 3;
}

View File

@ -72,7 +72,7 @@ export default class SecondScene extends Scene {
let i = 0;
let fps = [15, 30, 60];
cycleFramerateButton.onClick = () => {
this.game.setMaxFPS(fps[i]);
this.game.setMaxUpdateFPS(fps[i]);
i = (i + 1) % 3;
}