Déplacer le joueur avec du code

C'est l'heure de coder ! Nous allons utiliser les actions que nous avons créées dans la partie précédente pour déplacer le personnage.

Faîtes un clic droit sur le nœud Player et sélectionnez Attacher un script pour lui ajouter un nouveau script. Dans la fenêtre contextuelle, mettez le Modèle à Empty avant d'appuyer sur le bouton Créer.

image0

Commençons par les propriétés de la classe. Nous allons définir une vitesse de déplacement, une accélération de chute qui représente la gravité, et une vélocité que nous utiliserons pour déplacer le personnage.

extends KinematicBody

# How fast the player moves in meters per second.
export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
export var fall_acceleration = 75

var velocity = Vector3.ZERO

Ce sont des propriétés courantes pour un corps pouvant se déplacer. La velocity est un vecteur 3D combinant une vitesse et une direction. Ici, nous la définissons comme propriété car nous voulons la mettre à jour et réutiliser sa valeur d'une image à l'autre.

Note

Les valeurs sont assez différentes du code 2D car les distances sont exprimées en mètres. En 2D, mille unités (pixels) peuvent ne correspondre qu'à la moitié de la largeur de votre écran, en 3D, c'est un kilomètre.

Codons le déplacement maintenant. Nous commençons par calculer le vecteur de direction en utilisant l'objet Input global, dans _physics_process().

func _physics_process(delta):
    # We create a local variable to store the input direction.
    var direction = Vector3.ZERO

    # We check for each move input and update the direction accordingly.
    if Input.is_action_pressed("move_right"):
        direction.x += 1
    if Input.is_action_pressed("move_left"):
        direction.x -= 1
    if Input.is_action_pressed("move_back"):
        # Notice how we are working with the vector's x and z axes.
        # In 3D, the XZ plane is the ground plane.
        direction.z += 1
    if Input.is_action_pressed("move_forward"):
        direction.z -= 1

Ici, nous allons faire tous nos calculs dans la fonction virtuelle _physics_process(). Comme _process(), cela nous permet de mettre à jour le nœud à chaque image, mais elle est conçue spécifiquement pour le code lié à la physique, comme le déplacement d'un corps cinématique ou rigide.

Voir aussi

Pour en savoir plus sur la différence entre _process() et _physics_process(), voir Idle et traitement physique.

Nous commençons par initialiser une variable direction à Vector3.ZERO. Ensuite, nous vérifions si le joueur appuie sur une ou plusieurs des entrées move_*, et nous mettons à jour les composants x et z du vecteur en conséquence. Ils correspondent aux axes du plan du sol.

Ces quatre conditions nous donnent huit possibilités et huit directions possibles.

Dans le cas où le joueur appuie, par exemple, sur W et D simultanément, le vecteur aura une longueur d'environ 1.4. Mais si il appuie sur une seule touche, le vecteur aura une longueur de 1. Nous voulons que la longueur du vecteur soit consistente. Pour ce faire, nous pouvons appeler sa méthode normalize().

#func _physics_process(delta):
    #...

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        $Pivot.look_at(translation + direction, Vector3.UP)

Ici, nous normalisons seulement le vecteur si la direction a une longueur supérieure à zéro, ce qui signifie que le joueur appuie sur une touche de direction.

Dans ce cas, nous récupérons également le nœud Pivot et appelons sa méthode look_at(). Cette méthode prend une position dans l'espace en coordonnées globales vers laquelle regarder, et la direction vers le haut. Dans ce cas, nous pouvons utiliser la constante Vector3.UP.

Note

Les coordonnées locales d'un nœud, comme translation, sont relatives à leur parent. Les coordonnées globales sont relatives aux principaux axes du monde que vous pouvez voir dans la fenêtre d'affichage.

En 3D, la propriété qui contient la position d'un nœud est translation. En lui ajoutant la direction, nous obtenons une position où regarder qui est à un mètre du Player.

Ensuite, nous mettons à jour la vélocité. Nous devons calculer la vélocité au sol et la vitesse de chute séparément. Assurez-vous d'indenter une fois vers l'arrière pour que les lignes soient à l'intérieur de la fonction _physics_process() mais en dehors de la condition que nous venons d'écrire.

func _physics_process(delta):
    #...
    if direction != Vector3.ZERO:
        #...

    # Ground velocity
    velocity.x = direction.x * speed
    velocity.z = direction.z * speed
    # Vertical velocity
    velocity.y -= fall_acceleration * delta
    # Moving the character
    velocity = move_and_slide(velocity, Vector3.UP)

Pour la vélocité verticale, nous soustrayons l'accélération de la chute multipliée par le temps delta à chaque image. Remarquez l'utilisation de l'opérateur -=, qui est un raccourci pour variable = variable - ....

Cette ligne de code va faire descendre notre personnage à chaque frame. Cela peut sembler étrange si il est déjà sur le sol. Mais nous devons le faire pour que le personnage soit en collision avec le sol à chaque image.

Le moteur de physique peut seulement détecter les interactions avec les murs, le sol, ou d'autres corps pendant une image donnée si des mouvements et des collisions se produisent. Nous utiliserons cette propriété plus tard pour coder le saut.

