Skip to content

G2.8 — Running, Debugging & the Remote Tree

What you'll learn

  • Run the right target: F5 (main scene) versus F6 (current scene).
  • Read the Output and Debugger docks, set breakpoints, and step through code.
  • Inspect and tweak the live game with the Remote scene tree.
  • See the full hand-off: every concept here mapped to where the game books first need it.

How it applies

  • Without observability you debug by guesswork. If you cannot see a running node's actual state, you theorize instead of looking. The remote tree and the debugger turn "I think it's null" into "I can see it is null," which is the difference between a fix and a guess.
  • Breakpoints beat scattering prints. A breakpoint pauses the whole game at a line and lets you inspect every variable at that instant, without editing and re-running for each value. For anything past a trivial check, it is the faster instrument.
  • The stack trace points past the symptom. The crash line is where a bad value was used; the trace is how it got there (L2.4). Reading the trace is how you fix causes instead of symptoms.
  • Running the wrong target wastes every iteration. Launching the whole game to test one scene buries what you changed; F6 runs the scene in isolation. Small, but it compounds over a day.

Concepts

Running: F5 versus F6

As in L1.1: F5 runs the project's main scene (the game from its entry point); F6 runs the current scene by itself. Test a piece in isolation with F6; check the whole game with F5. Reaching for the wrong one is a small, constant tax on iteration.

The Output and Debugger docks

  • The Output dock shows print, push_warning, and push_error text (L1.1, L2.4). It is the cheapest instrument: a print confirms a line ran and what a value was.
  • The Debugger dock activates on a runtime error or a breakpoint. It shows the error message, the stack trace (the chain of calls that reached the failure), and panels for the call stack and variables. When the game errors, this is where you read why and how it got there — not just the last line.

print_debug(value) is a print that also reports the file and line it was called from — handy when several prints would otherwise be indistinguishable.

Breakpoints and stepping

Click the gutter beside a line in the script editor (or write breakpoint) to set a breakpoint. When execution reaches it, the game pauses and the editor shows the full state at that moment:

  • Step Over runs the next line without descending into its function calls.
  • Step Into descends into the called function.
  • Continue resumes until the next breakpoint.
  • The Stack and Variables panels let you read every local and member at the paused instant.

A breakpoint inspects the entire state at a point in time without editing code — strictly more than a print, which shows one value and requires a re-run to add another. For tracing how a value became wrong, pause near the failure and read the variables.

The Remote scene tree

While the game runs, the editor's Scene dock offers a Remote tab: the live tree of the running game. Select a running node there and the Inspector shows — and lets you edit — its current properties, live. You can watch an enemy's position change, confirm a node exists (or does not), flip a flag, and see the effect immediately.

This is the primary live-observability surface, and the QA framing is exact: it is the running program's instrument panel. "Is the node even in the tree?" (the orphan bug, G1.2), "what is this value right now?", "did this node get freed?" — the remote tree answers all three by showing you, rather than by inference. The Monitors tab adds performance graphs (frame time, memory, draw calls) for when the question is "why is it slow," not "why is it wrong."

Example

Diagnosing a "call on Nil" (L2.4) with the tools, not guesses:

  1. The Debugger shows the error and the line enemy.take_damage(5)enemy is null.
  2. Set a breakpoint just before that line and re-run to the pause.
  3. In the Variables panel, read enemy: confirm it is null rather than assuming.
  4. Walk the stack back to where enemy was assigned; in the Remote tree, check whether the node it should have referenced is actually present.

Each step observes rather than theorizes. The remote tree and debugger convert L2.4's "read the error and trace the cause" from a thought experiment into something you can watch.

The hand-off: what you can now read

This is the last chapter, so here is the contract the book set out to fulfill — every Foundations concept mapped to where the game books first rely on it. When a game-book chapter uses one of these without explanation, it is because it lives here:

Game-book chapter assumes… Foundations source
ARPG M1.2 / idle M1.2 — preload, res:// paths G1.1, G1.3
ARPG M1.4 — SignalBus (autoload + custom signals) L2.3 (signal/emit), G1.5 (autoload), G2.6 (connect)
ARPG M1.3 / idle — InputMap, Input.get_vector G2.5, L1.6
ARPG M2.1 — CharacterBody2D, _physics_process, move_and_slide G2.1, G2.7, L1.6
ARPG M2.2 — @onready, $, AnimatedSprite2D, flip_h L2.2, G1.4, G2.3
ARPG M2.3 — Camera2D follow G2.3
ARPG M2.4 — node FSM (enum, match, class_name, is, child nodes, transitioned signal) L1.3, L2.1, L2.3, G1.2
ARPG M3.1 — Area2D, collision layers/masks, as cast G2.7, L2.1
ARPG M3.2 — Health component (@export, property setter, signal) L2.2, L2.3
ARPG M3.3 — CONNECT_ONE_SHOT, await animation_finished, node paths G2.6, L2.3, G1.4
ARPG M5 / M6 — StatBlock, ItemData custom Resources G1.6, L2.1, L2.2
ARPG M6.3 — weighted drop tables (Array, randi_range) L1.5, L1.6
idle M1.4 — Control, containers, anchors, size flags G2.4
idle M2.1 — Button.pressed, @onready, %-format text G2.6, L2.2, L1.2
idle M2.2 — class_name, property setter, emit L2.1, L2.2, L2.3
idle M2.3 — autoload registration, lambda handler G1.5, L1.4
idle M3.1 — _process, delta, accumulator (while) G2.1, L1.3

