Contrôler l’interface utilisateur du jeu avec du code

Introduction

Dans ce tutoriel, vous allez connecter un personnage à une barre de vie et animer la perte de vie.

../../_images/lifebar_tutorial_final_result.gif

Voici ce que vous allez créer : la barre et le compteur s’animent lorsque le personnage prend un coup. Ils s’estompent quand il meurt.

Vous apprendrez :

  • Comment connecter un personnage à une interface graphique avec des signaux
  • Comment contrôler une interface graphique avec GDscript
  • Comment animer une barre de vie avec le nœud Tween

Si vous voulez apprendre comment configurer l’interface à la place, consultez les tutoriels pas-à-pas de l’interface utilisateur :

  • Créer un écran de menu principal
  • Créer une interface utilisateur de jeu

Lorsque vous codez un jeu, vous voulez d’abord construire le gameplay de base : les mécaniques principales, les actions du joueur, les conditions de victoire et de défaite. L’interface utilisateur arrive un peu plus tard. Vous voulez que tous les éléments qui composent votre projet soient séparés si possible. Chaque personnage devrait être dans sa propre scène, avec ses propres scripts, de même que pour les éléments de l’interface utilisateur. Cela permet d’éviter les bugs, de garder votre projet gérable, et permet à différents membres de l’équipe de travailler sur différentes parties du jeu.

Une fois que le gameplay de base et l’interface utilisateur sont prêts, vous devrez les connecter d’une manière ou d’une autre. Dans notre exemple, nous avons l’ennemi qui attaque le joueur à intervalles de temps constants. Nous voulons que la barre de vie se mette à jour lorsque le joueur subit des dégâts.

Pour ce faire, nous utiliserons des signaux.

Note

Les signaux sont la version Godot du patron de conception Observateur. Ils nous permettent d’envoyer un message. D’autres nœuds peuvent se connecter à l’objet qui émet le signal et reçoit l’information. C’est un outil puissant que nous utilisons beaucoup pour l’interface utilisateur et les systèmes de succès. Mais vous ne voulez pas les utiliser partout. La connexion de deux nœuds ajoute un certain couplage entre eux. Quand il y a beaucoup de connexions, elles deviennent difficiles à gérer. Plus d’informations sur le tutoriel vidéo Introduction to signals in the Godot game engine sur GDquest.

Télécharger et explorer le projet de démarrage

Télécharger le projet Godot : ui_code_life_bar.zip. Il contient toutes les ressources et les scripts dont vous avez besoin pour démarrer. Extraire l’archive .zip pour obtenir deux dossiers : start et end.

Charger le projet start dans Godot. Dans le dock FileSystem, double-cliquez sur LevelMockup.tscn pour l’ouvrir. C’est une maquette de jeu de rôle où 2 personnages se font face. L’ennemi rose attaque et endommage le carré vert à intervalles réguliers, jusqu’à sa mort. N’hésitez pas à essayer le jeu : les mécanismes de combat de base fonctionnent déjà. Mais comme le personnage n’est pas connecté à la barre de vie, le GUI ne fait rien.

Note

C’est typique de la façon dont vous codez un jeu : vous implémentez d’abord le gameplay de base, vous gérez la mort du joueur, et seulement ensuite vous ajouterez l’interface. C’est parce que l’interface utilisateur écoute ce qui se passe dans le jeu. Il ne peut donc pas fonctionner si d’autres systèmes ne sont pas encore en place. Si vous concevez l’interface utilisateur avant de prototyper et de tester le gameplay, il y a de fortes chances qu’il ne fonctionnera pas bien et que vous devrez le recréer à partir de zéro.

Cette scène contient un background, une interface graphique, et deux personnages.

../../_images/lifebar_tutorial_life_bar_step_tut_LevelMockup_scene_tree.png

L’arbre des scènes, avec la scène de l’interface graphique pour afficher ses enfants

La scène de l’interface graphique contient tous les éléments de l’interface graphique. Elle est attaché à un script permettant de récupérer les informations des différents éléments de la scène :

