Vue d’ensemble du mouvement 2D

Introduction

Tous les débutants se demandent : « Comment déplacer mon personnage ? ». Selon le style de jeu que vous réalisez, vous pouvez avoir des exigences particulières, mais en général, les mouvements de la plupart des jeux en 2D sont basés sur un petit nombre de dessins.

Nous utiliserons KinematicBody2D pour cet exemple, mais les principes s’appliqueront tout autant aux autres types de nœuds ((Area2D, RigidBody2D).

Configuration

Chaque exemple ci-dessous utilise la même configuration de scène. Commencez avec un KinematicBody2D avec deux enfants: Sprite et CollisionShape2D. Vous pouvez utiliser l’icône Godot (« icon.png ») pour la texture du Sprite ou utiliser n’importe quelle autre image 2D que vous avez.

Ouvrez Projet -> Paramètres du projet et sélectionnez l’onglet « Plan des entrées ». Ajoutez les actions d’entrées suivantes (voir InputEvent pour les détails) :

../../_images/movement_inputs.png

Mouvement 8 directions

Dans ce scénario, vous souhaitez que l’utilisateur appuie sur les quatre touches directionnelles (haut/gauche/bas/droite ou Z/Q/S/D) et se déplace dans la direction sélectionnée. Le nom « mouvement à 8 directions » vient du fait que le joueur peut se déplacer en diagonale en appuyant sur deux touches simultanément.

../../_images/movement_8way.gif

Ajoutez un script au kinematic body et ajoutez le code suivant :

extends KinematicBody2D

export (int) var speed = 200

var velocity = Vector2()

func get_input():
    velocity = Vector2()
    if Input.is_action_pressed('right'):
        velocity.x += 1
    if Input.is_action_pressed('left'):
        velocity.x -= 1
    if Input.is_action_pressed('down'):
        velocity.y += 1
    if Input.is_action_pressed('up'):
        velocity.y -= 1
    velocity = velocity.normalized() * speed

func _physics_process(delta):
    get_input()
    velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int Speed = 200;

    Vector2 velocity = new Vector2();

    public void GetInput()
    {
        velocity = new Vector2();

        if (Input.IsActionPressed("right"))
            velocity.x += 1;

        if (Input.IsActionPressed("left"))
            velocity.x -= 1;

        if (Input.IsActionPressed("down"))
            velocity.y += 1;

        if (Input.IsActionPressed("up"))
            velocity.y -= 1;

        velocity = velocity.Normalized() * Speed;
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        velocity = MoveAndSlide(velocity);
    }
}

Dans la fonction ``get_input () ``, nous vérifions les quatre événements clés et nous les additionnons pour obtenir le vecteur vitesse. Cela a l’avantage de permettre à deux touches opposées de s’annuler mutuellement, mais entraîne également une accélération du mouvement en diagonale du fait de l’addition des deux directions.

Nous pouvons éviter cela si nous normalisons la vélocité, ce qui signifie que nous définissons sa longueur à 1, et que nous multiplions par la vitesse désirée.

Astuce

Si vous n’avez jamais utilisé de calcul vectoriel auparavant ou avez besoin d’une remise à niveau, vous pouvez voir une explication de l’utilisation des vecteurs dans Godot sur Vector math.

Rotation + Mouvement

Ce type de mouvement est parfois appelé « style Asteroids » car il ressemble à la façon dont ce jeu d’arcade classique fonctionnait. Appuyez sur les touches gauche/droite pour faire pivoter le personnage, tandis que haut/bas le déplace vers l’avant ou l’arrière dans toute direction vers laquelle il fait face.

../../_images/movement_rotate1.gif
extends KinematicBody2D

export (int) var speed = 200
export (float) var rotation_speed = 1.5

var velocity = Vector2()
var rotation_dir = 0

func get_input():
    rotation_dir = 0
    velocity = Vector2()
    if Input.is_action_pressed('right'):
        rotation_dir += 1
    if Input.is_action_pressed('left'):
        rotation_dir -= 1
    if Input.is_action_pressed('down'):
        velocity = Vector2(-speed, 0).rotated(rotation)
    if Input.is_action_pressed('up'):
        velocity = Vector2(speed, 0).rotated(rotation)

