1. Introduction — Ce qui a changé dans Godot 4
Godot 4 a entièrement repensé le fonctionnement des signaux. Si vous venez de Godot 3, l'ancienne syntaxe à base de chaînes object.connect("signal_name", target, "method_name") a disparu. À la place, Godot 4 utilise une API basée sur Callable, sûre au niveau des types, adaptée au refactoring, et qui détecte les erreurs à la compilation plutôt qu'à l'exécution.
Ce guide couvre tout ce que vous devez savoir sur les signaux dans Godot 4.4+ : les déclarer, les connecter, les déconnecter, les émettre, les attendre avec await, ainsi que les patterns les plus utiles en pratique.
Dans Godot 4, les signaux sont des objets de première classe. Vous y accédez comme des propriétés (par exemple button.pressed) et vous appelez des méthodes dessus (.connect(), .emit(), .disconnect()). Fini l'API à base de chaînes.
2. Déclarer des signaux personnalisés
Déclarez les signaux en haut de votre script avec le mot-clé signal. Vous pouvez éventuellement préciser les noms et types des paramètres pour la documentation et l'autocomplétion de l'éditeur.
# Simple signal with no parameters
signal game_over
# Signal with typed parameters
signal health_changed(new_health: int)
# Signal with multiple parameters
signal item_picked_up(item_name: String, quantity: int)
# Signal with no type hints (works, but typed is recommended)
signal something_happened(data)
Ajoutez toujours des annotations de type aux paramètres de signal. Cela active l'autocomplétion dans l'éditeur et rend votre code auto-documenté. Les signaux avec annotations de type affichent aussi les informations de paramètres dans l'onglet Signals du dock Node.
3. Connecter des signaux (la nouvelle méthode)
Le plus grand changement dans Godot 4 concerne la façon de connecter les signaux. Au lieu de passer des chaînes, vous utilisez le signal comme une propriété et appelez .connect() avec un Callable (une référence à une fonction).
Connexion de base
# Godot 3 (OLD — no longer works in Godot 4)
# button.connect("pressed", self, "_on_button_pressed")
# Godot 4 — callable-based connection
button.pressed.connect(_on_button_pressed)
func _on_button_pressed() -> void:
print("Button was pressed!")
Connexion avec arguments supplémentaires via bind()
Utilisez .bind() pour transmettre des données supplémentaires en même temps que le signal. C'est pratique lorsque vous connectez plusieurs signaux à la même méthode.
# Pass extra data via bind()
buy_button.pressed.connect(_on_item_button.bind("sword"))
sell_button.pressed.connect(_on_item_button.bind("shield"))
func _on_item_button(item_id: String) -> void:
print("Selected item: ", item_id)
Connexions par lambda (en ligne)
Pour de courts gestionnaires, vous pouvez utiliser directement une fonction lambda. Cela garde la logique simple à proximité du point de connexion.
# Lambda — great for one-liners
button.pressed.connect(func(): print("Button pressed!"))
# Lambda with parameters
health_changed.connect(func(hp: int): health_label.text = str(hp))
# Multi-line lambda
enemy.died.connect(func():
score += 100
score_label.text = "Score: %d" % score
print("Enemy defeated!")
)
Les connexions par lambda ne peuvent pas être déconnectées facilement car vous n'avez pas de référence à la fonction anonyme. Si vous devez déconnecter plus tard, stockez le Callable dans une variable ou utilisez plutôt une méthode nommée.
Connexion dans l'éditeur (dock Node)
Vous pouvez toujours connecter des signaux via l'interface de l'éditeur Godot. Sélectionnez un nœud, ouvrez le dock Node > onglet Signals, double-cliquez sur un signal, puis choisissez le nœud et la méthode cibles. L'éditeur génère automatiquement une méthode comme _on_button_pressed() dans votre script. C'est identique à un appel à .connect() en code — cela vous évite simplement de le taper.
4. Déconnecter des signaux
Déconnectez un signal lorsque vous ne voulez plus le recevoir. C'est important pour éviter les erreurs lorsque des nœuds sont libérés ou lors du changement d'état du jeu.
# Disconnect a signal
button.pressed.disconnect(_on_button_pressed)
# Always check before disconnecting to avoid errors
if button.pressed.is_connected(_on_button_pressed):
button.pressed.disconnect(_on_button_pressed)
Pour les connexions de signal entre scènes (par exemple vers un Autoload), déconnectez toujours dans _exit_tree() afin d'éviter les références orphelines quand le nœud est libéré :
func _ready() -> void:
EventBus.player_died.connect(_on_player_died)
func _exit_tree() -> void:
if EventBus.player_died.is_connected(_on_player_died):
EventBus.player_died.disconnect(_on_player_died)
5. Émettre des signaux
Dans Godot 4, utilisez .emit() à la place de l'ancien emit_signal(). Le signal est un objet, donc vous appelez la méthode directement dessus.
signal health_changed(new_health: int)
signal died
var health: int = 100
func take_damage(amount: int) -> void:
health -= amount
health_changed.emit(health) # Emit with argument
if health <= 0:
died.emit() # Emit with no arguments
L'ancien emit_signal("signal_name") fonctionne encore techniquement dans Godot 4 mais il est déprécié. Préférez toujours signal_name.emit() pour le nouveau code. La version à base de chaînes pourrait être supprimée dans une future version de Godot.
6. Attendre des signaux avec await
Godot 4 a remplacé yield() par await. Cela vous permet de mettre en pause une fonction jusqu'à ce qu'un signal se déclenche, ce qui est parfait pour les cinématiques, les tutoriels, les animations séquentielles et les événements temporisés.
func play_cutscene() -> void:
# Wait for a timer
await get_tree().create_timer(2.0).timeout
# Wait for an animation to finish
animation_player.play("intro")
await animation_player.animation_finished
# Wait for player input (custom signal)
dialogue_label.text = "Press any key to continue..."
await player_pressed_continue
# Continue execution after the signal fires
print("Cutscene complete!")
Récupérer des valeurs depuis les signaux attendus
Si le signal émet des arguments, await les renvoie. Pour un seul argument, vous obtenez directement la valeur. Pour plusieurs arguments, vous obtenez un tableau.
# Single parameter — returns the value directly
var final_health: int = await health_changed
print("Health is now: ", final_health)
# Multiple parameters — returns an array
var result = await item_picked_up
var item_name: String = result[0]
var quantity: int = result[1]
7. Flags de connexion de signal
Godot fournit des flags de connexion comme deuxième argument de .connect(), qui modifient le comportement de la connexion.
CONNECT_ONE_SHOT
La connexion est automatiquement supprimée après un seul déclenchement du signal. Parfait pour les événements uniques comme les animations de mort ou le déblocage de succès.
# Auto-disconnects after firing once
enemy.died.connect(_on_first_kill, CONNECT_ONE_SHOT)
func _on_first_kill() -> void:
unlock_achievement("first_blood")
CONNECT_DEFERRED
La méthode connectée est appelée à la fin de la frame courante (pendant le temps d'inactivité) plutôt qu'immédiatement. Utile lorsque le gestionnaire de signal modifie l'arbre de scène, ce qui n'est pas sûr à faire pendant le traitement physique ou le traitement des signaux.
# Called at end of frame — safe for scene tree changes
button.pressed.connect(_on_restart, CONNECT_DEFERRED)
func _on_restart() -> void:
get_tree().reload_current_scene()
Combiner les flags
# One-shot AND deferred
trigger.body_entered.connect(_on_trigger, CONNECT_ONE_SHOT | CONNECT_DEFERRED)
8. Patterns courants
EventBus (hub de signaux global)
Le pattern EventBus utilise un singleton Autoload pour découpler les nœuds. N'importe quel nœud peut émettre ou écouter des événements globaux sans avoir besoin d'une référence directe vers un autre nœud. C'est l'un des patterns les plus puissants de Godot.
extends Node
# Define all global signals in one place
signal player_died
signal score_changed(new_score: int)
signal level_completed(level_id: int)
signal item_collected(item_name: String)
signal settings_changed
func die() -> void:
# Any script can emit global signals
EventBus.player_died.emit()
func _ready() -> void:
# Any script can listen to global signals
EventBus.score_changed.connect(_on_score_changed)
EventBus.player_died.connect(_on_player_died)
func _on_score_changed(new_score: int) -> void:
score_label.text = "Score: %d" % new_score
func _on_player_died() -> void:
game_over_screen.show()
Allez dans Project > Project Settings > Autoload, ajoutez votre script event_bus.gd et nommez-le EventBus. Il devient automatiquement un singleton accessible globalement.
Relais de signal (communication parent-enfant)
Un nœud parent écoute les signaux de ses enfants et relaie ou agrège les informations. Les enfants n'ont jamais besoin de se connaître les uns les autres.
signal inventory_updated
func _ready() -> void:
# Connect to all slot children
for slot in get_children():
if slot.has_signal("item_changed"):
slot.item_changed.connect(_on_slot_changed)
func _on_slot_changed() -> void:
inventory_updated.emit()
Signal + Await pour un flux de jeu séquentiel
func run_level() -> void:
spawn_enemies()
await EventBus.all_enemies_defeated
show_treasure_chest()
await EventBus.chest_opened
play_exit_animation()
await get_tree().create_timer(1.5).timeout
load_next_level()
Connexions dynamiques pour les nœuds instanciés
func spawn_enemy(pos: Vector2) -> void:
var enemy = enemy_scene.instantiate()
enemy.position = pos
# Connect signals before adding to scene tree
enemy.died.connect(_on_enemy_died.bind(enemy))
enemy.health_changed.connect(_on_enemy_health_changed)
add_child(enemy)
func _on_enemy_died(enemy: Node) -> void:
enemies_alive -= 1
enemy.queue_free()
9. Aide-mémoire de migration (Godot 3 → 4)
Mettez ce tableau en favori comme référence rapide lors du portage de votre projet Godot 3.
| Opération | Godot 3 | Godot 4 |
|---|---|---|
| Connecter | connect("sig", obj, "method") |
sig.connect(method) |
| Déconnecter | disconnect("sig", obj, "method") |
sig.disconnect(method) |
| Émettre | emit_signal("sig", args) |
sig.emit(args) |
| Vérifier la connexion | is_connected("sig", obj, "method") |
sig.is_connected(method) |
| Attendre un signal | yield(obj, "sig") |
await obj.sig |
| Lier des arguments supplémentaires | connect("sig", obj, "method", [data]) |
sig.connect(method.bind(data)) |
| Une seule fois | connect("sig", obj, "method", [], CONNECT_ONESHOT) |
sig.connect(method, CONNECT_ONE_SHOT) |
10. Erreurs courantes
1. Utiliser un connect à base de chaînes (style Godot 3)
# WRONG — Godot 3 syntax, will not compile
button.connect("pressed", self, "_on_button_pressed")
# CORRECT — Godot 4 syntax
button.pressed.connect(_on_button_pressed)
2. Utiliser emit_signal() au lieu de .emit()
Même si emit_signal() fonctionne encore, il contourne les vérifications à la compilation et est officiellement déprécié. Utilisez .emit().
# AVOID — deprecated, no compile-time checks
emit_signal("health_changed", health)
# PREFER — type-safe, catches typos at compile time
health_changed.emit(health)
3. Se connecter à un nœud libéré
Si vous connectez un signal à une méthode sur un nœud, et que ce nœud est libéré avec queue_free(), la prochaine émission du signal plantera. Solutions :
- Déconnecter dans
_exit_tree() - Utiliser
CONNECT_ONE_SHOTpour les événements uniques - Vérifier
is_instance_valid(target)avant d'émettre
# Safe emission pattern
for connection in my_signal.get_connections():
if is_instance_valid(connection["callable"].get_object()):
pass # Connection is still valid
my_signal.emit() # Godot handles invalid connections gracefully in 4.x
4. Oublier de déconnecter les signaux entre scènes
Les connexions aux signaux d'Autoload persistent d'un changement de scène à l'autre. Si vous connectez dans _ready() sans jamais déconnecter, vous obtiendrez des erreurs ou un comportement inattendu au rechargement de la scène.
# WRONG — never disconnects, leaks across scene changes
func _ready() -> void:
EventBus.score_changed.connect(_on_score_changed)
# CORRECT — clean disconnect
func _ready() -> void:
EventBus.score_changed.connect(_on_score_changed)
func _exit_tree() -> void:
EventBus.score_changed.disconnect(_on_score_changed)
5. Connecter deux fois le même signal
Si _ready() est appelée plusieurs fois (par exemple lors du re-parentage d'un nœud), vous pourriez connecter accidentellement la même méthode deux fois. Le gestionnaire se déclenchera alors deux fois par émission.
# Guard against double-connection
func _ready() -> void:
if not EventBus.score_changed.is_connected(_on_score_changed):
EventBus.score_changed.connect(_on_score_changed)
Envie de laisser l'IA gérer l'architecture de vos signaux ?
Godot MCP Pro connecte des assistants IA comme Claude directement à votre éditeur Godot. Il peut connecter, déconnecter, auditer et visualiser les flux de signaux dans l'ensemble de votre projet — automatiquement.
analyze_signal_flow
find_signal_connections
connect_signal
disconnect_signal
get_signals