onready var number_label = $Bars/LifeBar/Count/Background/Number
onready var bar = $Bars/LifeBar/TextureProgress
onready var tween = $Tween
public class Gui : MarginContainer
{
    private Tween _tween;
    private Label _numberLabel;
    private TextureProgress _bar;

    public override void _Ready()
    {
        // C# doesn't have an onready feature, this works just the same.
        _bar = (TextureProgress) GetNode("Bars/LifeBar/TextureProgress");
        _tween = (Tween) GetNode("Tween");
        _numberLabel = (Label) GetNode("Bars/LifeBar/Count/Background/Number");
    }
}
  • number_label fait référence à un nœud Label. Il contient le compteur de vie
  • bar fait référence à un nœud TextureProgress, permettant de contrôler la barre de vie
  • tween est un nœud de type composant qui permet d’animer et contrôler une valeur ou une méthode de n’importe quel autre nœud

Note

Le projet utilise une organisation simple fonctionnelle pour les Game Jams et les petits jeux.

A la racine du projet, dans le dossier res://, vous trouverez le LevelMockup. C’est la scène principale du jeu et notre scène de travail. Tous les éléments qui composent notre jeu sont dans le dossier scenes/. Le dossier assets/ contient toute les sprites et la police pour le compteur de vie. Dans le dossier `scripts/, vous trouverez les scripts contrôlant le joueur, de l’ennemi et l’interface graphique du jeu.

Cliquez sur l’icône d’édition de scène (Clap) à droite du nœud dans l’arbre des scènes pour ouvrir la scène dans l’éditeur. Vous verrez que la barre de vie et d’énergie sont aussi des sous-scènes.

../../_images/lifebar_tutorial_Player_with_editable_children_on.png

L’arbre des scènes, avec la scène du joueur Player ouverte pour afficher ses enfants

Initialiser la barre de vie avec la valeur maximale de vie du joueur

On doit donner l “information à l’interface graphique du nombre de points de vie actuel du joueur, pour mettre à jour la texture de la barre de vie, et afficher le nombre de points de vie restant du joueur dans le compteur en dans le coin haut droit de l’écran. Pour faire cela, on envoie la vie du joueur à l’interface graphique à chaque fois que le joueur prends un coup. Une fois reçu, on mets à jour les nœuds ``Lifebar``et ``Number``avec cette valeur.

On pourrait s’arrêter là pour afficher la valeur dans le compteur; mais on doit initialiser la valeur maximale max_value de la barre de vie pour la mettre à jour dans les bonnes proportions. Cette première étape permettra de donner à GUI quel est la valeur maximale max_health de vie du joueur.

Astuce

La barre de vie, un noeud TextureProgress, dans la section Range a la propriété max_value à 100 par défaut. Si l’on a pas besoin d’afficher la vie du joueur à l’aide d’un nombre, il n’est pas nécessaire de modifier cette propriété. On enverra, un pourcentage de la vie restante, du joueur Player à l’interface GUI : health / max_health * 100 .

../../_images/lifebar_tutorial_TextureProgress_default_max_value.png

Cliquez sur l’icône de script à droite du nœud GUI dans l’arbre des scènes pour ouvrir son script dans l’éditeur. Dans la fonction _ready, nous allons récupérer la vie maximale max_health du joueur Player et la stocker dans une nouvelle variable player_max_health. Cette variable est ensuite utilisée pour initialiser la valeur maximale max_value de la barre de vie bar :

func _ready():
    var player_max_health = $"../Characters/Player".max_health
    bar.max_value = player_max_health
public override void _Ready()
{
    // Add this below _bar, _tween, and _numberLabel.
    var player = (Player) GetNode("../Characters/Player");
    _bar.MaxValue = player.MaxHealth;
}

Décomposons ce script. L’instruction $"../Characters/Player" permet de récupérer, dans le nœud parent LevelMockup de notre nœud GUI à l’aide du chemin ../, le nœud Player dans le nœud Characters. Le symbole $ permet de récupérer le nœud. Nous accédons ensuite à la variable max_health du nœud Player à l’aide du symbole .. On accèdent donc bien à la valeur maximale de vie du joueur.