If any concept a game book uses at its start is missing from this table, that is a gap to report; if every row resolves, you are equipped to work on those books rather than follow them.

Walkthrough

  1. Reproduce a runtime error (reuse the null-$Child setup from L2.4). Run, and read the Debugger: the message, then the stack trace. Identify the failing line and the call that led to it.
  2. Set a breakpoint on the line before the failure. Re-run; when it pauses, read the Variables panel and confirm the offending value is what you suspected (e.g. null). Use Step Over to advance one line.
  3. With a moving node (your G2.1/G2.7 player), run, open the Remote scene tree, select the player, and watch its position update live in the Inspector as you move. Change a property there and see the running game react.
  4. Open the Monitors tab and watch frame time as the scene runs — the surface you would use for a performance question rather than a correctness one.

Optional sanity check

Recreate the orphan bug from G1.2: Node.new() without add_child. Run and check the Remote tree — the node is not there, confirming it never entered the tree. Add add_child and watch it appear in the remote tree. The remote tree answers "is it even in the tree?" by showing you, which is the whole value of live observability.

Self-check quiz

Q1 — Why is a breakpoint often better than adding print statements?

A. Breakpoints make the game run faster. B. A breakpoint pauses the game and exposes every variable at that instant, without editing and re-running for each value a print would show. C. print does not work in Godot. D. Breakpoints automatically fix the bug.

Reveal answer

B. Pausing at a line lets you inspect the entire state at once via the Variables/Stack panels, where prints show one value each and need a re-run to add more. A is irrelevant (it pauses, not speeds). C is false (print is useful, just narrower). D overstates it — a breakpoint reveals, it does not repair.

Q2 — The game runs and you want to confirm a specific enemy node actually exists and see its live position. Which tool?

A. The Output dock. B. The Remote scene tree, which shows the running game's live node tree and lets the Inspector read/edit a selected node's current properties. C. The FileSystem dock. D. The Project Settings dialog.

Reveal answer

B. The Remote tab mirrors the running game's tree; selecting a node shows its live properties, answering "is it in the tree?" and "what is its position now?" directly. A shows printed text, not live node state. C lists files on disk. D edits project configuration, not runtime state.

Q3 — You are editing enemy.tscn and want to test only it. Which key?

A. F5 — it runs whatever is open. B. F6 — run the current scene by itself. C. F8 — stop. D. Either; they are equivalent.

Reveal answer

B. F6 runs the current scene in isolation; F5 runs the project's main scene regardless of what is open (so A misstates it), and they are not equivalent (D). C stops a running game, it does not launch a scene.

Integration question

Q4 — open

Capstone, spanning the whole book. You press F5 and the game crashes with a "call on Nil" on a line that reads an enemy's StatBlock to apply damage. Lay out a complete diagnosis using the debugging tools of this chapter and the concepts from across Foundations — what you would observe in the Debugger and Remote tree, which Part A and Part B ideas are candidate causes, and how the same ideas would have prevented it. Conclude with why finishing this book means you can now work on the ARPG and idle books rather than merely copy them.

Reveal expected answer

Observe first. The Debugger names the failing line and that the base is Nil, so the StatBlock (or the enemy) reference is null; the stack trace shows how execution reached it. Set a breakpoint before the line, re-run to the pause, and read the Variables panel to confirm which reference is null rather than guessing; use the Remote tree to check whether the enemy node is actually in the tree and whether its stats slot is populated. Candidate causes, by concept: the StatBlock is a custom Resource (G1.6) — was it assigned in the Inspector, or is the @export var stats slot empty? The reference may be a child grabbed without @onready (L2.2) so it evaluated before the tree was built (G2.1 lifecycle); it may be a cast that returned null without a guard (as, L2.1); the enemy itself may be an orphan never added to the tree (G1.2); or a shared resource was .duplicate()d incorrectly so one path holds nothing (L1.5). Prevention, same concepts: type the reference (var stats: StatBlock) so a wrong assignment is caught early (L1.2); grab children with @onready (L2.2); guard casts with a null check (L2.1); assert(stats != null, ...) at the top of the function so the failure names the violated assumption at the door, not three calls later (L2.4). Why you can now work on the game books: every layer of that diagnosis — types and references (Part A), the node/scene/resource model and the lifecycle (Part B), and the observability tools (this chapter) — is something you now reason about directly. When the ARPG book builds a Health component or the idle book wires a tick autoload, you are not copying steps; you know what each line is, why it is where it is, and how to find out when it misbehaves. The hand-off table above is the proof: each thing those books assume has a home here, and you have lived in all of them.