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" :
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.
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.
using Godot;
public partial class Player : Area2D
{
[Export]
public int Speed { get; set; } = 400; // How fast the player will move (pixels/sec).
public Vector2 ScreenSize; // 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.
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
public override void _Ready()
{
ScreenSize = GetViewportRect().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
.
We need to assign a key to this action. Click the "+" icon on the right, to open the event manager window.
The "Listening for Input..." field should automatically be selected. Press the "right" key on your keyboard, and the menu should look like this now.
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 :
move_left
lié à la touche flèche gauche.move_up
lié à la touche flèche haut.Et
move_down
lié à la touche flèche vers le bas.
Votre onglet contrôles devrait ressembler à ceci :
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()
public override void _Process(double delta)
{
var velocity = Vector2.Zero; // The player's movement vector.
if (Input.IsActionPressed("move_right"))
{
velocity.X += 1;
}
if (Input.IsActionPressed("move_left"))
{
velocity.X -= 1;
}
if (Input.IsActionPressed("move_down"))
{
velocity.Y += 1;
}
if (Input.IsActionPressed("move_up"))
{
velocity.Y -= 1;
}
var animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
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)
Position += velocity * (float)delta;
Position = new Vector2(
x: Mathf.Clamp(Position.X, 0, ScreenSize.X),
y: Mathf.Clamp(Position.Y, 0, ScreenSize.Y)
);
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
if (velocity.X != 0)
{
animatedSprite2D.Animation = "walk";
animatedSprite2D.FlipV = false;
// See the note below about boolean assignment.
animatedSprite2D.FlipH = velocity.X < 0;
}
else if (velocity.Y != 0)
{
animatedSprite2D.Animation = "up";
animatedSprite2D.FlipV = 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
if (velocity.X < 0)
{
animatedSprite2D.FlipH = true;
}
else
{
animatedSprite2D.FlipH = 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()
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
// Don't forget to rebuild the project so the editor knows about the new signal.
[Signal]
public delegate void HitEventHandler();
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 :
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.
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)
private void OnBodyEntered(Node2D body)
{
Hide(); // Player disappears after being hit.
EmitSignal(SignalName.Hit);
// Must be deferred as we can't change physics properties on a physics callback.
GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred(CollisionShape2D.PropertyName.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
public void Start(Vector2 position)
{
Position = position;
Show();
GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
}
Maintenant que le joueur fonctionne, nous allons travailler sur l'ennemi dans la prochaine leçon.