La seconde ligne assigne cette valeur à bar.max_value. On pourrait combiner ces deux lignes en une seule mais on a besoin de cette valeur plus tard dans ce tutoriel, il donc plus judicieux de la stocker dans une variable``player_max_health``.

Le script associée au nœud joueur``Player.gd`` initialise la valeur de vie health à la valeur de vie maximale max_health au début du jeu. Alors pourquoi utiliser la variable max_health? Il y a deux raisons :

On a pas la garantie que la vie actuelle``health` sera toujours égal à la vie maximale``max_health`: une version future du jeu pourrait charger un niveau où le joueur a déjà perdu de la vie.

Note

Quand vous ouvrez une scène dans le jeu, Godot créer les nœuds un par un, en suivant l “ordre donnée dans votre arbre des scènes, du haut vers le bas. GUI et Player`ne font pas partis de la même branche de nœud. Pour être sûr, qu’elles existent lorsqu’on y accèdent, on doit utiliser la fonction `_ready. Godot appelle ces fonctions _ready après avoir chargé tous les nœuds, avant que le jeu commence. C’est la fonction parfaite pour initialiser notre jeu et préparer le jeu. Vous apprendrez plus sur cette fonction _ready`dans : :doc:`scripting_continued

Mettre à jour la santé avec un signal lorsque le joueur prend un coup

Notre interface graphique est prête à recevoir les mises-à-jour de la valeur de health de Player. Pour ce faire, nous allons utiliser des signaux.

Note

Il existe de nombreux signaux intégrés utiles comme enter_tree et exit_tree, que tous les nœuds émettent lorsqu’ils sont respectivement créés et détruits. Vous pouvez également créer votre propre signal en utilisant le mot-clé signal. Sur le nœud Player, vous trouverez deux signaux que nous avons créés pour vous : died et health_changed.

Pourquoi n’obtenons-nous pas directement le nœud Player dans la fonction _process et regardons la valeur santé ? L’accès aux nœuds de cette façon crée un couplage étroit entre eux. Si vous l’avez fait avec parcimonie, cela peut fonctionner. Au fur et à mesure que votre jeu grossit, vous pouvez avoir beaucoup plus de connexions. Si vous obtenez des nœuds de cette façon, cela devient très complexe rapidement. Non seulement cela : vous vérifiez le changement d’état en permanence dans la fonction _processus. Cette vérification se produit 60 fois par seconde et vous casserez probablement le jeu à cause de l’ordre dans lequel le code s’exécute.

À une trame donnée, vous regardez potentiellement la propriété d’un autre nœud avant qu’il ait été mis à jour : vous obtenez une valeur de la dernière trame. Cela conduit à des bogues obscurs qui sont difficiles à corriger. D’autre part, un signal est émis juste après qu’un changement s’est produit. Cela vous garantit que vous obtenez des informations fraîches. Et vous mettrez à jour l’état de votre nœud connecté directement après que le changement se produise.

Note

Le patron Observateur, dont dérivent les signaux, ajoute quand même un peu de couplage entre les branches de nœuds. Mais il est généralement plus léger et plus sûr que l’accès direct aux nœuds pour communiquer entre deux classes distinctes. Il peut être acceptable pour un nœud parent d’obtenir les valeurs de ses enfants. Mais vous voudrez favoriser les signaux si vous travaillez avec deux branches distinctes. Lisez Game Programming Patterns pour plus d’informations sur Observer pattern. Le livre complet est disponible en ligne gratuitement.

Dans cette optique, connectons le nœud GUI à Player. Cliquez sur le nœud Player dans le dock de scène pour le sélectionner. Dirigez-vous vers l’inspecteur et cliquez sur l’onglet Nœud. C’est l’endroit où connecter les nœuds, pour écouter celui que vous avez sélectionné.

La première section énumère les signaux personnalisés définis dans Player.gd :

  • died est émis quand le personnage vient de mourir. Nous l’utiliserons dans un moment pour cacher l’interface utilisateur.
  • health_changed est émis lorsque le personnage a été touché.
../../_images/lifebar_tutorial_health_changed_signal.png

On se connecte au signal health_changed

