Score et rejouer

Dans cette partie, nous ajouterons le score, la lecture de la musique et la possibilité de redémarrer le jeu.

Nous devons garder la trace du score actuel dans une variable et l'afficher à l'écran en utilisant une interface minimale. Nous allons utiliser une étiquette de texte pour ce faire.

Dans la scène principale, ajoutez un nouveau nœud Control comme enfant de Main et nommez-le UserInterface. Vous serez automatiquement amené à l'écran 2D, où vous pourrez modifier votre interface utilisateur (IU).

Ajoutez un nœud Label et renommez-le en ScoreLabel.

image0

Dans l'Inspecteur, définissez le Text du Label avec un texte générique tel que "Score : 0".

image1

En outre, le texte est blanc par défaut, comme l'arrière-plan de notre jeu. Nous devons changer sa couleur pour le voir au moment de l'exécution.

Faites défiler l'écran jusqu'à Couleurs personnalisées, développez "Couleurs" et cliquez sur la case noire à côté de Couleur de la police pour teinter le texte.

image2

Choisissez un ton sombre pour qu'il contraste bien avec la scène 3D.

image3

Enfin, cliquez et faites glisser le texte dans la fenêtre d'affichage pour l'éloigner du coin supérieur gauche.

image4

Le nœud UserInterface nous permet de regrouper notre interface utilisateur dans une branche de l'arbre de scène et d'utiliser une ressource de thème qui se propagera à tous ses enfants. Nous allons l’utiliser pour définir la police de caractères de notre jeu.

Création d'un thème d'interface utilisateur

Une fois encore, sélectionnez le nœud UserInterface. Dans l'Inspecteur, créez une nouvelle ressource de thème dans Theme -> Theme.

image5

Cliquez dessus pour ouvrir l'éditeur de thème dans le panneau inférieur. Il vous donne un aperçu de l'apparence de tous les widgets intégrés à l'interface utilisateur avec votre ressources de thème.

image6

Par défaut, un thème ne possède qu'une seule propriété, la Default Font.

Voir aussi

Vous pouvez ajouter d'autres propriétés à la ressource de thème pour concevoir des interfaces utilisateur complexes, mais cela dépasse le cadre de cette série. Pour en savoir plus sur la création et l'édition de thèmes, consultez Introduction à l'habillage des interfaces graphiques.

Cliquez sur la propriété Default Font et créez une nouvelle DynamicFont.

image7

Développez la DynamicFont en cliquant dessus et développez sa section Font. Vous y verrez un champ vide Font Data.

image8

Celui-ci attend un fichier de police comme ceux que vous avez sur votre ordinateur. DynamicFont supporte les formats suivants :

  • TrueType (.ttf)

  • OpenType(.otf)

  • Web Open Font Format 1 (.woff)

  • Web Open Font Format 2 (.woff2, depuis Godot 3.5)

Dans le dock FileSystem, développez le répertoire fonts et cliquez et faites glisser le fichier Montserrat-Medium.ttf que nous avons inclus dans le projet sur le Font Data. Le texte réapparaîtra dans l'aperçu du thème.

Le texte est un peu petit. Réglez le paramètre Settings -> Size sur 22 pixels pour augmenter la taille du texte.

image9

Garder une trace du score

Travaillons ensuite sur le score. Attachez un nouveau script au ScoreLabel et définissez la variable score.

extends Label

var score = 0

Le score devrait augmenter de 1 chaque fois que nous écrasons un monstre. Nous pouvons utiliser leur squashed signal pour savoir quand cela se produit. Cependant, comme nous instancions les monstres à partir du code, nous ne pouvons pas faire la connexion dans l'éditeur.

Au lieu de cela, nous devons établir la connexion depuis le code à chaque fois que nous créons un monstre.

Ouvrez le script Main.gd. S'il est toujours ouvert, vous pouvez cliquer sur son nom dans la colonne de gauche de l'éditeur de script.

image10

Vous pouvez également double-cliquer sur le fichier Main.gd dans le dock FileSystem.

Au bas de la fonction _on_MobTimer_timeout(), ajoutez la ligne suivante.

func _on_MobTimer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")

Cette ligne signifie que lorsque le mob émet le signal squashed, le noeud ScoreLabel le recevra et appellera la fonction _on_Mob_squashed().

Retournez au script ScoreLabel.gd pour définir la fonction de rappel _on_Mob_squashed().

Là, nous incrémentons le score et mettons à jour le texte affiché.

func _on_Mob_squashed():
    score += 1
    text = "Score: %s" % score

La deuxième ligne utilise la valeur de la variable score pour remplacer le caractère générique %s. En utilisant cette fonction, Godot convertit automatiquement les valeurs en texte, ce qui est pratique pour sortir du texte dans les étiquettes ou en utilisant la fonction print().

Voir aussi

Vous pouvez en savoir plus sur le formatage des chaînes de caractères ici : Chaînes de format GDScript.

Vous pouvez maintenant jouer le jeu et écraser quelques ennemis pour voir le score augmenter.

image11

Note

Dans un jeu complexe, vous voudrez peut-être séparer complètement votre interface utilisateur de l'univers du jeu. Dans ce cas, vous ne garderez pas trace du score sur l'étiquette. Au lieu de cela, vous voudrez peut-être le stocker dans un objet distinct et dédié. Mais pour le prototypage ou lorsque votre projet est simple, il est bon de garder votre code simple. La programmation est toujours un exercice d'équilibre.

Réessayer le jeu

Nous allons maintenant ajouter la possibilité de rejouer après être mort. Lorsque le joueur meurt, nous affichons un message à l'écran et nous attendons une entrée.

