PHASE 16: Performance Monitor - FPS tracker with visual graph, memory usage, sprite count (F3 toggle)

This commit is contained in:
2025-12-11 13:26:14 +01:00
parent 1f45429d08
commit 96b2636907
2 changed files with 204 additions and 0 deletions

View File

@@ -0,0 +1,203 @@
/**
* PERFORMANCE MONITOR
* Tracks FPS, memory usage, and performance metrics
*/
class PerformanceMonitor {
constructor(scene) {
this.scene = scene;
this.enabled = true; // Toggle with F3
// FPS tracking
this.fps = 60;
this.fpsHistory = [];
this.maxHistoryLength = 60; // 1 second at 60fps
// Memory tracking (if available)
this.memoryUsage = 0;
// Performance stats
this.stats = {
avgFPS: 60,
minFPS: 60,
maxFPS: 60,
frameTime: 16.67, // ms
drawCalls: 0,
activeSprites: 0
};
// Create UI
this.createUI();
// Keyboard toggle (F3)
if (scene.input && scene.input.keyboard) {
scene.input.keyboard.on('keydown-F3', () => {
this.toggle();
});
}
}
createUI() {
if (!this.scene.add) return;
// Background panel
this.panel = this.scene.add.rectangle(10, 10, 200, 120, 0x000000, 0.7);
this.panel.setOrigin(0, 0);
this.panel.setScrollFactor(0);
this.panel.setDepth(100000);
// Text display
this.text = this.scene.add.text(15, 15, '', {
fontSize: '12px',
fontFamily: 'Courier New',
color: '#00ff00',
stroke: '#000000',
strokeThickness: 2
});
this.text.setOrigin(0, 0);
this.text.setScrollFactor(0);
this.text.setDepth(100001);
// FPS graph (mini)
this.graph = this.scene.add.graphics();
this.graph.setScrollFactor(0);
this.graph.setDepth(100001);
this.updateVisibility();
}
toggle() {
this.enabled = !this.enabled;
this.updateVisibility();
console.log(`📊 Performance Monitor: ${this.enabled ? 'ON' : 'OFF'}`);
}
updateVisibility() {
if (this.panel) this.panel.setVisible(this.enabled);
if (this.text) this.text.setVisible(this.enabled);
if (this.graph) this.graph.setVisible(this.enabled);
}
update(delta) {
if (!this.enabled) return;
// Calculate FPS
this.fps = 1000 / delta;
this.fpsHistory.push(this.fps);
if (this.fpsHistory.length > this.maxHistoryLength) {
this.fpsHistory.shift();
}
// Calculate stats
this.stats.avgFPS = this.fpsHistory.reduce((a, b) => a + b, 0) / this.fpsHistory.length;
this.stats.minFPS = Math.min(...this.fpsHistory);
this.stats.maxFPS = Math.max(...this.fpsHistory);
this.stats.frameTime = delta;
// Count active sprites
this.stats.activeSprites = this.countActiveSprites();
// Memory (if available)
if (performance.memory) {
this.memoryUsage = (performance.memory.usedJSHeapSize / 1048576).toFixed(2); // MB
}
// Update UI every 5 frames (reduce overhead)
if (this.scene.game.loop.frame % 5 === 0) {
this.updateUI();
}
}
countActiveSprites() {
if (!this.scene.children) return 0;
return this.scene.children.list.filter(child =>
child.active && child.visible
).length;
}
updateUI() {
if (!this.text) return;
// Build text
let lines = [
`FPS: ${this.fps.toFixed(1)}`,
`Avg: ${this.stats.avgFPS.toFixed(1)}`,
`Min: ${this.stats.minFPS.toFixed(1)} / Max: ${this.stats.maxFPS.toFixed(1)}`,
`Frame: ${this.stats.frameTime.toFixed(2)}ms`,
`Sprites: ${this.stats.activeSprites}`
];
if (this.memoryUsage > 0) {
lines.push(`Memory: ${this.memoryUsage} MB`);
}
this.text.setText(lines.join('\n'));
// Update graph
this.drawGraph();
}
drawGraph() {
if (!this.graph) return;
this.graph.clear();
const graphX = 15;
const graphY = 100;
const graphWidth = 180;
const graphHeight = 20;
const maxFPS = 60;
// Draw background
this.graph.fillStyle(0x222222, 0.5);
this.graph.fillRect(graphX, graphY, graphWidth, graphHeight);
// Draw FPS bars
this.graph.fillStyle(0x00ff00, 0.8);
const barWidth = graphWidth / this.maxHistoryLength;
this.fpsHistory.forEach((fps, i) => {
const barHeight = (fps / maxFPS) * graphHeight;
const x = graphX + (i * barWidth);
const y = graphY + graphHeight - barHeight;
// Color based on FPS
if (fps < 30) {
this.graph.fillStyle(0xff0000, 0.8); // Red
} else if (fps < 50) {
this.graph.fillStyle(0xffaa00, 0.8); // Orange
} else {
this.graph.fillStyle(0x00ff00, 0.8); // Green
}
this.graph.fillRect(x, y, barWidth, barHeight);
});
// Draw 60 FPS line
this.graph.lineStyle(1, 0xffffff, 0.5);
this.graph.beginPath();
this.graph.moveTo(graphX, graphY);
this.graph.lineTo(graphX + graphWidth, graphY);
this.graph.strokePath();
}
// Get current stats for external use
getStats() {
return {
...this.stats,
currentFPS: this.fps,
memoryMB: this.memoryUsage
};
}
destroy() {
if (this.panel) this.panel.destroy();
if (this.text) this.text.destroy();
if (this.graph) this.graph.destroy();
}
}
// Export
if (typeof module !== 'undefined' && module.exports) {
module.exports = PerformanceMonitor;
}