Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Utiliser les signaux

Dans cette leçon, nous allons étudier les signaux. Il s'agit de messages que les nœuds émettent lorsque quelque chose de spécifique leur arrive, comme la pression d'un bouton. D'autres nœuds peuvent se connecter à ce signal et appeler une fonction lorsque l'événement se produit.

Les Signaux sont un mécanisme de délégation intégré à Godot qui permet à un objet de jeu de réagir à un changement au sein d'un autre sans qu'ils n'aient à stocker des références l'un de l'autre. L'utilisation des signaux limite le couplage et permet de garder votre code flexible.

For example, you might have a life bar on the screen that represents the player's health. When the player takes damage or uses a healing potion, you want the bar to reflect the change. To do so, in Godot, you would use signals.

Note

Comme mentionné dans l'introduction, les signaux sont la version de Godot du modèle de l'observateur. Vous pouvez en apprendre davantage à ce sujet ici : https://gameprogrammingpatterns.com/observer.html

Nous allons maintenant utiliser un signal pour faire bouger et arrêter notre icône Godot de la lesson précédente (Écoute des entrées du joueur) en appuyant sur un bouton.

Configuration de la scène

To add a button to our game, we will create a new main scene which will include both a Button and the sprite_2d.tscn scene we created in the Création de votre premier script lesson.

Créez une nouvelle scène en allant dans le menu Scène -> Nouvelle scène.

../../_images/signals_01_new_scene.webp

In the Scene dock, click the 2D Scene button. This will add a Node2D as our root.

../../_images/signals_02_2d_scene.webp

In the FileSystem dock, click and drag the sprite_2d.tscn file you saved previously onto the Node2D to instantiate it.

../../_images/signals_03_dragging_scene.png

We want to add another node as a sibling of the Sprite2D. To do so, right-click on Node2D and select Add Child Node.

../../_images/signals_04_add_child_node.webp

Search for the Button node and add it.

../../_images/signals_05_add_button.webp

Le nœud est petit par défaut. Cliquez et faites glisser la poignée inférieure droite du bouton dans la fenêtre d'affichage pour le redimensionner.

../../_images/signals_06_drag_button.png

Si vous ne voyez pas les poignées, assurez-vous que l’outil de sélection est actif dans la barre d’outils.

../../_images/signals_07_select_tool.webp

Cliquez et faites glisser le bouton lui-même pour le rapprocher du sprite.

You can also write a label on the Button by editing its Text property in the Inspector. Enter Toggle motion.

../../_images/signals_08_toggle_motion_text.webp

Votre arbre de scène et votre fenêtre d'affichage doivent ressembler à ceci.

../../_images/signals_09_scene_setup.png

Save your newly created scene as node_2d.tscn, if you haven't already. You can then run it with F6 (Cmd + R on macOS). At the moment, the button will be visible, but nothing will happen if you press it.

Connecter un signal dans l'éditeur

Here, we want to connect the Button's "pressed" signal to our Sprite2D, and we want to call a new function that will toggle its motion on and off. We need to have a script attached to the Sprite2D node, which we do from the previous lesson.

Vous pouvez connecter des signaux dans le dock Nœud. Sélectionnez le nœud Button et, sur le côté droit de l'éditeur, cliquez sur l'onglet nommé "Nœud" à côté de l'inspecteur.

../../_images/signals_10_node_dock.webp

Le dock affiche la liste des signaux disponibles sur le nœud sélectionné.

../../_images/signals_11_pressed_signals.webp

Double-cliquez sur le signal "pressed" pour ouvrir la fenêtre de connexion de nœud.

../../_images/signals_12_node_connection.png

There, you can connect the signal to the Sprite2D node. The node needs a receiver method, a function that Godot will call when the Button emits the signal. The editor generates one for you. By convention, we name these callback methods "_on_node_name_signal_name". Here, it'll be "_on_button_pressed".

Note

Lorsque vous connectez des signaux via le dock Nœud de l'éditeur, vous pouvez utiliser deux modes. Le plus simple vous permet seulement de connecter les nœuds auxquels un script est attaché et crée une nouvelle fonction de callback dessus.

../../_images/signals_advanced_connection_window.png

La vue avancée vous permet de connecter n'importe quel nœud et n'importe quelle fonction intégrée, d'ajouter des arguments au callback, et de définir des options. Vous pouvez activer le mode en cliquant sur le bouton radio situé en bas à droite de la fenêtre.

Cliquez sur le bouton "Connecter" pour compléter la connexion du signal et passer à l'espace de travail Script. Vous devriez voir la nouvelle méthode avec une icône de connexion dans la marge de gauche.

../../_images/signals_13_signals_connection_icon.webp

Si vous cliquez sur l'icône, une fenêtre s'ouvre et affiche des informations à propos de la connexion. Cette fonctionnalité est uniquement disponible quand les nœuds sont connectés depuis l'éditeur.

../../_images/signals_14_signals_connection_info.webp

Remplaçons la ligne avec le mot-clé pass par du code qui va activer et désactiver le mouvement du nœud.

Our Sprite2D moves thanks to code in the _process() function. Godot provides a method to toggle processing on and off: Node.set_process(). Another method of the Node class, is_processing(), returns true if idle processing is active. We can use the not keyword to invert the value.

func _on_button_pressed():
    set_process(not is_processing())

Cette fonction activera et désactivera le traitement et, par conséquent, le mouvement de l'icône lorsque le bouton sera appuyé.

Avant d'essayer le jeu, nous devons simplifier notre fonction _process() afin de déplacer le nœud automatiquement et ne pas attendre d'entrée utilisateur. Remplacez-la par le code suivant, que nous avons vu il y a deux leçons :