Sélectionnez health_changed et cliquez sur le bouton Connecter dans le coin inférieur droit pour ouvrir la fenêtre Connecter un signal. Sur le côté gauche, vous pouvez choisir le nœud qui écoutera ce signal. Sélectionnez le nœud GUI. Le côté droit de l’écran vous permet d’ajouter des valeurs optionnelles avec le signal. Nous nous en sommes déjà occupés dans player.GD. En général, je recommande de ne pas ajouter trop d’arguments en utilisant cette fenêtre car ils sont moins pratiques que ceux ajouter à partir du code.

../../_images/lifebar_tutorial_connect_signal_window_health_changed.png

La fenêtre Connecter un signal avec le nœud GUI sélectionné

Astuce

Vous pouvez éventuellement connecter des nœuds à partir du code. Mais le faire à partir de l’éditeur a deux avantages :

  1. Godot peut écrire de nouvelles fonctions de rappel pour vous dans le script connecté
  2. Une icône d’émetteur apparaît à côté du nœud qui émet le signal dans le dock de scène

En bas de la fenêtre, vous trouverez le chemin d’accès au nœud que vous avez sélectionné. Nous nous intéressons à la deuxième ligne appelée « Méthode dans le nœud ». C’est la méthode sur le noeud GUI qui est appelé lorsque le signal est émis. Cette méthode reçoit les valeurs envoyées avec le signal et vous permet de les traiter. Si vous regardez à droite, il y a un bouton radio « Créer une fonction » qui est activé par défaut. Cliquez sur le bouton Connecter au bas de la fenêtre. Godot crée la méthode à l’intérieur du nœud GUI. L’éditeur de script s’ouvre avec le curseur à l’intérieur d’une nouvelle fonction _on_player_health_changed.

Note

Lorsque vous connectez des nœuds à partir de l’éditeur, Godot génère un nom de méthode avec le modèle suivant : _on_EmitterName_signal_name. Si vous avez déjà écrit la méthode, l’option « Créer une fonction » la conservera. Vous pouvez remplacer le nom par ce que vous voulez.

../../_images/lifebar_tutorial_godot_generates_signal_callback.png

Godot écrit la méthode de rappel pour vous et vous y emmène

Entre les parenthèses après le nom de notre méthode, ajouter un argument player_health. Quand le signal health_changed sera émis, il enverra l’information de point de vie actuel health avec. Votre code devrait ressembler à :

func _on_Player_health_changed(player_health):
    pass
public void OnPlayerHealthChanged(int playerHealth)
{
}

Note

Le moteur ne convertit pas PascalCase en snake_case, pour les exemples C# nous utiliserons PascalCase pour les noms de méthode et camelCase pour les paramètres de méthode, ce qui suit les conventions de nommage officielles C#.

../../_images/lifebar_tutorial_player_gd_emits_health_changed_code.png

Dans le script Player.gd, quand le joueur émets le signal health_changed, il envoies aussi la valeur health

Dans la méthode _on_Player_health_changed, appelons une seconde fonction update_health avec la variable player_health comme argument.

Note

On pourrait directement mettre à jour la valeur de vie dans LifeBar et Number. Il y a deux raisons pour utiliser cette fonction à la place :

  1. Le nom est explicite pour nous et nos camarades sur le fait que lorsque le joueur prends un coup, nous mettons à jour le compteur de vie dans l’interface
  2. On réutilisera cette fonction plus tard

Créer une nouvelle fonction update_health après _on_Player_health_changed. Elle prends new\_value comme seul argument :

func update_health(new_value):
    pass
public void UpdateHealth(int health)
{
}

Cette fonction a besoin de :

  • d’assigner new_value à la propriété text du nœud Number. Il ne faut pas oublier de convertir cette valeur en chaîne de caractères
  • d’assigner new_value à la propriété value du nœud TextureProgress
func update_health(new_value):
    number_label.text = str(new_value)
    bar.value = new_value
public void UpdateHealth(int health)
{
    _numberLabel.Text = health.ToString();
    _bar.Value = health;
}

Astuce

str est une fonction intégrée qui convertit une valeur en texte. La propriété text de Number n “accepte que les chaînes de caractères, on ne peut pas assigner new_value directement

