PHASE 16: Performance Monitor - FPS tracker with visual graph, memory usage, sprite count (F3 toggle)
This commit is contained in:
@@ -70,6 +70,7 @@
|
|||||||
<script src="src/utils/SpatialGrid.js"></script>
|
<script src="src/utils/SpatialGrid.js"></script>
|
||||||
<script src="src/utils/Pathfinding.js"></script>
|
<script src="src/utils/Pathfinding.js"></script>
|
||||||
<script src="src/utils/Compression.js"></script>
|
<script src="src/utils/Compression.js"></script>
|
||||||
|
<script src="src/utils/PerformanceMonitor.js"></script>
|
||||||
|
|
||||||
<!-- Data -->
|
<!-- Data -->
|
||||||
<script src="src/data/CraftingRecipes.js"></script>
|
<script src="src/data/CraftingRecipes.js"></script>
|
||||||
|
|||||||
203
src/utils/PerformanceMonitor.js
Normal file
203
src/utils/PerformanceMonitor.js
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user