Sur la dernière ligne, nous appelons KinematicBody.move_and_slide(). C'est une méthode puissante de la classe KinematicBody qui nous permet de déplacer fluidement un personnage. Si il touche un mur au milieu du déplacement, le moteur va essayer de le fluidifier pour vous.

La fonction prend deux paramètres : notre vélocité et la direction vers le haut. Elle déplace le caractère et retourne la vélocité restante après avoir appliqué les collisions. Lors de la collision avec le sol ou un mur, la fonction va réduire ou réinitialiser la vitesse vers cette direction pour vous. Dans notre cas, stocker la valeur retournée par la fonction empêche le personnage d'accumuler un momentum vertical, qui pourrait autrement devenir si important que le personnage passerait à travers le sol après un certain temps.

Et c'est tout le code dont vous avez besoin pour déplacer le personnage sur le sol.

Voici le code complet Player.gd pour référence.

extends KinematicBody

# How fast the player moves in meters per second.
export var speed = 14
# The downward acceleration when in the air, in meters per second squared.
export var fall_acceleration = 75

var velocity = Vector3.ZERO


func _physics_process(delta):
    var direction = Vector3.ZERO

    if Input.is_action_pressed("move_right"):
        direction.x += 1
    if Input.is_action_pressed("move_left"):
        direction.x -= 1
    if Input.is_action_pressed("move_back"):
        direction.z += 1
    if Input.is_action_pressed("move_forward"):
        direction.z -= 1

    if direction != Vector3.ZERO:
        direction = direction.normalized()
        $Pivot.look_at(translation + direction, Vector3.UP)

    velocity.x = direction.x * speed
    velocity.z = direction.z * speed
    velocity.y -= fall_acceleration * delta
    velocity = move_and_slide(velocity, Vector3.UP)

Tester le mouvement de notre joueur

Nous allons mettre notre joueur dans la scène Main pour le tester. Pour ce faire, nous devons instancier le joueur, puis ajouter une caméra. Contrairement à la 2D, en 3D, vous ne verrez rien si votre scène n'a pas de caméra pointant vers quelque chose.

Enregistrez votre scène Player et ouvrez la scène Main. Vous pouvez cliquer sur l'onglet Main en haut de l'éditeur pour le faire.

image1

Si vous avez déjà fermé la scène, dirigez-vous vers le dock Système de fichiers et double-cliquez sur Maint.tscn pour la rouvrir.

Pour instancier le Player, faîtes un clic droit sur le nœud Main et sélectionnez Instancier une scène enfant.

image2

Dans la fenêtre contextuelle, double-cliquez sur Player.tscn. Le personnage devrait apparaître au milieu de la fenêtre d'affichage.

Ajout d'une caméra

Ajoutons ensuite la caméra. Comme nous l'avons fait avec le Pivot du Player, nous allons créer un rig basique. Faîtes un clic-droit sur le nœud Main à nouveau, mais sélectionnez Ajouter un nœud enfant cette fois. Créez un nouveau Position3D, nommez-le CameraPivot, et ajoutez-lui un nœud Camera comme enfant. Votre arbre de scène devrait ressembler à ceci.

image3

Remarquez la case à cocher Aperçu qui apparaît en haut à gauche lorsque la Camera est sélectionnée. Vous pouvez cliquer dessus pour avoir un aperçu de la projection de la caméra dans le jeu.

image4

Nous allons utiliser le Pivot pour faire pivoter la caméra comme si elle était sur une grue. Divisons d'abord la vue 3D pour pouvoir naviguer librement dans la scène tout en voyant ce que voit la caméra.

Dans la barre d'outils juste au-dessus de la fenêtre d'affichage, cliquez sur Affichage, puis 2 vues. Vous pouvez également appuyer sur Ctrl + 2 (Cmd + 2 sur MacOS).

image5

Dans le vue du bas, sélectionnez la Camera et activez l'aperçu de la caméra en cliquant sur la case à cocher.

image6

Dans la vue du haut, déplacez la caméra d'environ 19 unités sur l'axe Z (le bleu).

image7

C'est là que la magie opère. Sélectionnez le CameraPivot et faîtes-le pivoter de 45 degrés autour de l'axe X (en utilisant le cercle rouge). Vous verrez la caméra bouger comme si elle était attachée à une grue.

image8

Vous pouvez lancer la scène en appuyant sur F6 et utiliser les touches fléchées pour déplacer le personnage.

image9

Nous pouvons voir un certain espace vide autour du personnage à cause de la projection en perspective. Dans ce jeu, nous utiliserons une projection orthographique à la place pour mieux encadrer la zone de jeu et faciliter la lecture des distances pour le joueur.

Sélectionnez à nouveau la Camera et, dans l'Inspecteur, réglez la Projection sur Orthogonal et la Size à 19. Le personnage devrait maintenant avoir l'air plus plat et le sol devrait remplir tout l'arrière-plan.

image10

Avec ceci, nous avons à la fois le mouvement du joueur et la vue en place. Ensuite, nous allons travailler sur les monstres.