Source code for core.debug_overlay

"""On-screen debug overlay for runtime diagnostics.

Toggle with ``DebugOverlay.enabled = True`` or bind to a key in a script.

Displays:
- FPS (smoothed)
- Entity count
- Per-system update timings (if profiling is enabled on the World)

Usage::

    from core.debug_overlay import DebugOverlay

    # In player or script setup:
    DebugOverlay.enabled = True        # show overlay
    DebugOverlay.show_fps = True
    DebugOverlay.show_entity_count = True
    DebugOverlay.show_system_timings = True
"""
from __future__ import annotations
import time
import pygame
from collections import deque


[docs] class DebugOverlay: """Static debug overlay drawn on top of the game surface.""" enabled: bool = False show_fps: bool = True show_entity_count: bool = True show_system_timings: bool = True font_size: int = 14 color: tuple = (0, 255, 0) bg_color: tuple = (0, 0, 0, 160) position: tuple = (8, 8) _font: pygame.font.Font | None = None _fps_buffer: deque = deque(maxlen=30) _system_timings: dict[str, float] = {} _entity_count: int = 0 _last_time: float = 0.0
[docs] @classmethod def update(cls, dt: float, world=None): """Call once per frame to collect stats.""" if not cls.enabled: return # FPS if dt > 0: cls._fps_buffer.append(1.0 / dt) # Entity count if world is not None: cls._entity_count = len(world.entities) if hasattr(world, "entities") else 0 # System timings from profiling data if available if hasattr(world, "_system_timings"): cls._system_timings = dict(world._system_timings)
[docs] @classmethod def draw(cls, surface: pygame.Surface): """Draw the overlay onto *surface*. Call after all game rendering.""" if not cls.enabled: return if cls._font is None: try: cls._font = pygame.font.SysFont("consolas,courier,monospace", cls.font_size) except Exception: cls._font = pygame.font.Font(None, cls.font_size) lines: list[str] = [] if cls.show_fps and cls._fps_buffer: avg_fps = sum(cls._fps_buffer) / len(cls._fps_buffer) lines.append(f"FPS: {avg_fps:.0f}") if cls.show_entity_count: lines.append(f"Entities: {cls._entity_count}") if cls.show_system_timings and cls._system_timings: lines.append("--- Systems ---") for name, ms in sorted(cls._system_timings.items()): lines.append(f" {name}: {ms:.2f}ms") if not lines: return x, y = cls.position line_h = cls.font_size + 2 # Compute background rect max_w = 0 rendered: list[pygame.Surface] = [] for line in lines: surf = cls._font.render(line, True, cls.color) rendered.append(surf) max_w = max(max_w, surf.get_width()) total_h = line_h * len(lines) bg = pygame.Surface((max_w + 12, total_h + 8), pygame.SRCALPHA) bg.fill(cls.bg_color) surface.blit(bg, (x - 4, y - 2)) for i, surf in enumerate(rendered): surface.blit(surf, (x, y + i * line_h))
[docs] @classmethod def reset(cls): """Reset all collected stats.""" cls._fps_buffer.clear() cls._system_timings.clear() cls._entity_count = 0 cls._font = None