Up to date

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

Coder le joueur

Dans cette leçon, nous ajouterons le mouvement du joueur, l'animation, et la détection des collisions.

Pour ce faire, nous devons ajouter des fonctionnalités que nous ne pouvons pas obtenir à partir d'un nœud intégré, nous allons donc ajouter un script. Cliquez sur le nœud Player et cliquez sur le bouton "Attacher un script" :

../../_images/add_script_button.webp

Dans la fenêtre de réglages du script, vous pouvez laisser les paramètres par défaut. Cliquez simplement sur "Créer" :

Note

Si vous créez un script en C# ou un autre langage, sélectionnez le language dans le menu déroulant langage avant de cliquer sur créer.

../../_images/attach_node_window.webp

Note

Si c'est la première fois que vous rencontrez du GDScript, merci de lire Langages de script avant de continuer.

Commencez par déclarer les variables membres dont cet objet aura besoin :

extends Area2D

@export var speed = 400 # How fast the player will move (pixels/sec).
var screen_size # Size of the game window.

L'utilisation du mot-clé export sur la première variable SPEED nous permet de définir sa valeur dans l'inspecteur. Cela peut être pratique pour les valeurs que vous voulez pouvoir ajuster de la même manière que les propriétés intégrées d'un nœud. Cliquez sur le nœud Player et vous verrez maintenant apparaître la section "Script Variables" dans l'inspecteur. Notez que, si vous changez la valeur ici, cela remplacera la valeur écrite dans le script.

Avertissement

If you're using C#, you need to (re)build the project assemblies whenever you want to see new export variables or signals. This build can be manually triggered by clicking the Build button at the top right of the editor.

../../_images/build_dotnet.webp
../../_images/export_variable.webp

Your player.gd script should already contain a _ready() and a _process() function. If you didn't select the default template shown above, create these functions while following the lesson.

La fonction _ready() est appelée lorsqu'un nœud entre dans l'arbre de scène, ce qui est un bon moment pour trouver la taille de la fenêtre de jeu :

func _ready():
    screen_size = get_viewport_rect().size

Maintenant nous pouvons utiliser la fonction _process() pour définir ce que le joueur va faire. _process() est appelée à chaque image, nous l'utiliserons donc pour mettre à jour les éléments de notre jeu qui vont changer souvent. Pour le joueur, nous devons faire ce qui suit :

  • Vérifier les entrées.

  • Se déplacer dans la direction donnée.

  • Jouer l'animation appropriée.

Tout d'abord, nous devons vérifier les entrées - le joueur appuie-t-il sur une touche ? Pour ce jeu, nous avons 4 entrées de direction à vérifier. Les actions d'entrées sont définies dans les Paramètres du projet sous "Contrôles". Vous pouvez définir des événements personnalisés et leur affecter différentes touches, des événements souris ou d'autres entrées. Pour ce jeu, nous allons associer les touches fléchées aux quatre directions.

Cliquez sur Projet -> Paramètres du projet... pour ouvrir la fenêtre des paramètres du projet et cliquez sur l'onglet Contrôles en haut. Tapez "move_right" dans la barre supérieure et cliquez sur le bouton "Ajouter" pour ajouter l'action move_right.

../../_images/input-mapping-add-action.webp

We need to assign a key to this action. Click the "+" icon on the right, to open the event manager window.

../../_images/input-mapping-add-key.webp

The "Listening for Input..." field should automatically be selected. Press the "right" key on your keyboard, and the menu should look like this now.

../../_images/input-mapping-event-configuration.webp

Select the "ok" button. The "right" key is now associated with the move_right action.

Répétez ces étapes pour ajouter trois autres contrôles :

  1. move_left lié à la touche flèche gauche.

  2. move_up lié à la touche flèche haut.

  3. Et move_down lié à la touche flèche vers le bas.

Votre onglet contrôles devrait ressembler à ceci :

../../_images/input-mapping-completed.webp

