debug-issue
Systematic Godot debugging decision trees for physics, signals, rendering, navigation, and input issues
Debug Issue
Systematic debugging using domain-specific decision trees. Start by identifying the symptom category, then follow the corresponding tree.
Category Detection
Map user's symptom to the right tree:
| Symptom keywords | Category |
|---|---|
| "pass through", "no collision", "overlap", "stuck", "slides off" | Physics |
| "signal not firing", "not connected", "callback not called" | Signals |
| "invisible", "not showing", "behind", "flickering", "wrong color" | Rendering |
| "not moving to target", "path wrong", "stuck on nav", "no path" | Navigation |
| "key not working", "input ignored", "wrong button", "double input" | Input |
Physics Decision Tree
Check in this order -- each step is the most common cause at that point:
-
Collision layer/mask mismatch (most common):
nodes(action="get_property", scene="<scene>.tscn", node_path="<body>", property="collision_layer") nodes(action="get_property", scene="<scene>.tscn", node_path="<body>", property="collision_mask")Rule: Object A detects Object B only if A's
collision_maskhas a bit that matches B'scollision_layer. Both must be set correctly. -
Missing CollisionShape2D/3D:
scenes(action="get", scene="<scene>.tscn")Verify every physics body and Area has a CollisionShape child. A body without a shape = invisible to physics.
-
Wrong body type:
StaticBody2D: immovable (walls, floors)CharacterBody2D: player/NPC movement viamove_and_slide()RigidBody2D: physics-driven (projectiles, debris)- Common mistake: using
RigidBody2Dfor a player character, then fighting the physics engine
-
Script velocity issues:
- Check if
velocityis being set beforemove_and_slide() - Check if gravity is applied in
_physics_process(not_process) - Check if
deltais used for frame-independent movement
- Check if
Signals Decision Tree
-
Connection exists?
- Check scene file for
[connection]entries - Or check script for
connect()calls - Missing connection = signal silently does nothing
- Check scene file for
-
Signature match?
- Signal definition parameters must match handler function parameters
signal health_changed(new_hp: int)requires handlerfunc _on_health_changed(new_hp: int)- Extra or missing parameters = Godot error at runtime
-
Signal emitted?
- Add
print("signal emitted")beforeemit_signal()/signal.emit() - If not printed, the emit call is never reached (logic bug)
- Add
-
Callable valid?
- If connected via code: target node must exist when
connect()runs - If target is freed, signal emits to invalid callable = error
- If connected via code: target node must exist when
Rendering Decision Tree
-
Visibility:
visibleproperty = false? Check node and ALL parents (parent invisible = children invisible)modulate.a= 0? (fully transparent)
-
Z-index:
- Lower z-index renders behind higher z-index
z_as_relative= true means z_index is relative to parent- Sprite behind a TileMapLayer? Check z_index values on both
-
Material/shader:
- CanvasItemMaterial or ShaderMaterial overriding appearance?
- Shader compilation errors make object invisible (check Output panel)
-
Viewport issues:
- SubViewport not updating? Check
render_target_update_mode - Camera2D not active? Only one Camera2D should have
enabled = trueper viewport
- SubViewport not updating? Check
Navigation Decision Tree
-
NavigationRegion setup:
NavigationRegion2D/3Dmust exist in scene- Must have a
NavigationPolygon/NavigationMeshresource assigned
-
Navigation mesh baked?
- Mesh must be baked (either in editor or via
bake_navigation_mesh()) - Unbaked mesh = no paths available
- Mesh must be baked (either in editor or via
-
NavigationAgent config:
path_desired_distance: how close before moving to next path pointtarget_desired_distance: how close to target before stopping- Values too small = agent oscillates; too large = imprecise
-
Path calculation:
- Call
NavigationAgent.set_target_position()then checkget_next_path_position() - If returns current position, no valid path exists (check mesh coverage)
- Call
Input Decision Tree
-
Input Map:
- Action defined in Project Settings > Input Map?
- Check
project.godotfor[input]section
-
Action name match:
Input.is_action_pressed("jump")-- exact string match required- Typo in action name = silently returns false (no error!)
-
Event processing:
_input()vs_unhandled_input()-- if another node consumes the event,_unhandled_inputnever firesset_process_input(false)disables_input()on that node
-
Conflicts:
- Two actions on same key = both fire
- UI elements (Button, LineEdit) consume input events before game nodes
Diagnostic Steps
For any category:
- Inspect the scene tree:
scenes(action="get", scene="<scene>.tscn") - Read relevant scripts:
scripts(action="get", path="res://scripts/<name>.gd") - Check properties:
nodes(action="get_property", ...) - Run the scene:
editor(action="run_scene", scene="<scene>.tscn")and check output for errors - Report findings: present root cause and specific fix (property change, missing node, script edit)
When to Use
- Any "it doesn't work" debugging scenario in Godot
- Systematic elimination when the cause is not obvious
- Verifying correct setup of physics, signals, rendering, navigation, or input