func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

Your complete sprite_2d.gd code should look like the following.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())

Exécutez la scène maintenant et cliquez sur le bouton pour voir le sprite démarrer et s'arrêter.

Connexion d'un signal via le code

Vous pouvez connecter les signaux depuis le code au lieu d'utiliser l'éditeur. C'est nécessaire quand vous créez des nœuds ou instanciez des scènes à l'intérieur d'un script.

Utilisons un nœud différent ici. Godot possède un nœud Timer qui est utile pour implémenter des temps de recharge d'une compétence, le rechargement d'une arme, etc.

Head back to the 2D workspace. You can either click the "2D" text at the top of the window or press Ctrl + F1 (Ctrl + Cmd + 1 on macOS).

In the Scene dock, right-click on the Sprite2D node and add a new child node. Search for Timer and add the corresponding node. Your scene should now look like this.

../../_images/signals_15_scene_tree.png

With the Timer node selected, go to the Inspector and enable the Autostart property.

../../_images/signals_18_timer_autostart.png

Click the script icon next to Sprite2D to jump back to the scripting workspace.

../../_images/signals_16_click_script.png

Nous devons faire deux opérations pour connecter un nœud via le code :

  1. Get a reference to the Timer from the Sprite2D.

  2. Call the connect() method on the Timer's "timeout" signal.

Note

To connect to a signal via code, you need to call the connect() method of the signal you want to listen to. In this case, we want to listen to the Timer's "timeout" signal.

We want to connect the signal when the scene is instantiated, and we can do that using the Node._ready() built-in function, which is called automatically by the engine when a node is fully instantiated.

Pour obtenir une référence vers un nœud relatif au nœud actuel, nous utilisons la méthode Node.get_node(). Nous pouvons stocker la référence dans une variable.

func _ready():
    var timer = get_node("Timer")

The function get_node() looks at the Sprite2D's children and gets nodes by their name. For example, if you renamed the Timer node to "BlinkingTimer" in the editor, you would have to change the call to get_node("BlinkingTimer").

We can now connect the Timer to the Sprite2D in the _ready() function.

func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)

The line reads like so: we connect the Timer's "timeout" signal to the node to which the script is attached. When the Timer emits timeout, we want to call the function _on_timer_timeout(), that we need to define. Let's add it at the bottom of our script and use it to toggle our sprite's visibility.

Note

By convention, we name these callback methods in GDScript as "_on_node_name_signal_name" and in C# as "OnNodeNameSignalName". Here, it'll be "_on_timer_timeout" for GDScript and OnTimerTimeout() for C#.

func _on_timer_timeout():
    visible = not visible

La propriété visible est un booléen qui contrôle la visibilité de notre nœud. La ligne visible = not visible inverse la valeur. Si visible est true, il devient false, et vice-versa.

Si vous exécutez la scène maintenant, vous verrez que le sprite clignote par intervalles d'une seconde.

Script en entier

That's it for our little moving and blinking Godot icon demo! Here is the complete sprite_2d.gd file for reference.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())


func _on_timer_timeout():
    visible = not visible

Signaux personnalisés

Note

Cette section est une référence sur la définition de votre propre signaux et ne dépends pas du projet créé dans les leçons précédentes.

Vous pouvez définir des signaux personnalisés dans un script. Disons, par exemple, que vous souhaitez monter un écran de game over quand la vie du joueur atteint zéro. Pour ce faire, vous pouvez définir un signal nommé "died" (mort) ou "health_depleted" (vie épuisée) lorsque sa santé atteint 0.

extends Node2D

signal health_depleted

var health = 10

Note

Comme les signaux représentent des événements qui viennent de se produire, nous utilisons généralement un verbe d'action au passé dans leur nom.

Vos signaux fonctionnent de la même façon que les signaux intégrés : ils apparaissent dans l'onglet Node et vous pouvez vous y connecter comme les autres.

../../_images/signals_17_custom_signal.png

To emit a signal in your scripts, call emit() on the signal.

func take_damage(amount):
    health -= amount
    if health <= 0:
        health_depleted.emit()

Un signal peut aussi déclarer en option un ou plusieurs arguments. Spécifiez les noms des arguments entre parenthèses :

extends Node

signal health_changed(old_value, new_value)

var health = 10

Note

Les arguments du signal apparaissent dans le dock des nœuds de l'éditeur, et Godot peut les utiliser pour générer des fonctions de rappel pour vous. Cependant, vous pouvez toujours émettre un nombre quelconque d'arguments lorsque vous émettez des signaux. C'est donc à vous d'émettre les valeurs correctes.

To emit values along with the signal, add them as extra arguments to the emit() function:

func take_damage(amount):
    var old_health = health
    health -= amount
    health_changed.emit(old_health, health)

Résumé

Tous les nœuds de Godot émettent des signaux lorsque quelque chose de spécifique leur arrive, comme l'appui sur un bouton. D'autres nœuds peuvent se connecter à des signaux individuels et réagir aux événements sélectionnés.

Les signaux ont de nombreuses utilisations. Grâce à eux, vous pouvez réagir à l'entrée ou la sortie d'un nœud dans le monde de jeu, à une collision, à l'entrée ou la sortie d'un personnage dans une zone, à un élément d'interface qui change de taille, et bien plus encore.

Par exemple, un Area2D représentant une pièce de monnaie émet un signal body_entered chaque fois que le corps physique du joueur entre dans sa forme de collision, ce qui vous permet de savoir quand le joueur l'a collectée.

Dans la section suivante, Votre premier jeu en 2D, vous allez créer un jeu 2D complet et mettre en pratique tout ce que vous avez appris jusqu'à présent.