Started getting annoyed that every time I wanted to test something mid-session I had to stop, edit a script, recompile, run again. So I finally wrote a proper in-game debug console. Press `, type a command, get output. Nothing fancy, but it's already saved me a ton of context-switching.
The part I didn't expect: Godot 4's Callable type makes command registration genuinely clean. No reflection hacks, no string-based dispatch, no giant match statement somewhere. You just pass a callable and it works.
# DebugConsole.gd (autoload)
extends CanvasLayer
var _commands: Dictionary = {}
func register(name: String, callable: Callable, description: String = "") -> void:
_commands[name] = { "callable": callable, "desc": description }
func execute(input: String) -> void:
var parts = input.strip_edges().split(" ", false)
if parts.is_empty():
return
var cmd = parts[0].to_lower()
var args = parts.slice(1)
if not _commands.has(cmd):
_print_line("Unknown command: %s" % cmd)
return
_commands[cmd]["callable"].call(args)Then from any system that wants to expose commands:
func _ready() -> void:
DebugConsole.register("godmode", _cmd_godmode, "toggle invincibility")
DebugConsole.register("tp", _cmd_teleport, "tp <x> <y>")
DebugConsole.register("timescale", _cmd_timescale, "timescale <float>")
func _cmd_timescale(args: Array) -> void:
if args.is_empty():
DebugConsole.print_line("Current: %s" % Engine.time_scale)
return
Engine.time_scale = float(args[0])Also added tab-completion over registered command names, which took maybe 20 minutes and is absolutely worth it. The help command just iterates _commands and prints descriptions.
Current command set: spawn, tp, timescale, godmode, give, and a reload_scene shortcut that has saved me probably a hundred alt-tabs already. Curious what commands other people find essential. What's the first thing you'd register?