Cliquez sur le bouton "Fermer" pour fermer les paramètres du projet.

Note

Nous n'avons associé qu'une seule touche à chaque action d'entrée, mais vous pouvez associer plusieurs touches, boutons de joystick ou boutons de souris à la même action d'entrée.

Vous pouvez détecter si une touche est pressée en utilisant Input.is_action_pressed(), qui retourne true s'il elle est pressée ou false si elle ne l'est pas.

func _process(delta):
    var velocity = Vector2.ZERO # The player's movement vector.
    if Input.is_action_pressed("move_right"):
        velocity.x += 1
    if Input.is_action_pressed("move_left"):
        velocity.x -= 1
    if Input.is_action_pressed("move_down"):
        velocity.y += 1
    if Input.is_action_pressed("move_up"):
        velocity.y -= 1

    if velocity.length() > 0:
        velocity = velocity.normalized() * speed
        $AnimatedSprite2D.play()
    else:
        $AnimatedSprite2D.stop()

Nous commençons par régler velocity à (0, 0) - par défaut le joueur ne doit pas bouger. Ensuite nous vérifions chaque entrées et les ajoutons ou les soustrayons à la velocity pour obtenir une direction totale. Par exemple, si vous maintenez right et down en même temps, le vecteur velocity résultant sera (1, 1). Dans ce cas, puisque nous ajoutons un mouvement horizontal et un mouvement vertical, le joueur se déplacerait plus vite que s'il se déplaçait horizontalement.

Nous pouvons empêcher cela si nous normalisons la vitesse, ce qui signifie que nous réglons sa longueur à 1, et la multiplions par la vitesse désirée. Cela signifie qu'il n'y a plus de mouvement diagonal rapide.

Astuce

Si vous n'avez jamais utilisé les mathématiques vectorielles auparavant, ou si vous avez besoin d'un rafraîchissement, vous pouvez voir une explication de l'utilisation des vecteurs dans Godot à Mathématiques des vecteurs. C'est bon à savoir mais ça ne sera pas nécessaire pour le reste de ce tutoriel.

We also check whether the player is moving so we can call play() or stop() on the AnimatedSprite2D.

Astuce

$ is shorthand for get_node(). So in the code above, $AnimatedSprite2D.play() is the same as get_node("AnimatedSprite2D").play().

In GDScript, $ returns the node at the relative path from the current node, or returns null if the node is not found. Since AnimatedSprite2D is a child of the current node, we can use $AnimatedSprite2D.

