fixed fps tracking in game loop
This commit is contained in:
parent
d9a87b2727
commit
15dd74f45e
|
@ -32,7 +32,7 @@ export default class BoidDemo extends Scene {
|
||||||
this.viewport.enableZoom();
|
this.viewport.enableZoom();
|
||||||
|
|
||||||
// Create a bunch of boids
|
// 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()));
|
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.fb = new FlockBehavior(this, boid, this.boids, 75, 50);
|
||||||
boid.setSize(5, 5);
|
boid.setSize(5, 5);
|
||||||
|
|
|
@ -45,12 +45,25 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
this.capacity = capacity ? capacity : 10;
|
this.capacity = capacity ? capacity : 10;
|
||||||
|
|
||||||
// If we're at the bottom of the tree, don't set a max size
|
// 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.capacity = Infinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.divided = false;
|
this.divided = false;
|
||||||
this.items = new Array();
|
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
|
// We aren't divided, but are at capacity - divide
|
||||||
this.subdivide();
|
this.subdivide();
|
||||||
this.deferInsert(item);
|
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.
|
* Divides this quadtree up into 4 smaller ones - called through insert.
|
||||||
*/
|
*/
|
||||||
protected subdivide(): void {
|
protected subdivide(): void {
|
||||||
let x = this.boundary.x;
|
this.divided = true;
|
||||||
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.distributeItems();
|
this.distributeItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,11 +242,16 @@ export default class QuadTree<T extends Region & Unique> implements Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the items in this quadtree
|
||||||
|
*/
|
||||||
clear(): void {
|
clear(): void {
|
||||||
delete this.nw;
|
if(this.nw){
|
||||||
delete this.ne;
|
this.nw.clear();
|
||||||
delete this.sw;
|
this.ne.clear();
|
||||||
delete this.se;
|
this.sw.clear();
|
||||||
|
this.se.clear();
|
||||||
|
}
|
||||||
|
|
||||||
for(let item in this.items){
|
for(let item in this.items){
|
||||||
delete this.items[item];
|
delete this.items[item];
|
||||||
|
|
|
@ -9,26 +9,46 @@ import SceneManager from "../Scene/SceneManager";
|
||||||
import AudioManager from "../Sound/AudioManager";
|
import AudioManager from "../Sound/AudioManager";
|
||||||
|
|
||||||
export default class GameLoop {
|
export default class GameLoop {
|
||||||
// The amount of time to spend on a physics step
|
/** The max allowed update fps.*/
|
||||||
private maxFPS: number;
|
private maxUpdateFPS: number;
|
||||||
|
|
||||||
|
/** The timestep for each update. This is the deltaT passed to update calls. */
|
||||||
private simulationTimestep: number;
|
private simulationTimestep: number;
|
||||||
|
|
||||||
// The time when the last frame was drawn
|
/** The amount of time we are yet to simulate. */
|
||||||
private lastFrameTime: number;
|
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;
|
private frame: number;
|
||||||
|
|
||||||
// Keeping track of the fps
|
/** The actual fps of the game. */
|
||||||
private runningFrameSum: number;
|
private fps: number;
|
||||||
private numFramesInSum: number;
|
|
||||||
private maxFramesInSum: number;
|
|
||||||
private fps: number;
|
|
||||||
|
|
||||||
private started: boolean;
|
/** The time between fps measurement updates. */
|
||||||
private running: boolean;
|
private fpsUpdateInterval: number;
|
||||||
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;
|
private panic: boolean;
|
||||||
|
|
||||||
|
/** The number of update steps this iteration of the game loop. */
|
||||||
private numUpdateSteps: number;
|
private numUpdateSteps: number;
|
||||||
|
|
||||||
// Game canvas and its width and height
|
// Game canvas and its width and height
|
||||||
|
@ -51,16 +71,23 @@ export default class GameLoop {
|
||||||
// Typecast the config object to a GameConfig object
|
// Typecast the config object to a GameConfig object
|
||||||
let gameConfig = config ? <GameConfig>config : new GameConfig();
|
let gameConfig = config ? <GameConfig>config : new GameConfig();
|
||||||
|
|
||||||
this.maxFPS = 60;
|
this.maxUpdateFPS = 60;
|
||||||
this.simulationTimestep = Math.floor(1000/this.maxFPS);
|
this.simulationTimestep = Math.floor(1000/this.maxUpdateFPS);
|
||||||
|
this.frameDelta = 0;
|
||||||
|
this.lastFrameTime = 0;
|
||||||
|
this.minFrameDelay = 0;
|
||||||
this.frame = 0;
|
this.frame = 0;
|
||||||
this.runningFrameSum = 0;
|
this.fps = this.maxUpdateFPS; // Initialize the fps to the max allowed fps
|
||||||
this.numFramesInSum = 0;
|
this.fpsUpdateInterval = 1000;
|
||||||
this.maxFramesInSum = 30;
|
this.lastFpsUpdate = 0;
|
||||||
this.fps = this.maxFPS;
|
this.framesSinceLastFpsUpdate = 0;
|
||||||
|
|
||||||
this.started = false;
|
this.started = false;
|
||||||
this.running = 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
|
// Get the game canvas and give it a background color
|
||||||
this.GAME_CANVAS = <HTMLCanvasElement>document.getElementById("game-canvas");
|
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
|
* Changes the maximum allowed physics framerate of the game
|
||||||
* @param initMax
|
* @param initMax
|
||||||
*/
|
*/
|
||||||
setMaxFPS(initMax: number): void {
|
setMaxUpdateFPS(initMax: number): void {
|
||||||
this.maxFPS = initMax;
|
this.maxUpdateFPS = initMax;
|
||||||
this.simulationTimestep = Math.floor(1000/this.maxFPS);
|
this.simulationTimestep = Math.floor(1000/this.maxUpdateFPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaxFPS(maxFPS: number): void {
|
||||||
|
this.minFrameDelay = 1000/maxFPS;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSceneManager(): SceneManager {
|
getSceneManager(): SceneManager {
|
||||||
|
@ -116,15 +147,10 @@ export default class GameLoop {
|
||||||
* Updates the frame count and sum of time for the framerate of the game
|
* Updates the frame count and sum of time for the framerate of the game
|
||||||
* @param timestep
|
* @param timestep
|
||||||
*/
|
*/
|
||||||
private updateFrameCount(timestep: number): void {
|
private updateFPS(timestamp: number): void {
|
||||||
this.frame += 1;
|
this.fps = 0.9 * this.framesSinceLastFpsUpdate * 1000 / (timestamp - this.lastFpsUpdate) +(1 - 0.9) * this.fps;
|
||||||
this.numFramesInSum += 1;
|
this.lastFpsUpdate = timestamp;
|
||||||
this.runningFrameSum += timestep;
|
this.framesSinceLastFpsUpdate = 0;
|
||||||
if(this.numFramesInSum >= this.maxFramesInSum){
|
|
||||||
this.fps = 1000 * this.numFramesInSum / this.runningFrameSum;
|
|
||||||
this.numFramesInSum = 0;
|
|
||||||
this.runningFrameSum = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.log("fps", "FPS: " + this.fps.toFixed(1));
|
Debug.log("fps", "FPS: " + this.fps.toFixed(1));
|
||||||
}
|
}
|
||||||
|
@ -150,6 +176,8 @@ export default class GameLoop {
|
||||||
this.render();
|
this.render();
|
||||||
|
|
||||||
this.lastFrameTime = timestamp;
|
this.lastFrameTime = timestamp;
|
||||||
|
this.lastFpsUpdate = timestamp;
|
||||||
|
this.framesSinceLastFpsUpdate = 0;
|
||||||
|
|
||||||
window.requestAnimationFrame(this.doFrame);
|
window.requestAnimationFrame(this.doFrame);
|
||||||
}
|
}
|
||||||
|
@ -163,14 +191,22 @@ export default class GameLoop {
|
||||||
window.requestAnimationFrame(this.doFrame);
|
window.requestAnimationFrame(this.doFrame);
|
||||||
|
|
||||||
// If we are trying to update too soon, return and do nothing
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently, update and draw are synced - eventually it would probably be good to desync these
|
// 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;
|
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)
|
// Update while we can (This will present problems if we leave the window)
|
||||||
this.numUpdateSteps = 0;
|
this.numUpdateSteps = 0;
|
||||||
while(this.frameDelta >= this.simulationTimestep){
|
while(this.frameDelta >= this.simulationTimestep){
|
||||||
|
@ -181,9 +217,6 @@ export default class GameLoop {
|
||||||
if(this.numUpdateSteps > 100){
|
if(this.numUpdateSteps > 100){
|
||||||
this.panic = true;
|
this.panic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the frame of the game
|
|
||||||
this.updateFrameCount(this.simulationTimestep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates are done, draw
|
// Updates are done, draw
|
||||||
|
|
|
@ -95,7 +95,7 @@ export default class MainScene extends Scene {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let fps = [15, 30, 60];
|
let fps = [15, 30, 60];
|
||||||
cycleFramerateButton.onClick = () => {
|
cycleFramerateButton.onClick = () => {
|
||||||
this.game.setMaxFPS(fps[i]);
|
this.game.setMaxUpdateFPS(fps[i]);
|
||||||
i = (i + 1) % 3;
|
i = (i + 1) % 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ export default class SecondScene extends Scene {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let fps = [15, 30, 60];
|
let fps = [15, 30, 60];
|
||||||
cycleFramerateButton.onClick = () => {
|
cycleFramerateButton.onClick = () => {
|
||||||
this.game.setMaxFPS(fps[i]);
|
this.game.setMaxUpdateFPS(fps[i]);
|
||||||
i = (i + 1) % 3;
|
i = (i + 1) % 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user