Debug Menu

A scene that holds other scenes which are tools to help debug the game.

Goals

The Goal of the Debug Menu is to be the main HUD for other scenes that allow us to quickly cheat, change aspects of the game, or whatever else helps testing/debugging.

How it works

The scene itself is located at res://src/DebugMenu.tscn and since it is just a "Container" of other scenes that are the debug tools themselves, this scene and its logic is pretty simple.

It listens to input and reacts to the action "ui_debug_levels". Whenever this action is released it will toggle the debug menu to either _open() or _close(). These functions already take care of pausing the game and handling keyboard/controller focus.

It is dynamically instanced by Game.tscn when it recognizes that the current build is a "debug" build.

Accessing other nodes from the DebugMenu

Many times some tool inside the DebugMenu will have to access specific nodes, like LinearWorld, Player, or DayNightCycle.

An "easy" solution for that is using find_node on the root.

var _player = get_tree().root.find_node("Player", true, false) as Player

It is usually frowned upon and discouraged to use find_node as it is and expensive function and might make some weird couplings, but since this is just a debug menu and not actual gameplay logic, and if it's not something that will run on a loop or on process, then it can be used.

It might be good to try to cache the reference to avoid having to find the node every time a button is pressed. But, since the DebugMenu lives on Game and is persistent through scene changes, a "player" or "world" node that you've cached might not be valid anymore. This is an how PlayerDebugTool deals with it:

caching find node results example

This way I run _is_player_valid() at the start of any functions that needs a reference to the player. If it has no reference it will grab one and check that it is valid. If it already has one and it's still valid, it will just return true. And if it can't find a valid reference it will return false and let me exit the function that needs the player.

example of is_player_valid being used inside another function

Setting up the Debug Menu export vars

DebugMenu has one export vars:

  • _path_first_focus is an "internal" export var, as in, it's not meant to be edited from outside of the scene. It's just to choose which of the debug tools will receive the keyboard/controller focus when DebugMenu is first opened.

Making DebugMenu "sections" keyboard/controller friendly

Godot is usually pretty good at understanding focus between control nodes and navigating through them with a controller/keyboard. What happens sometimes is that when you are navigating focus goes to a "container" control that shows no feedback.

To make any scene keyboard/controller friendly inside the current scenes from the DebugMenu, I had to connect the focus_entered signal of the root control node of the scene to itself, and then make a desired first button grab the focus:

grab focus example

With just that in each of the "child" scenes from the DebugMenu it's enough for Godot itself to handle navigation between the buttons of each section and not "lose" focus when navigating between sections.

With what script it communicates

With LinearWorld, DayNightCycle, Player and possibly whatever tool is inside it and what they require.