Revenez à la scène Main, sélectionnez le noeud UserInterface, ajoutez un noeud ColorRect comme enfant de celui-ci et nommez-le Retry. Ce nœud remplit un rectangle avec une couleur uniforme et servira de superposition pour assombrir l'écran.

Pour qu'il s'étende sur toute la fenêtre, vous pouvez utiliser le menu Layout de la barre d'outils.

image12

Ouvrez-le et appliquez la commande Full Rect.

image13

Rien ne se passe. Enfin, presque rien : seules les quatre broches vertes se déplacent vers les coins de la boîte de sélection.

image14

En effet, les nœuds d'interface utilisateur (tous ceux qui ont une icône verte) fonctionnent avec des ancres et des marges relatives à la boîte englobante de leur parent. Ici, le nœud UserInterface a une petite taille et le nœud Retry est limité par celle-ci.

Sélectionnez UserInterface et appliquez Layout -> Full Rect à celle-ci également. Le nœud Retry devrait maintenant couvrir la totalité de la fenêtre.

Changeons sa couleur pour qu'il assombrisse la zone de jeu. Sélectionnez Retry et dans l'Inspecteur, définissez sa Couleur sur quelque chose de sombre et de transparent. Pour ce faire, dans le sélecteur de couleurs, faites glisser le curseur A vers la gauche. Il contrôle le canal alpha de la couleur, c'est-à-dire son opacité.

image15

Ensuite, ajoutez un Label en tant qu'enfant de Retry et donnez-lui le Text "Press Enter to retry."

image16

Pour le déplacer et l'ancrer au centre de l'écran, appliquez-lui Layout -> Center.

image17

Coder l'option réessayer

Nous pouvons maintenant nous diriger vers le code pour afficher et cacher le nœud Retry lorsque le joueur meurt et rejoue.

Ouvrez le script Main.gd. Tout d'abord, nous voulons cacher l'overlay au début du jeu. Ajoutez cette ligne à la fonction _ready().

func _ready():
    #...
    $UserInterface/Retry.hide()

Ensuite, quand le joueur est touché, nous montrons la surimpression.

func _on_Player_hit():
    #...
    $UserInterface/Retry.show()

Enfin, lorsque le noeud Retry est visible, nous devons écouter l'entrée du joueur et redémarrer le jeu s'il appuie sur Entrée. Pour ce faire, nous utilisons le callback intégré _unhandled_input().

Si le joueur a appuyé sur l'action d'entrée prédéfinie ui_accept et que Retry est visible, nous rechargeons la scène actuelle.

func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        # This restarts the current scene.
        get_tree().reload_current_scene()

La fonction get_tree() nous donne accès à l'objet global SceneTree, qui nous permet de recharger et de redémarrer la scène actuelle.

Ajouter de la musique

Pour ajouter une musique qui joue continuellement en arrière-plan, nous allons utiliser une autre fonctionnalité de Godot : autoloads.

Pour lire de l'audio, il suffit d'ajouter un nœud AudioStreamPlayer à votre scène et d'y attacher un fichier audio. Lorsque vous démarrez la scène, elle peut jouer automatiquement. Cependant, lorsque vous rechargez la scène, comme nous le faisons pour jouer à nouveau, les nœuds audio sont également réinitialisés, et la musique reprend depuis le début.

Vous pouvez utiliser la fonction autoload pour que Godot charge automatiquement un nœud ou une scène au début du jeu, en dehors de la scène actuelle. Vous pouvez également l'utiliser pour créer des objets accessibles à tous.

Créez une nouvelle scène en allant dans le menu Scène et en cliquant sur Nouvelle scène.

image18

Cliquez sur le bouton Autre nœud pour créer un AudioStreamPlayer et le renommer en MusicPlayer.

image19

Nous avons inclus une bande sonore dans le répertoire art/, House In a Forest Loop.ogg. Cliquez et faites-la glisser sur la propriété Stream dans l'Inspector. Activez également l'option Autoplay pour que la musique soit jouée automatiquement au début du jeu.

image20

Enregistrez la scène sous le nom de MusicPlayer.tscn.

Nous devons l'enregistrer en tant qu'autoload. Allez dans le menu Projet -> Paramètres du projet... et cliquez sur l'onglet Autoload.

Dans le champ Chemin, vous voulez entrer le chemin d'accès à votre scène. Cliquez sur l'icône du dossier pour ouvrir le navigateur de fichiers et double-cliquez sur MusicPlayer.tscn. Ensuite, cliquez sur le bouton Add à droite pour enregistrer le nœud.

image21

Si vous lancez le jeu maintenant, la musique jouera automatiquement. Et même si vous perdez et réessayez, elle continue.

Avant de conclure cette leçon, voici un bref aperçu de son fonctionnement. Lorsque vous lancez le jeu, votre dock Scene change pour vous donner deux onglets : Remote et Local.

image22

L'onglet Remote vous permet de visualiser l'arbre de nœuds de votre jeu en cours. Vous y verrez le nœud Main et tout ce que la scène contient ainsi que les mobs instanciés en bas.

image23

Au sommet se trouvent le MusicPlayer autochargé et un noeud root, qui est le viewport de votre jeu.

Et c'est tout pour cette leçon. Dans la prochaine partie, nous ajouterons une animation pour rendre le jeu plus agréable à regarder et à toucher.

Voici le script complet Main.gd pour référence.

extends Node

export (PackedScene) var mob_scene


func _ready():
    randomize()
    $UserInterface/Retry.hide()


func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        get_tree().reload_current_scene()


func _on_MobTimer_timeout():
    var mob = mob_scene.instance()

    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    mob_spawn_location.unit_offset = randf()

    var player_position = $Player.transform.origin
    mob.initialize(mob_spawn_location.translation, player_position)

    add_child(mob)
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")


func _on_Player_hit():
    $MobTimer.stop()
    $UserInterface/Retry.show()