Source code for simvx.core.debug
"""Debug & diagnostic system for SimVX.
Static singleton (like Input) for data collection + DebugNode for rendering.
Usage:
from simvx.core import Debug
# Add to scene tree for overlay rendering
root.add_child(Debug.create_node())
# Toggle with F3 in-game, or programmatically:
Debug.enabled = True
"""
from __future__ import annotations
from __future__ import annotations
from ..engine import Input, Node
from ..input.enums import Key # noqa: I001
__all__ = ["Debug", "DebugNode"]
class _LazyAttr:
"""Descriptor for lazy-initialized class attributes."""
def __init__(self, factory):
self._factory = factory
self._attr = f"_{factory.__name__}"
def __set_name__(self, owner, name):
self._attr = f"_{name}"
def __get__(self, obj, objtype=None):
val = getattr(objtype, self._attr, None)
if val is None:
val = self._factory()
setattr(objtype, self._attr, val)
return val
def _make_profiler():
from .profiler import FrameProfiler
return FrameProfiler()
def _make_inspector():
from .ui_inspector import UIInspector
return UIInspector()
def _make_overlay():
from .overlay import DebugOverlay
return DebugOverlay()
[docs]
class Debug:
"""Static debug controller. Disabled by default — zero cost when off."""
enabled: bool = False
profiler = _LazyAttr(_make_profiler)
ui_inspector = _LazyAttr(_make_inspector)
overlay = _LazyAttr(_make_overlay)
[docs]
@classmethod
def toggle(cls):
cls.enabled = not cls.enabled
[docs]
@classmethod
def create_node(cls) -> DebugNode:
"""Factory for the debug overlay rendering node."""
return DebugNode(name="DebugOverlay")
[docs]
class DebugNode(Node):
"""Add to scene tree to render debug overlay. F3 toggles, F4 cycles features."""
_cycle_index: int = 0
_CYCLE = [
{"show_profiler": True},
{"show_profiler": True, "show_bounds": True},
{"show_profiler": True, "show_bounds": True, "show_labels": True},
{"show_profiler": True, "show_bounds": True, "show_labels": True, "show_event_log": True},
]
[docs]
def process(self, dt: float):
if Input.is_key_just_pressed(Key.F3):
Debug.toggle()
if Input.is_key_just_pressed(Key.F4) and Debug.enabled:
self._cycle_index = (self._cycle_index + 1) % (len(self._CYCLE) + 1)
overlay = Debug.overlay
# Reset all
overlay.show_profiler = False
overlay.show_bounds = False
overlay.show_labels = False
overlay.show_event_log = False
overlay.show_hit_target = False
# Apply cycle preset
if self._cycle_index < len(self._CYCLE):
for k, v in self._CYCLE[self._cycle_index].items():
setattr(overlay, k, v)
[docs]
def draw(self, renderer):
if not Debug.enabled:
return
tree = self.tree
Debug.overlay.draw_overlay(renderer, tree, Debug.profiler, Debug.ui_inspector)