Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

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.

In the main scene, add a new child node Control to Main and name it UserInterface. You will automatically be taken to the 2D screen, where you can edit your User Interface (UI).

Add a Label node and name it ScoreLabel

image1

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

image2

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.

Scroll down to Theme Overrides, and expand Colors and enable Font Color in order to tint the text to black (which contrasts well with the white 3D scene)

image3

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

image4

The UserInterface node allows us to group our UI in a branch of the scene tree and use a theme resource that will propagate to all its children. We'll use it to set our game's font.

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

Once again, select the UserInterface node. In the Inspector, create a new theme resource in 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.

Celui-ci attend un fichier de police comme ceux que vous avez sur votre ordinateur. Deux formats de fichiers de police communs sont TrueType Font (TTF) et OpenType Font (OTF).

In the FileSystem dock, expand the fonts directory and click and drag the Montserrat-Medium.ttf file we included in the project onto the Default Font. The text will reappear in the theme preview.

The text is a bit small. Set the Default Font Size to 22 pixels to increase the text's size.

image7

Garder une trace du score

Let's work on the score next. Attach a new script to the ScoreLabel and define the score variable.

extends Label

var score = 0

The score should increase by 1 every time we squash a monster. We can use their squashed signal to know when that happens. However, because we instantiate monsters from the code, we cannot connect the mob signal to the ScoreLabel via the editor.

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

Open the script main.gd. If it's still open, you can click on its name in the script editor's left column.

image8

Alternatively, you can double-click the main.gd file in the FileSystem dock.

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

func _on_mob_timer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

This line means that when the mob emits the squashed signal, the ScoreLabel node will receive it and call the function _on_mob_squashed().

Head back to the ScoreLabel.gd script to define the _on_mob_squashed() callback function.

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

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

The second line uses the value of the score variable to replace the placeholder %s. When using this feature, Godot automatically converts values to string text, which is convenient when outputting text in labels or when using the print() function.

Voir aussi

You can learn more about string formatting here: Chaînes de format GDScript. In C#, consider using string interpolation with "$".

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

image9

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.

Head back to the main.tscn scene, select the UserInterface node, add a child node ColorRect, and name it Retry. This node fills a rectangle with a uniform color and will serve as an overlay to darken the screen.

To make it span over the whole viewport, you can use the Anchor Preset menu in the toolbar.

image10

Ouvrez-le et appliquez la commande Full Rect.

image11

Nothing happens. Well, almost nothing; only the four green pins move to the corners of the selection box.

image12

This is because UI nodes (all the ones with a green icon) work with anchors and margins relative to their parent's bounding box. Here, the UserInterface node has a small size and the Retry one is limited by it.

Select the UserInterface and apply Anchor Preset -> Full Rect to it as well. The Retry node should now span the whole viewport.

Let's change its color so it darkens the game area. Select Retry and in the Inspector, set its Color to something both dark and transparent. To do so, in the color picker, drag the A slider to the left. It controls the color's Alpha channel, that is to say, its opacity/transparency.

image13

Next, add a Label as a child of Retry and give it the Text "Press Enter to retry." To move it and anchor it in the center of the screen, apply Anchor Preset -> Center to it.

image14

Coder l'option réessayer

We can now head to the code to show and hide the Retry node when the player dies and plays again.

Open the script main.gd. First, we want to hide the overlay at the start of the game. Add this line to the _ready() function.

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

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

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

Finally, when the Retry node is visible, we need to listen to the player's input and restart the game if they press enter. To do this, we use the built-in _unhandled_input() callback, which is triggered on any input.

If the player pressed the predefined ui_accept input action and Retry is visible, we reload the current scene.

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.

To play audio, all you need to do is add an AudioStreamPlayer node to your scene and attach an audio file to it. When you start the scene, it can play automatically. However, when you reload the scene, like we do to play again, the audio nodes are also reset, and the music starts back from the beginning.

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.

Create a new scene by going to the Scene menu and clicking New Scene or by using the + icon next to your currently opened scene.

image15

Click the Other Node button to create an AudioStreamPlayer and rename it to MusicPlayer.

image16

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.

image17

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.

image18

MusicPlayer.tscn now loads into any scene you open or play. So if you run the game now, the music will play automatically in any scene.

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.

image19

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.

image20

At the top are the autoloaded MusicPlayer and a root node, which is your game's viewport.

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.

Here is the complete main.gd script for reference.

extends Node

@export var mob_scene: PackedScene

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


func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

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

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()