1. Introduction
Godot 4 a introduit des changements majeurs dans le déplacement des personnages. Les anciens nœuds KinematicBody2D et KinematicBody3D ont été renommés en CharacterBody2D et CharacterBody3D, et l'API move_and_slide() a été entièrement repensée. Que vous migriez depuis Godot 3 ou que vous démarriez de zéro, ce guide couvre tout ce que vous devez savoir.
CharacterBody2D et CharacterBody3D sont des corps physiques conçus pour les personnages contrôlés par du code. Contrairement à RigidBody, ils ne réagissent pas automatiquement aux forces — vous contrôlez entièrement leur déplacement par script. Cela les rend idéaux pour les personnages joueurs, les PNJ et tout ce qui nécessite un déplacement précis et déterministe.
2. Ce qui a changé depuis Godot 3
Voici les changements les plus importants entre Godot 3 et Godot 4 pour le déplacement des personnages :
-
KinematicBody2Dest désormaisCharacterBody2D -
KinematicBody3Dest désormaisCharacterBody3D -
velocityest désormais une propriété intégrée — vous ne la passez plus àmove_and_slide() -
move_and_slide()ne prend aucun argument — il lit directement la propriétévelocity -
move_and_slide_with_snap()a été supprimé — utilisez plutôt la propriétéfloor_snap_length -
is_on_floor(),is_on_wall(),is_on_ceiling()fonctionnent toujours de la même manière -
get_gravity()a été ajouté dans Godot 4.4 pour lire la gravité par défaut du projet sous forme de vecteur — très pratique
Dans Godot 3, move_and_slide() renvoyait la vélocité résultante. Dans Godot 4, il renvoie un bool (indiquant si une collision s'est produite), et la vélocité est mise à jour sur place via la propriété velocity.
3. Contrôleur de plateforme 2D de base
Voici le contrôleur de plateforme à défilement horizontal standard. Le personnage peut se déplacer à gauche/droite et sauter lorsqu'il est au sol. C'est le modèle que Godot génère lorsque vous créez un nouveau script CharacterBody2D.
extends CharacterBody2D
const SPEED = 300.0
const JUMP_VELOCITY = -400.0
func _physics_process(delta: float) -> void:
# Apply gravity when not on floor
if not is_on_floor():
velocity += get_gravity() * delta
# Jump when on floor and jump pressed
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Horizontal movement
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
Décomposition ligne par ligne
-
extends CharacterBody2D— Le script hérite de CharacterBody2D, ce qui nous donne accès àvelocity,move_and_slide(),is_on_floor(), etc. -
SPEED = 300.0— Vitesse de déplacement horizontal en pixels par seconde. -
JUMP_VELOCITY = -400.0— Négatif parce que l'axe Y pointe vers le bas en 2D. Une valeur négative déplace le personnage vers le haut. -
get_gravity() * delta— Applique la gravité du projet à chaque frame.get_gravity()renvoie unVector2pointant vers le bas (par exemple,Vector2(0, 980)). La multiplication pardeltale rend indépendant de la fréquence d'images. -
Input.get_axis("ui_left", "ui_right")— Renvoie un float entre -1.0 et 1.0 selon la touche de direction pressée. Prend en charge l'entrée analogique. -
move_toward(velocity.x, 0, SPEED)— Décélère en douceur jusqu'à zéro lorsqu'aucune entrée n'est pressée. Le troisième argument est le changement maximal par appel. -
move_and_slide()— Déplace le corps en utilisant lavelocityactuelle, gère les collisions, glisse le long des surfaces et met à jourvelocityautomatiquement.
Votre CharacterBody2D a besoin d'au moins un nœud enfant CollisionShape2D avec une forme assignée (par exemple, RectangleShape2D ou CapsuleShape2D). Sans cela, move_and_slide() ne détectera aucune collision.
4. Contrôleur vue de dessus 2D de base
Pour les jeux en vue de dessus (RPG, twin-stick shooters, etc.), vous vous déplacez dans les quatre directions sans gravité. Le code est plus simple qu'un contrôleur de plateforme.
extends CharacterBody2D
const SPEED = 200.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input_direction * SPEED
move_and_slide()
Comment ça fonctionne
-
Input.get_vector()renvoie unVector2normalisé à partir de quatre actions d'entrée. Cela gère correctement le déplacement en diagonale — la longueur du vecteur est toujours 1.0 ou 0.0, donc le déplacement en diagonale n'est pas plus rapide que le déplacement cardinal. -
Aucune gravité n'est appliquée puisqu'il s'agit d'une vue de dessus. Le personnage s'arrête immédiatement lorsqu'aucune entrée n'est donnée, car nous définissons
velocitydirectement (aucune inertie).
Ajouter de l'accélération et de la friction
Pour une sensation plus fluide avec accélération et décélération :
extends CharacterBody2D
const SPEED = 200.0
const ACCELERATION = 1200.0
const FRICTION = 1000.0
func _physics_process(delta: float) -> void:
var input_direction := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
if input_direction != Vector2.ZERO:
velocity = velocity.move_toward(input_direction * SPEED, ACCELERATION * delta)
else:
velocity = velocity.move_toward(Vector2.ZERO, FRICTION * delta)
move_and_slide()
5. Contrôleur 3D à la troisième personne
La version 3D suit le même modèle. La principale différence est de travailler avec Vector3 et d'utiliser transform.basis pour convertir une entrée 2D en déplacement dans l'espace monde 3D.
extends CharacterBody3D
const SPEED = 5.0
const JUMP_VELOCITY = 4.5
func _physics_process(delta: float) -> void:
# Apply gravity
if not is_on_floor():
velocity += get_gravity() * delta
# Jump
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get input and convert to 3D direction
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
Principales différences avec la 2D
-
Unités : En 3D, 1 unité = 1 mètre (convention). Ainsi,
SPEED = 5.0signifie 5 mètres par seconde — des nombres bien plus petits que les valeurs en pixels de la 2D. -
La vélocité de saut est positive : En 3D, l'axe Y pointe vers le haut (contrairement à la 2D), donc
JUMP_VELOCITY = 4.5est positif. -
Transformation de direction :
transform.basis * Vector3(input_dir.x, 0, input_dir.y)convertit l'entrée 2D en une direction 3D relative à l'orientation du personnage. Le Y de l'entrée est mappé sur l'axe Z (avant/arrière en 3D). -
Seuls X et Z sont contrôlés : Nous définissons
velocity.xetvelocity.zséparément, en laissantvelocity.ypour la gravité et le saut.
La méthode get_gravity() a été ajoutée dans Godot 4.4. Pour les versions plus anciennes, utilisez Vector2(0, ProjectSettings.get_setting("physics/2d/default_gravity")) pour la 2D, ou Vector3(0, -ProjectSettings.get_setting("physics/3d/default_gravity"), 0) pour la 3D.
6. Référence des propriétés principales
CharacterBody2D et CharacterBody3D partagent tous deux ces propriétés importantes. Ajustez-les dans l'Inspecteur ou par code.
| Propriété | Type | Par défaut | Description |
|---|---|---|---|
velocity |
Vector2 / Vector3 | Vector2.ZERO |
La vélocité du personnage. Définissez-la avant d'appeler move_and_slide(). Mise à jour après l'appel avec la vélocité résultante.
|
floor_snap_length |
float | 1.0 |
Remplace move_and_slide_with_snap(). Distance d'accrochage du personnage au sol. Réglez sur 0 pour désactiver. Utile pour les pentes et les escaliers.
|
up_direction |
Vector2 / Vector3 | Vector2.UP |
Définit quelle direction est « le haut ». Cela détermine ce qui compte comme sol, mur ou plafond. Par défaut (0, -1) en 2D, (0, 1, 0) en 3D.
|
floor_stop_on_slope |
bool | true |
Lorsque true, le personnage ne glisse pas le long des pentes à l'arrêt. Essentiel pour les jeux de plateforme. |
floor_max_angle |
float | 0.785 (45°) |
Angle de pente maximal praticable en radians. Les surfaces plus raides sont traitées comme des murs. Utilisez deg_to_rad(60) pour définir 60 degrés.
|
max_slides |
int | 6 |
Nombre maximal d'itérations de collision par appel de move_and_slide(). Des valeurs plus élevées sont plus précises mais plus lentes.
|
wall_min_slide_angle |
float | 0.262 (15°) |
Angle minimal pour le glissement sur les murs. Empêche le personnage de se coincer sur des murs presque parallèles. |
platform_on_leave |
PlatformOnLeave | ADD_VELOCITY |
Comportement en quittant une plateforme mobile. ADD_VELOCITY conserve l'élan, ADD_UPWARD_VELOCITY n'ajoute que la composante vers le haut, DO_NOTHING ignore la vélocité de la plateforme.
|
slide_on_ceiling |
bool | true |
Lorsque true, autorise le glissement sur les plafonds. Lorsque false, arrête la vélocité horizontale au contact du plafond. |
7. Détection de collision après move_and_slide()
Après avoir appelé move_and_slide(), vous pouvez inspecter les collisions qui se sont produites. C'est utile pour réagir à des types de colliders spécifiques, jouer des effets sonores à l'atterrissage ou implémenter des mécaniques de saut mural.
func _physics_process(delta: float) -> void:
# ... set velocity here ...
move_and_slide()
# Check all collisions from this frame
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
var collider := collision.get_collider()
print("Collided with: ", collider.name)
print("Normal: ", collision.get_normal())
print("Position: ", collision.get_position())
# Practical example: bounce off enemies
for i in get_slide_collision_count():
var collision := get_slide_collision(i)
if collision.get_collider().is_in_group("enemies"):
velocity = collision.get_normal() * 300
break
Méthodes utiles de KinematicCollision
get_collider()— L'objet qui a été touché.get_normal()— La normale de la surface de collision. Pointe à l'opposé de la surface.get_position()— Le point dans l'espace monde où la collision s'est produite.get_travel()— La distance parcourue par le corps avant la collision.get_remainder()— Le déplacement restant qui n'a pas été appliqué.get_collider_velocity()— La vélocité du collider (utile pour les plateformes mobiles).
8. Aide-mémoire de migration
Référence rapide pour convertir du code Godot 3 en Godot 4 :
| Godot 3 | Godot 4 |
|---|---|
KinematicBody2D |
CharacterBody2D |
KinematicBody3D |
CharacterBody3D |
velocity = move_and_slide(velocity, Vector2.UP) |
velocity = ... |
move_and_slide_with_snap(vel, snap, up) |
floor_snap_length = 4.0 |
var vel = move_and_slide(...)renvoie la vélocité |
move_and_slide()la propriété velocity est mise à jour sur place |
var gravity = ProjectSettings.get("physics/2d/default_gravity") |
get_gravity() (4.4+) |
move_and_slide(..., up_direction, ...) |
up_direction = Vector2.UP (défini comme propriété) |
move_and_slide(..., stop_on_slope, ...) |
floor_stop_on_slope = true (défini comme propriété) |
Lorsque vous ouvrez un projet Godot 3 dans Godot 4, le moteur propose de convertir votre projet. Il renomme les nœuds et tente de mettre à jour les scripts, mais il ne gère pas toujours correctement les appels à move_and_slide(). Vous devrez probablement corriger manuellement votre code de déplacement.
9. Erreurs courantes
Erreur 1 : passer des arguments à move_and_slide()
# ERROR: move_and_slide() takes no arguments in Godot 4
velocity = move_and_slide(velocity, Vector2.UP)
# Set velocity, then call move_and_slide() with no arguments
velocity.x = direction * SPEED
move_and_slide()
Erreur 2 : oublier de définir velocity avant move_and_slide()
func _physics_process(delta: float) -> void:
move_and_slide() # velocity is Vector2.ZERO — nothing happens
Erreur 3 : utiliser _process() au lieu de _physics_process()
move_and_slide() doit être appelé dans _physics_process(), qui s'exécute à une fréquence fixe (60 Hz par défaut). Utiliser _process() lie la physique à la fréquence d'images du rendu, provoquant un comportement incohérent.
Erreur 4 : ne pas configurer de CollisionShape
Un CharacterBody2D/CharacterBody3D sans nœud enfant CollisionShape traversera tout. Godot affiche une icône d'avertissement dans l'éditeur si la forme de collision est absente.
Erreur 5 : écraser velocity.y avec la gravité à chaque frame
# This overwrites any jump velocity!
velocity.y = gravity * delta # should be +=, not =
# Gravity accumulates over time
velocity += get_gravity() * delta
Erreur 6 : appliquer la gravité lorsqu'on est au sol
Encadrez toujours la gravité dans if not is_on_floor(). Sans cette vérification, la gravité continue de s'accumuler tant que le personnage est au sol. Lorsque le personnage marche au-delà d'un rebord, il plongera à une vitesse extrême au lieu de tomber naturellement.