Il faut appeler notre nouvelle fonction update_health à la fin de la fonction _ready pour initialiser la propriété text dans le nœud Number avec la bonne valeur au démarrage du jeu. Lancez le jeu avec F5 : la barre de vie se met à jour à chaque attaque !

../../_images/lifebar_tutorial_LifeBar_health_update_no_anim.gif

À chaque attaque, le nœud Number et le nœud TextureProgress (la barre de vie) se mettent à jour

Animer la perte de vie avec un nœud Tween

Notre interface est fonctionnel, mais elle pourrait bénéficier d’un peu d’animation. C’est une bonne opportunité d’introduire le nœud Tween, un outil essentiel pour animer des propriété. Ce nœud permet d’animer entre deux état sur une certaine durée. par exemple il peut animer la barre de vie TextureProgress entre deux états, avant et après le coup.

La scène GUI contient déjà un nœud enfant Tween qui est assigné à la variable tween dans notre script. On peut maintenant l’utiliser, nous avons à faire quelques modifications dans notre fonction update_health.

Nous utiliserons la méthode interpolate_property du nœud Tween. Elle prend sept paramètres :

  1. Une référence au nœud dont la propriété est à animer
  2. Le nom de la propriété en chaîne de caractère
  3. La valeur initiale
  4. La valeur finale
  5. La durée de l’animation en seconde
  6. Le type de transition
  7. Le lissage à utiliser en combinaison avec l’équation.

Les deux derniers paramètres combinées correspondent à une équation de lissage. Cela permet de contrôler l’évolution de la valeur entre la valeur initiale et finale.

Cliquez sur l’icône de script à côté du nœud GUI pour l’ouvrir à nouveau. Le nœud Number a besoin de texte pour se mettre à jour, et Bar a besoin d’un flottant ou d’un entier. On peut utiliser interpolate_property pour animer un nombre, mais pas pour animer du texte directement. Nous allons l’utiliser pour animer une nouvelle variable GUI nommée animated_health.

En haut du script, définissez une nouvelle variable, nommez-la animated_health, et mettez sa valeur à 0, puis revenez à la fonction update_health et effacez son contenu. Animons la valeur « animated_health ». Appelez la fonction``interpolate_property`` du nœud Tween :

func update_health(new_value):
    tween.interpolate_property(self, "animated_health", animated_health, new_value, 0.6, Tween.TRANS_LINEAR, Tween.EASE_IN)
// Add this to the top of your class.
private float _animatedHealth = 0;

public void UpdateHealth(int health)
{
    _tween.InterpolateProperty(this, "_animatedHealth", _animatedHealth, health, 0.6f, Tween.TransitionType.Linear,
        Tween.EaseType.In);
}

Décomposons l’appel :

tween.interpolate_property(self, "animated_health", ...

On veut animer dans le nœud GUI, la propriété animated_health, or ce script étant rattaché à GUI, on peut s’y référer avec self. La méthode interpolate_property du nœud Tween a besoin du nom de la propriété en chaîne de caractère. C’est pour cela qu’on l’écrit "animated_health".

... _health", animated_health, new_value, 0.6 ...

La valeur initiale est la valeur actuelle de la barre de vie. Il faudra coder cette partie, mais dans notre cas on utilisera animated_health. La valeur finale de l’animation sera la new_value dans notre fonction update_health. Et 0.6 est la durée de l’animation en seconde.

...  0.6, tween.TRANS_LINEAR, Tween.EASE_IN)

Les deux derniers arguments sont des constantes de la classe Tween. TRANS_LINEAR nous donnera une animation linéaire et EASE_IN n’a pas d’action sur une animation linéaire, mais on doit donner un dernier argument sinon on aura une erreur.

Cette animation étant défini, elle ne sera joué que lorsque le nœud Tween``sera activé avec ``tween.start(). Il suffit de lancer l’animation une fois si le nœud n’est pas actif. Ajoutons ce code à la dernière ligne :

if not tween.is_active():
    tween.start()
if (!_tween.IsActive())
{
    _tween.Start();
}

Note