Maintenant que nous avons une direction de mouvement, nous pouvons mettre à jour la position du joueur. Nous pouvons aussi utiliser clamp() pour l'empêcher de quitter l'écran. Clamping une valeur signifie la limiter à une plage donnée. Ajoutez ce qui suit au bas de la fonction _process (assurez-vous que ce n'est pas indenté sous le else) :

position += velocity * delta
position = position.clamp(Vector2.ZERO, screen_size)

Astuce

Le paramètre delta de la fonction _process() fait référence à la longueur d'une trame - le temps que l'image précédente a mis pour se terminer. Utiliser cette valeur assure que le mouvement restera constant même si le taux d'images par seconde varie.

Cliquez sur "Lancer la scène" (F6, Cmd + R on macOS) et vérifiez que vous pouvez déplacer le joueur autour de l'écran dans toutes les directions.

Avertissement

Si vous obtenez une erreur le panneau "Debugger" qui dit

Attempt to call function 'play' in base 'null instance' on a null instance

this likely means you spelled the name of the AnimatedSprite2D node wrong. Node names are case-sensitive and $NodeName must match the name you see in the scene tree.

Choisir les animations

Now that the player can move, we need to change which animation the AnimatedSprite2D is playing based on its direction. We have the "walk" animation, which shows the player walking to the right. This animation should be flipped horizontally using the flip_h property for left movement. We also have the "up" animation, which should be flipped vertically with flip_v for downward movement. Let's place this code at the end of the _process() function:

if velocity.x != 0:
    $AnimatedSprite2D.animation = "walk"
    $AnimatedSprite2D.flip_v = false
    # See the note below about boolean assignment.
    $AnimatedSprite2D.flip_h = velocity.x < 0
elif velocity.y != 0:
    $AnimatedSprite2D.animation = "up"
    $AnimatedSprite2D.flip_v = velocity.y > 0

Note

Les affectations booléennes dans le code ci-dessus sont un raccourci courant pour les programmeurs. Puisque nous faisons un test de comparaison (booléen) et aussi assignons une valeur booléenne, nous pouvons faire les deux en même temps. Considérez ce code par rapport à l'affectation booléenne d'une ligne ci-dessus :

if velocity.x < 0:
    $AnimatedSprite2D.flip_h = true
else:
    $AnimatedSprite2D.flip_h = false

Relancez la scène et vérifiez que les animations sont correctes dans chacune des directions.

Astuce

Une erreur courante consiste ici à mal taper les noms des animations. Les noms des animations dans le panneau SpriteFrames doivent correspondre à ce que vous tapez dans le code. Si vous avez nommé l'animation "Walk", vous devez également utiliser un "W" majuscule dans le code.

Lorsque vous êtes sûr que le mouvement fonctionne correctement, ajoutez cette ligne à _ready(), afin que le joueur soit caché au début du jeu :

hide()

Préparation pour les collisions

Nous voulons que Player détecte quand il est touché par un ennemi, mais nous ne n’avons pas encore créé d'ennemis ! Ce n'est pas grave, car nous allons utiliser la fonctionnalité signal de Godot pour le faire fonctionner.

Add the following at the top of the script. If you're using GDScript, add it after extends Area2D. If you're using C#, add it after public partial class Player : Area2D:

signal hit

Ceci définit un signal personnalisé appelé "hit" que notre joueur émettra lorsqu'il entre en collision avec un ennemi. Nous utiliserons Area2D pour détecter la collision. Sélectionnez le nœud Player et cliquez sur l'onglet "Nœud" à côté de l'onglet Inspecteur pour voir la liste des signaux que le joueur peut émettre :

../../_images/player_signals.webp

Notice our custom "hit" signal is there as well! Since our enemies are going to be RigidBody2D nodes, we want the body_entered(body: Node2D) signal. This signal will be emitted when a body contacts the player. Click "Connect.." and the "Connect a Signal" window appears.

Godot will create a function with that exact name directly in script for you. You don't need to change the default settings right now.

Avertissement

If you're using an external text editor (for example, Visual Studio Code), a bug currently prevents Godot from doing so. You'll be sent to your external editor, but the new function won't be there.

In this case, you'll need to write the function yourself into the Player's script file.

../../_images/player_signal_connection.webp

Note the green icon indicating that a signal is connected to this function; this does not mean the function exists, only that the signal will attempt to connect to a function with that name, so double-check that the spelling of the function matches exactly!

Next, add this code to the function:

func _on_body_entered(body):
    hide() # Player disappears after being hit.
    hit.emit()
    # Must be deferred as we can't change physics properties on a physics callback.
    $CollisionShape2D.set_deferred("disabled", true)

Chaque fois qu'un ennemi frappe le joueur, le signal sera émis. Nous devons désactiver la collision du joueur afin de ne pas déclencher le signal hit plus d'une fois.

Note

Désactiver la forme de la zone de collision peut provoquer une erreur si cela se produit pendant le traitement des collisions par le moteur. L'utilisation de set_deferred() indique à Godot d'attendre pour désactiver la forme jusqu'à ce que l'on puisse le faire en toute sécurité.

La dernière étape consiste à ajouter une fonction que nous pouvons appeler pour réinitialiser le joueur au début d'une nouvelle partie.

func start(pos):
    position = pos
    show()
    $CollisionShape2D.disabled = false

Maintenant que le joueur fonctionne, nous allons travailler sur l'ennemi dans la prochaine leçon.