NEVER Do in Signal Architecture
- NEVER use the legacy string-based
Object.connect()β Typos result in silent failures. Always usesignal.connect(_callback)for compile-time validation. - NEVER use signals to dictate behavior top-down β Signals are past-tense events (e.g., "died"). Use direct method calls for commands (e.g., "kill").
- NEVER connect a signal twice to the same Callable β This throws an
ERR_INVALID_PARAMETERat runtime unless using theObject.CONNECT_REFERENCE_COUNTEDflag to stack connections. - NEVER use a Global Signal Bus for local data β Pollutes global state and makes debugging harder. Use local connections for scene-specific logic.
- NEVER assume callbacks must accept all signal arguments β Use
unbind()to drop unwanted parameters and keep your API clean. - NEVER create circular signal dependencies β A signals B, B signals back to A? Use a mediator (parent or AutoLoad) to break the loop.
- NEVER skip signal typing β
signal movedwithout types lacks editor support. Always usesignal moved(dir: Vector2). - NEVER forget to disconnect dynamic signals β Ghost connections cause "call on null instance" errors. Disconnect in
_exit_tree()or when retargeting (disconnect_ghost_signals.gd). - NEVER emit signals with immediate side effects on the emitter β If
died.emit()callsqueue_free(), listeners might fail to respond. Emit first. - NEVER use signals for high-frequency data streams β Sending 1000+ signals/second (like per-particle updates) is inefficient. Use shared arrays or direct buffers.
Signal Up / Call Down
- Children β parents: past-tense signals (
health_changed,died). - Parents β children: direct calls / properties (
apply_damage,play_anim). - Siblings: parent mediator or carefully scoped Autoload bus β never sibling hard refs.
Use signals for: UI presses, death β game over, loot β inventory, cross-scene bus events. Use direct calls for: parent commanding child, local property access.
Decision Tree: Where to Connect
| Scope | Pattern | MANDATORY script |
|---|---|---|
| Child notifies parent / UI | Local signal.connect in parent _ready |
signal_up_call_down_pattern.gd |
| Parent orchestrates children | Method calls down (not signals) | same |
| Cross-scene / systems (achievements, save) | Autoload bus | global_signal_bus_router.gd / global_event_bus.gd |
| Linear async steps (load β fade β spawn) | await signal sequence |
await_signal_sequencing.gd / complex_signal_sequencer.gd |
| Retarget tracking (new enemy) | Disconnect old first | disconnect_ghost_signals.gd |
| One-shot / physics-safe | CONNECT_ONE_SHOT / CONNECT_DEFERRED |
one_shot_deferred_connections.gd |
| Extra context / drop args | Callable.bind / unbind |
callable_bind_context.gd / unbind_unwanted_args.gd |
Available Scripts
- signal_up_call_down_pattern.gd β MANDATORY before hierarchy wiring.
- global_signal_bus_router.gd / global_event_bus.gd β MANDATORY before Autoload buses.
- disconnect_ghost_signals.gd β MANDATORY when switching tracked emitters.
- await_signal_sequencing.gd / complex_signal_sequencer.gd β MANDATORY for multi-step awaits.
- safe_dynamic_connections.gd β
is_connectedguards. - one_shot_deferred_connections.gd β one-shot / deferred flags.
- callable_bind_context.gd / unbind_unwanted_args.gd β bind/unbind.
- track_signal_emitter_source.gd β
CONNECT_APPEND_SOURCE_OBJECT. - signal_debugger.gd / signal_spy.gd β debug / test spies.
Lambda Capture Cleanup (complete)
Godot auto-disconnects most connections when a node frees. Exception: lambdas that capture locals β you must disconnect manually.
var my_lambda: Callable
func _ready() -> void:
var x := 10
my_lambda = func(): print(x)
player.died.connect(my_lambda)
func _exit_tree() -> void:
if player and player.died.is_connected(my_lambda):
player.died.disconnect(my_lambda)Prefer named methods or disconnect_ghost_signals.gd when retargeting.
CONNECT_REFERENCE_COUNTED β Correct Semantics
CONNECT_REFERENCE_COUNTED means multiple identical connects share one connection with a refcount (connect N times / disconnect N times). It is not "auto-cleanup when the emitter frees" and does not fix capturing-lambda leaks.
- Auto-cleanup on free: normal connections to Object methods (non-capturing) are cleared when either side is freed.
- Capturing lambdas: always manual
disconnect(see above). - One-shot auto-remove after fire:
CONNECT_ONE_SHOT.
Deep recipes (on demand)
LLM-ignorance rule: if a general agent would not know it before reading, it lives here or in
scripts/β never delete, only move.
| Topic | Reference |
|---|---|
| Patterns 1β7 + gotchas | implementation-patterns.md |
Reference
Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain β do not preload the whole lattice.
Official Documentation
- Using signals β Core emit/connect model and why signals decouple nodes without hard references.
- Scene organization β Canonical βsignal up, call downβ ownership rules that keep parentβchild command flows explicit.
- Instancing with signals β Emit from spawned scenes so parents/managers receive bullets, loot, and other products without fixed node paths.
- Autoloads versus regular nodes β When a global EventBus is justified vs when scene-local signal wiring is safer.
- Singletons (Autoload) β How to register a typed signal bus that survives scene changes.
- Signal β Typed
SignalAPI:emit,connect,is_connected, and disconnect helpers used throughout this skill. - Callable β
bind()/unbind()for injecting or discarding callback context without wrapper lambdas. - Object β
CONNECT_ONE_SHOT,CONNECT_DEFERRED,CONNECT_REFERENCE_COUNTED, andCONNECT_APPEND_SOURCE_OBJECTflags. - GDScript basics β Typed
signaldeclarations andawaiton signals for linear async sequences. - Using SceneTree β Connection lifetime across enter/exit tree and why dynamic listeners must disconnect when retargeting.
- Godot notifications β Safe connection timing relative to
_ready, parent caches, and user signals. - Idle and Physics Processing β Why deferred signal handlers matter when callbacks mutate physics bodies mid-step.
Related Skills
Prerequisites
- godot-project-foundations β Project layout, Autoload registration, and scene ownership conventions signals plug into.
- godot-gdscript-mastery β Typed Callables,
await, and signal syntax required before advanced connect flags and sequencers. - godot-autoload-architecture β Singleton boot order and ownership rules for global EventBus routers (not for local scene events).
Complements
- godot-composition β Component nodes emit past-tense events; parents compose by connecting those signals and calling down.
- godot-scene-management β Scene swaps and loaders must reconnect or re-emit through buses without ghost listeners.
- godot-state-machine-advanced β State enter/exit often drives signal fan-out; keeps FSM transitions from becoming circular signal graphs.
- godot-resource-data-patterns β Prefer Resources for shared config; signals carry change events, not duplicated mutable state blobs.
- godot-testing-patterns β
watch_signals/ spies pair with this skillβs emit contracts for unit and integration tests. - godot-ui-containers β Buttons and menus should signal intent upward; controllers call down to update Control trees.
Downstream / consumers
- godot-dialogue-system β Line/choice completion events should follow signal-up orchestration into UI and quest listeners.
- godot-ability-system β Cooldown, cast, and hit payloads need typed signals so HUD/VFX stay decoupled from ability nodes.
- godot-combat-system β Damage/death/score chains are the classic signal-up fan-out into UI, audio, and progression.
- godot-performance-optimization β Escalate when high-frequency emit storms show up; replace per-tick signals with buffers or direct reads.
Master
- godot-master β Library router and mirrored module entry; open when discovering which Domain Skill owns a cross-cutting architecture concern.