func _physics_process(delta):
    get_input()
    rotation += rotation_dir * rotation_speed * delta
    velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int Speed = 200;
    [Export] public float RotationSpeed = 1.5f;

    Vector2 velocity = new Vector2();
    int rotationDir = 0;

    public void GetInput()
    {
        rotationDir = 0;
        velocity = new Vector2();

        if (Input.IsActionPressed("right"))
            rotationDir += 1;

        if (Input.IsActionPressed("left"))
            rotationDir -= 1;

        if (Input.IsActionPressed("down"))
            velocity = new Vector2(-Speed, 0).Rotated(Rotation);

        if (Input.IsActionPressed("up"))
            velocity = new Vector2(Speed, 0).Rotated(Rotation);

        velocity = velocity.Normalized() * Speed;
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        Rotation += rotationDir * RotationSpeed * delta;
        velocity = MoveAndSlide(velocity);
    }
}

Ici, nous avons ajouté deux nouvelles variables pour suivre notre direction et notre vitesse de rotation. A nouveau, appuyer sur les deux touches en même temps les annulera et ne provoquera aucune rotation. La rotation est appliquée directement à la propriété rotation du corps.

Pour définir la vitesse, nous utilisons la méthode Vector2.rotated(), de sorte qu’elle pointe dans la même direction que le corps. rotated()`` est une fonction vectorielle utile que vous pouvez utiliser dans de nombreuses circonstances où vous auriez autrement besoin d’appliquer des fonctions trigonométriques.

Rotation + Mouvement (souris)

Ce style de mouvement est une variation du précédent. Cette fois, la direction est définie par la position de la souris au lieu du clavier. Le personnage « regardera » toujours vers le pointeur de la souris. Les entrées avant/arrière restent toutefois les mêmes.

../../_images/movement_rotate2.gif
extends KinematicBody2D

export (int) var speed = 200

var velocity = Vector2()

func get_input():
    look_at(get_global_mouse_position())
    velocity = Vector2()
    if Input.is_action_pressed('down'):
        velocity = Vector2(-speed, 0).rotated(rotation)
    if Input.is_action_pressed('up'):
        velocity = Vector2(speed, 0).rotated(rotation)

func _physics_process(delta):
    get_input()
    velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int Speed = 200;

    Vector2 velocity = new Vector2();

    public void GetInput()
    {
        LookAt(GetGlobalMousePosition());
        velocity = new Vector2();

        if (Input.IsActionPressed("down"))
            velocity = new Vector2(-Speed, 0).Rotated(Rotation);

        if (Input.IsActionPressed("up"))
            velocity = new Vector2(Speed, 0).Rotated(Rotation);

        velocity = velocity.Normalized() * Speed;
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        velocity = MoveAndSlide(velocity);
    }
}

Nous utilisons ici la méthode Node2D look_at() pour diriger le joueur vers une position donnée. Sans cette fonction, vous pourriez obtenir le même effet en définissant l’angle comme suit :

rotation = get_global_mouse_position().angle_to_point(position)
var rotation = GetGlobalMousePosition().AngleToPoint(Position);

Cliquer-et-déplacer

Ce dernier exemple utilise uniquement la souris pour contrôler le personnage. En cliquant sur l’écran, le joueur se déplacera à l’emplacement cible.

../../_images/movement_click.gif
extends KinematicBody2D

export (int) var speed = 200

var target = Vector2()
var velocity = Vector2()

func _input(event):
    if event.is_action_pressed('click'):
        target = get_global_mouse_position()

func _physics_process(delta):
    velocity = (target - position).normalized() * speed
    # rotation = velocity.angle()
    if (target - position).length() > 5:
        velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int Speed = 200;

    Vector2 target = new Vector2();
    Vector2 velocity = new Vector2();

    public override void _Input(InputEvent @event)
    {
        if (@event.IsActionPressed("click"))
        {
            target = GetGlobalMousePosition();
        }
    }

    public override void _PhysicsProcess(float delta)
    {
        velocity = (target - Position).Normalized() * Speed;
        // Rotation = velocity.Angle();
        if ((target - Position).Length() > 5)
        {
            velocity = MoveAndSlide(velocity);
        }
    }
}

Notez la vérification length() que nous faisons avant le mouvement. Sans ce test, le corps « gigoterait » lorsqu’il atteindrait la position cible, car il se déplace légèrement au-delà de la position et essaie de reculer, se déplace trop loin et répète ce schéma encore et encore.

Décommenter la ligne rotation amènera le corps à pointer dans la direction de son mouvement si vous préférez.

Astuce

Cette technique peut également être utilisée comme base du personnage « suivant ». La position cible peut être celle de n’importe quel objet vers lequel vous voulez déplacer.

Résumé

Vous pouvez trouver ces exemples de code utiles comme points de départ pour vos propres projets. N’hésitez pas à les utiliser et à les expérimenter pour voir ce que vous pouvez faire.

Vous pouvez télécharger cet exemple de projet ici: 2D_movement_demo.zip