Up to date
This page is up to date for Godot 4.3.
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.
Par exemple, vous pouvez avoir une barre de vie sur l'écran qui représente la santé du joueur. Lorsque le joueur subit des dégâts ou utilise une potion de soin, vous souhaitez que la barre reflète le changement. Pour ce faire, dans Godot, vous utiliseriez des signaux.
Comme les méthodes (Callable), les signaux sont un type de classe de base depuis Godot 4.0. Cela signifie que vous pouvez les transmettre directement en tant qu'arguments de méthode sans avoir à les transmettre en tant que chaînes, ce qui permet une meilleure saisie semi-automatique et est moins sujet aux erreurs. Consultez la référence de classe Signal pour obtenir une liste de ce que vous pouvez faire directement avec le type Signal.
Voir aussi
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 :Game Programming Patterns.
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.
Note
Pour ce projet, nous suivrons les conventions de nommage de Godot.
GDScript : Les classes (nœuds) utilisent le PascalCase, les variables et les fonctions utilisent le snake_case, et les constantes utilisent le ALL_CAPS (Voir Guide de style GDScript).
C# : Les classes, les variables d'exportation et les méthodes utilisent PascalCase, les attributs privés utilisent _camelCase, les variables locales et les paramètres utilisent camelCase (Voir Guide de style C#). Attention à taper les noms des méthodes précisément lorsque vous connectez des signaux.
Configuration de la scène
Pour ajouter un bouton à notre jeu, nous allons créer une nouvelle scène qui inclura à la fois un Button et la scène sprite_2d.tscn que nous avons créée dans la leçon Création de votre premier script.
Créez une nouvelle scène en allant dans le menu Scène -> Nouvelle scène.
Dans le dock Scène, cliquez sur le bouton Scène 2D. Cela ajoutera un Node2D comme racine.
Dans le dock FileSystem, cliquez et faites glisser le fichier sprite_2d.tscn que vous avez enregistré précédemment sur le Node2D pour l'instancier.
Nous voulons ajouter un autre nœud comme frère ou sœur du Sprite2D. Pour ce faire, cliquez avec le bouton droit de la souris sur Node2D et sélectionnez Ajouter un nœud enfant.
Recherchez le type de nœud Button et ajoutez-le.
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.
Si vous ne voyez pas les poignées, assurez-vous que l’outil de sélection est actif dans la barre d’outils.
Cliquez et faites glisser le bouton lui-même pour le rapprocher du sprite.
Vous pouvez également ajouter une légende au Button en modifiant sa propriété Text dans l'inspecteur. Entrez Toggle motion.
Votre arbre de scène et votre fenêtre d'affichage doivent ressembler à ceci.
Enregistrez votre scène nouvellement créée sous node_2d.tscn, si ce n'est pas déjà fait. Vous pouvez ensuite l'exécuter avec F6 (Cmd + R sur macOS). Pour le moment, le bouton sera visible, mais rien ne se passera si vous appuyez dessus.
Connecter un signal dans l'éditeur
Ici, nous voulons connecter le signal "pressed" du Button à notre Sprite2D, et nous voulons appeler une nouvelle fonction qui va activer et désactiver son mouvement. Il nous faudra un script attaché au nœud Sprite2D, comme nous l'avons fait dans la leçon précédente.
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.
Le dock affiche la liste des signaux disponibles sur le nœud sélectionné.
Double-cliquez sur le signal "pressed" pour ouvrir la fenêtre de connexion de nœud.
Ici, vous pouvez connectez le signal au nœud Sprite2D. Le nœud a besoin d'une méthode de réception, une fonction que Godot appellera quand le Button émet le signal. L'éditeur en génère une pour vous. Par convention, nous nommons ces méthodes de callback "_on_NomDuNode_nom_du_signal". Dans ce cas, ce sera "_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.
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.
Note
If you are using an external editor (such as VS Code), this automatic code generation might not work. In this case, you need to connect the signal via code as explained in the next section.
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.
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.
Remplaçons la ligne avec le mot-clé pass par du code qui va activer et désactiver le mouvement du nœud.
Notre Sprite2D se déplace grâce au code dans la fonction _process(). Godot fournit une méthode pour activer ou désactiver le traitement : Node.set_process(). Une autre méthode de la classe Node, is_processing(), retourne true si le traitement est actif. Nous pouvons utiliser le mot-clé not pour inverser la valeur.
func _on_button_pressed():
set_process(not is_processing())
// We also specified this function name in PascalCase in the editor's connection window.
private void OnButtonPressed()
{
SetProcess(!IsProcessing());
}
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
public override void _Process(double delta)
{
Rotation += _angularSpeed * (float)delta;
var velocity = Vector2.Up.Rotated(Rotation) * _speed;
Position += velocity * (float)delta;
}
Votre code complet sprite_2d.gd devrait ressembler à ce qui suit.
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())
using Godot;
public partial class MySprite2D : Sprite2D
{
private float _speed = 400;
private float _angularSpeed = Mathf.Pi;
public override void _Process(double delta)
{
Rotation += _angularSpeed * (float)delta;
var velocity = Vector2.Up.Rotated(Rotation) * _speed;
Position += velocity * (float)delta;
}
// We also specified this function name in PascalCase in the editor's connection window.
private void OnButtonPressed()
{
SetProcess(!IsProcessing());
}
}
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.
Revenez à l'espace de travail 2D. Vous pouvez soit cliquer sur le texte « 2D » en haut de la fenêtre, soit appuyer sur Ctrl + F1 (Ctrl + Cmd + 1 sur macOS).
Dans le dock Scène, faîtes un clic-droit sur le nœud "Sprite2D" et ajoutez un nouveau nœud. Recherchez "Timer" et ajoutez le nœud correspondant. Votre scène devrait maintenant ressembler à cela.
Avec le nœud Timer sélectionné, allez dans l'inspecteur et cochez la propriété Autostart.
Cliquez sur l'icône de script à côté de Sprite2D pour revenir à l'espace de travail de script.
Nous devons faire deux opérations pour connecter un nœud via le code :
Obtenir une référence au Timer du Sprite2D.
Appelez la méthode
connect()sur le signal "timeout" du Timer.
Note
Pour se connecter à un signal via le code, vous devez appeler la méthode connect() du nœud que vous voulez écouter. Dans ce cas, nous voulons écouter le signal "timeout" du Timer.
Nous voulons connecter the signal lorsque la scène est instanciée, pour celà nous pouvons utiliser la fonction intégrée Node._ready() qui est appelée automatiquement par le moteur lorsqu'un noeud est complètement instancié.
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")
public override void _Ready()
{
var timer = GetNode<Timer>("Timer");
}
La fonction get_node() regarde les enfants du Sprite2D et récupère les nœuds par leur nom. Par exemple, si vous renommez le nœud Timer en "BlinkingTimer" dans l'éditeur, vous devrez changer l'appel en get_node("BlinkingTimer").
Nous pouvons maintenant connecter le Timer au Sprite2D dans la fonction _ready().
func _ready():
var timer = get_node("Timer")
timer.timeout.connect(_on_timer_timeout)
public override void _Ready()
{
var timer = GetNode<Timer>("Timer");
timer.Timeout += OnTimerTimeout;
}
La ligne se lit ainsi : nous connectons le signal "timeout" du Timer au nœud auquel le script est attaché. Lorsque le Timer émet timeout, nous voulons appeler la fonction _on_timer_timeout(), que nous devons définir. Ajoutons-la au bas de notre script et utilisons-la pour basculer la visibilité de notre sprite.
Note
Par convention, nous nommons ces méthodes de rappel dans GDScript comme "_on_node_name_signal_name" et en C# comme "OnNodeNameSignalName". Ici, ce sera "_on_timer_timeout" pour GDScript et OnTimerTimeout() pour C#.
func _on_timer_timeout():
visible = not visible
private void OnTimerTimeout()
{
Visible = !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 Node2D maintenant, vous verrez que le sprite clignote à des intervalles d'une seconde.
Script en entier
Voilà pour notre petite démonstration d'icône Godot mobile et clignotante ! Voici le fichier sprite_2d.gd complet pour référence.
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
using Godot;
public partial class MySprite2D : Sprite2D
{
private float _speed = 400;
private float _angularSpeed = Mathf.Pi;
public override void _Ready()
{
var timer = GetNode<Timer>("Timer");
timer.Timeout += OnTimerTimeout;
}
public override void _Process(double delta)
{
Rotation += _angularSpeed * (float)delta;
var velocity = Vector2.Up.Rotated(Rotation) * _speed;
Position += velocity * (float)delta;
}
// We also specified this function name in PascalCase in the editor's connection window.
private void OnButtonPressed()
{
SetProcess(!IsProcessing());
}
private void OnTimerTimeout()
{
Visible = !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
using Godot;
public partial class MyNode2D : Node2D
{
[Signal]
public delegate void HealthDepletedEventHandler();
private int _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.
Pour émettre un signal dans vos scripts, appelez emit() sur le signal.
func take_damage(amount):
health -= amount
if health <= 0:
health_depleted.emit()
public void TakeDamage(int amount)
{
_health -= amount;
if (_health <= 0)
{
EmitSignal(SignalName.HealthDepleted);
}
}
Un signal peut aussi déclarer en option un ou plusieurs arguments. Spécifiez les noms des arguments entre parenthèses :
extends Node2D
signal health_changed(old_value, new_value)
var health = 10
using Godot;
public partial class MyNode : Node
{
[Signal]
public delegate void HealthChangedEventHandler(int oldValue, int newValue);
private int _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.
Pour émettre des valeurs en même temps que le signal, ajoutez-les comme arguments supplémentaires à la fonction emit() :
func take_damage(amount):
var old_health = health
health -= amount
health_changed.emit(old_health, health)
public void TakeDamage(int amount)
{
int oldHealth = _health;
_health -= amount;
EmitSignal(SignalName.HealthChanged, oldHealth, _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.