Même si nous aurions pu animer la propriété health de Player, ce n’est pas pertinent dans notre cas. Les personnages doivent perdre leur vie instantanément quand ils sont touchés. Cela rend la gestion des états des personnages plus simple, comme savoir si un personnage est mort. Nous voudrons toujours stocker les animations dans des conteneurs de données ou des nœuds séparés. Le nœud Tween est parfait pour animer à l’aide d’un script. Pour créer des animations en passant par l’éditeur, il faut utiliser le nœud AnimationPlayer.

Assigner animated_health à la barre de vie

Notre variable animated_health est maintenant animée mais nous ne mettons plus à jour les nœuds Bar``et ``Number. Arrangeons cela.

Jusqu’à maintenant, la fonction update_health ressemble à cela :

func update_health(new_value):
    tween.interpolate_property(self, "animated_health", animated_health, new_value, 0.6, Tween.TRANS_LINEAR, Tween.EASE_IN)
    if not tween.is_active():
        tween.start()
public void UpdateHealth(int health)
{
    _tween.InterpolateProperty(this, "_animatedHealth", _animatedHealth, health, 0.6f, Tween.TransitionType.Linear,
        Tween.EaseType.In);

    if(!_tween.IsActive())
    {
        _tween.Start();
    }
}

Dans ce cas particulier, number_label prends du texte, nous avons donc besoin d’utiliser la méthode _process pour l’animer. Nous allons maintenant mettre à jour les nœuds Number et TextureProgress comme avant, à l’intérieur de _process :

func _process(delta):
    number_label.text = str(animated_health)
    bar.value = animated_health
public override void _Process(float delta)
{
    _numberLabel.Text = _animatedHealth.ToString();
    _bar.Value = _animatedHealth;
}

Note

number_label et bar sont des variables qui stocke les références vers les noeuds Number et TextureProgress.

Lancez le jeu, vous observez que la barre s’anime sans accroc. Par contre le texte affiche un nombre décimal et fait n’importe quoi. De plus, en considérant le style du jeu, il serait bien que la barre de vie de s’anime d’une façon plus saccadée.

../../_images/lifebar_tutorial_number_animation_messed_up.gif

L’animation est fluide mais le nombre est cassé

On peut résoudre les deux problèmes en arrondissant la variable animated_health. Utiliser une varible locale round_value pour stocker l’arrondi. Puis assigner la à number_label.text et bar.value :

func _process(delta):
    var round_value = round(animated_health)
    number_label.text = str(round_value)
    bar.value = round_value
public override void _Process(float delta)
{
    var roundValue = Mathf.Round(_animatedHealth);
    _numberLabel.Text = roundValue.ToString();
    _bar.Value = roundValue;
}

Essayez le jeu une nouvelle fois pour voir une belle animation blocs par blocs.

../../_images/lifebar_tutorial_number_animation_working.gif

En arrondissant animated_health, on a fait d’une pierre deux coups

Astuce

À chaque fois que le joueur prends un coup, il envoie le signal health_changed, à partir duquel le nœud GUI appelle la fonction _on_Player_health_changed, qui appelle la fonction update_health. Cela met à jour la valeur animée et les variables number_label et bar dans la fonction _process. Retenez que l’animation de la barre de vie montrant la vie diminuer graduellement est une illusion pour rendre la GUI plus vivante. Si le joueur prends 3 dégâts, c’est immédiat.

Réaliser un fondu de la barre quand le joueur meurt

Quand le personnage vert meurt, il joue une animation de mort et disparaît. À ce moment on ne veut plus afficher l’interface. Faisons disparaître la barre quand le personnage meurt. On réutilisera le nœud Tween puisqu’il peut gérer plusieurs animations en parallèle.

