Best Practices & Common Pitfalls
43.1 Best practices
Type everything: variables, parameters, returns, arrays, dictionaries. Turn on strict warnings for new projects.
Small, focused scenes that can run on their own (F6). Compose behaviors with component nodes.
Call down, signal up; use an event bus only for truly global events.
Data in Resources, logic in scripts. Designers tweak .tres files, not code.
Use the Input Map, never raw keycodes for gameplay.
Movement in _physics_process with delta; visuals in _process.
Use @onready and %UniqueName instead of long node paths.
Prefer queue_free() over free(); check is_instance_valid() for references that may die.
Keep autoloads few and focused.
Version control from day one; commit .import/.uid files; ignore .godot/.
Profile before optimizing; test exported builds regularly.
43.2 Common pitfalls
Accessing child nodes in _init() or before _ready() null instance errors
Use @onready or _ready(); guard setters with is_node_ready() Forgetting delta Speed depends on frame rate
Multiply per-frame changes by delta (not for velocity passed to move_and_slide)
Multiplying velocity by delta before move_and_slide() Extremely slow movement
move_and_slide() applies delta internally Adding/removing nodes inside physics callbacks
"Can't change this state while flushing queries" call_deferred() / set_deferred() Modifying a shared Resource Changing one enemy changes all duplicate() or *Local to Scene* Comparing floats with == Conditions never true is_equal_approx() or thresholds 5 / 2 == 2 surprises Use floats: 5 / 2.0 Arrays/dictionaries shared by reference duplicate() / duplicate(true)
Not calling super() in overridden _ready() Parent setup skipped Call super() Long get_node("../../..") paths Breaks after restructuring
Signals, exported references, unique names, groups Freed object references
"Invalid access to previously freed instance" is_instance_valid(), disconnect, use signals Forgetting collision masks Areas/bodies never detect each other
Check layer/mask pairs; enable *Visible Collision Shapes* await on a node that gets freed Errors after the wait
Check is_inside_tree()/validity after awaiting Typos in string-based names ("jump ")
Constants, StringName constants, enums Loading user-editable .tres saves Arbitrary code execution
Use JSON/ConfigFile for player-facing save files Not waiting for navigation map sync Agents don't move on first frame
await get_tree().physics_frame before setting targets UI blocking game clicks
Set decorative Controls' mouse_filter to IGNORE; use _unhandled_input Pixel art blurry
Default texture filter *Nearest*, integer scaling Threads touching the scene tree
Compute on threads, apply with call_deferred() Forgetting export templates / testing only in editor
Install matching templates; test exports early