diff --git a/index.html b/index.html index 219c5ee..521bbf9 100644 --- a/index.html +++ b/index.html @@ -70,6 +70,7 @@ + diff --git a/src/utils/PerformanceMonitor.js b/src/utils/PerformanceMonitor.js new file mode 100644 index 0000000..78ab2a1 --- /dev/null +++ b/src/utils/PerformanceMonitor.js @@ -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; +}