Tout d’abord l’interface GUI doit être connecté au signal envoyé à la mort died du joueur ``Player`. Appuyer sur F1 pour repasser sur l’espace de travail 2D. Sélectionner le nœud ``Player``dans le dock des scène et cliquer dans l’inspecteur sur l’onglet Node.

Trouver et sélectionner le signal died, et cliquer sur le bouton Connect.

../../_images/lifebar_tutorial_player_died_signal_enemy_connected.png

Le signal devrait déjà être connecté à l’ennemi

Dans la fenêtre de connexion des signaux, connecter le nœud GUI. Le chemin du nœud devrait être ../../GUI et _on_Player_died devrait être afficher dans Method in Node . Laisser l’option Make Function activé et cliquer sur Connect au bas de la fenêtre. Cela vous emmènera sur le script GUI.gd.

../../_images/lifebar_tutorial_player_died_connecting_signal_window.png

Vous devriez avoir ces valeurs dans la fenêtre de connexion des signaux

Note

Vous devriez maintenant voir un modèle : chaque fois que le GUI a besoin d’une nouvelle pièce d’informations, nous émettons un nouveau signal. Utilisez-les judicieusement : plus vous ajoutez des connexions, plus elles seront difficiles à suivre.

Pour animer un fondu sur un élément UI, nous devons utiliser la propriété modulate. modulate est une Color qui multiplie les couleurs de nos textures.

Note

modulate provient de la classe CanvasItem, tous les nœuds 2D et UI en hérite. Cela vous permet de contrôler la visibilité du nœud, d’y assigner un shader, et de le modifier en utilisant une couleur avec modulate.

modulate prend une valeur Color avec 4 canaux : rouge, vert, bleu et alpha. Si vous assombrissons n’importe lequel des trois premiers canaux, cela assombrit l’interface. Si nous abaissons le canal alpha, notre interface disparaît petit à petit.

Nous allons interpoler entre deux couleurs : d’un blanc avec un alpha de 1`, soit une opacité totale, à un blanc pur avec une valeur alpha de ``0`, complètement transparent. Ajoutons deux variables en haut de la méthode ``_on_Player_died et nommons les start_color et end_color. Utilisez le constructeur Color() pour créer deux valeurs Color.

func _on_Player_died():
    var start_color = Color(1.0, 1.0, 1.0, 1.0)
    var end_color = Color(1.0, 1.0, 1.0, 0.0)
public void OnPlayerDied()
{
    var startColor = new Color(1.0f, 1.0f, 1.0f);
    var endColor = new Color(1.0f, 1.0f, 1.0f, 0.0f);
}

Color(1.0, 1.0, 1.0) correspond au blanc. Le quatrième argument, respectivement 1.0 et 0.0 pour start_color et end_color, est le canal alpha.

Nous devons ensuite appeler à nouveau la méthode interpolate_property du noeud Tween :

tween.interpolate_property(self, "modulate", start_color, end_color, 1.0, Tween.TRANS_LINEAR, Tween.EASE_IN)
_tween.InterpolateProperty(this, "modulate", startColor, endColor, 1.0f, Tween.TransitionType.Linear,
  Tween.EaseType.In);

Cette fois nous changeons la propriété modulate et nous faisons une animation à partir de la start_color jusqu’à la end_color. La durée est d’une seconde, avec une transition linéaire. Ici encore le lissage n’est pas important parce que la transition est linéaire. Voici la méthode _on_Player_died complète :

func _on_Player_died():
    var start_color = Color(1.0, 1.0, 1.0, 1.0)
    var end_color = Color(1.0, 1.0, 1.0, 0.0)
    tween.interpolate_property(self, "modulate", start_color, end_color, 1.0, Tween.TRANS_LINEAR, Tween.EASE_IN)
public void OnPlayerDied()
{
    var startColor = new Color(1.0f, 1.0f, 1.0f);
    var endColor = new Color(1.0f, 1.0f, 1.0f, 0.0f);

    _tween.InterpolateProperty(this, "modulate", startColor, endColor, 1.0f, Tween.TransitionType.Linear,
        Tween.EaseType.In);
}

Et voilà c’est tout. Vous pouvez lancer le jeu à présent afin de voir le résultat final !

../../_images/lifebar_tutorial_final_result.gif

Le résultat final. Félicitations d’être arrivé jusque là !

Note

En utilisant exactement la même technique, il est possible de changer la couleur de la barre quand le Joueur s’empoisonne, de la rendre rouge quand sa santé est faible, de secouer l’interface utilisateur quand il prend un coup critique … Le principe est le même : envoyer un signal qui transmet l’information du « Joueur » au « GUI » et laisser le « GUI » traiter ce signal.