Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Caractère cinématique (2D)

Introduction

Oui, le nom semble étrange. "Personnage cinématique". Qu'est-ce que c'est que ça ? La raison pour ce nom est que, lorsque les moteurs physiques sont apparus, ils étaient appelés moteurs "dynamiques" (parce qu'ils s'occupaient principalement des réactions de collisions). Beaucoup de tentatives ont été faites pour créer un contrôleur de personnage qui utilise les moteurs dynamiques, mais ce n'était pas aussi facile que ça en avait l'air. Godot a l'une des meilleures implémentations de contrôleur de personnage dynamique que vous pouvez trouver (comme vous pouvez le voir dans la démo de jeu de plateforme 2d), mais l'utiliser demande un niveau de compétence considérable ainsi qu'une bonne connaissance des moteurs physiques (ou beaucoup de patience avec des essais et des erreurs).

Certains moteurs physiques, comme Havok, semblent considérer les contrôleurs de personnages dynamiques comme la meilleure option, alors que d'autres (PhysX) promeuvent plutôt les contrôleurs cinématiques.

Et donc, quelle est la différence ? :

  • Un contrôleur de personnage dynamique utilise un corps rigide avec un tenseur d'inertie infini. En fait, cela veut simplement dire que le corps rigide ne peut pas tourner. Les moteurs physiques laissent toujours les objets entrer en collision, puis résolvent leurs collisions tous ensemble. Cela fait que le contrôleur de personnage dynamique peut interagir avec d'autres objets physiques sans problème, comme on peut le voir dans la démo du jeu de plateforme. Cependant, ces interactions ne sont pas toujours prévisibles. Les collisions peuvent aussi prendre plus qu'une trame pour être résolues, donc quelques collisions peuvent sembler être un petit peu déplacées. Ces problèmes peuvent être résolus, mais demandent un certain niveau de compétence.

  • Un contrôleur de personnage cinématique est toujours considéré comme commençant dans un état sans collision, et va toujours aller vers un état sans collision. S'il commence dans un état de collision, il essaiera de se libérer tout seul comme les corps rigides le font, mais c'est une exception, pas la règle. Cela fait que leur contrôle et leur mouvement sont beaucoup plus prévisibles et plus faciles à programmer. Par contre, l'inconvénient est qu'ils ne peuvent pas interagir directement avec les autres objets physiques, à moins que cela ne soit fait à la main dans le code.

This short tutorial focuses on the kinematic character controller. It uses the old-school way of handling collisions, which is not necessarily simpler under the hood, but well hidden and presented as an API.

Processus physique

To manage the logic of a kinematic body or character, it is always advised to use physics process, because it's called before physics step and its execution is in sync with physics server, also it is called the same amount of times per second, always. This makes physics and motion calculation work in a more predictable way than using regular process, which might have spikes or lose precision if the frame rate is too high or too low.

extends CharacterBody2D

func _physics_process(delta):
    pass

Configuration de la scène

Pour avoir quelque chose à tester, voici la scène (tirée du tutoriel tilemap) : kinematic_character_2d_starter.zip. Nous allons créer une nouvelle scène pour le personnage. Utilisez le sprite de robot et créez une scène comme celle-ci :

../../_images/kbscene.webp

Vous remarquerez qu'il y a une icône d'avertissement à côté de notre nœud CollisionShape2D ; c'est parce que nous n'avons pas défini de forme pour lui. Créez un nouveau CircleShape2D dans la propriété de forme de CollisionShape2D. Cliquez sur <CircleShape2D> pour accéder aux options correspondantes, et réglez le rayon sur 30 :

../../_images/kbradius.webp

Note : Comme mentionné précédemment dans le tutoriel sur la physique, le moteur physique ne peut pas gérer l'échelle sur la plupart des types de formes (seuls les polygones de collision, les plans et les segments fonctionnent), donc changez toujours les paramètres (comme le rayon) de la forme au lieu de l'échelle. Il en va de même pour les corps cinématiques/rigides/statiques eux-mêmes, car leur échelle affecte l'échelle des formes.

Maintenant, créez un script pour le personnage, celui utilisé comme exemple ci-dessus devrait servir de base.

Enfin, instanciez cette scène personnage dans la tilemap, et faites de la tilemap la scène principale, de sorte qu'elle s'exécute lorsque vous appuyez sur play.

../../_images/kbinstance.webp

Déplacer le personnage cinématique

Retournez à la scène du personnage, et ouvrez le script, la magie commence maintenant ! Le corps cinématique ne fera rien par défaut, mais il a une fonction utile appelée CharacterBody2D.move_and_collide(). Cette fonction prend un Vector2 comme argument, et essaie d'appliquer ce mouvement au corps cinématique. Si une collision se produit, elle s'arrête au moment même de la collision.

Déplaçons notre sprite vers le bas jusqu'à ce qu'il touche le sol :

extends CharacterBody2D

func _physics_process(delta):
    move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame

Il en résulte que le personnage bouge, mais s'arrête pile lorsqu'il touche le sol. Plutôt cool, hein ?

La prochaine étape sera d'ajouter de la gravité au mélange, de cette façon il se comporte un peu plus comme un personnage de jeu ordinaire :

extends CharacterBody2D

const GRAVITY = 200.0

func _physics_process(delta):
    velocity.y += delta * GRAVITY

    var motion = velocity * delta
    move_and_collide(motion)

Maintenant, que le personnage tombe en douceur. Faisons-le marcher sur les côtés, à gauche et à droite en touchant les touches directionnelles. Rappelez-vous que les valeurs utilisées (pour la vitesse au moins) sont en pixels/seconde.

This adds basic support for walking when pressing left and right:

extends CharacterBody2D

const GRAVITY = 200.0
const WALK_SPEED = 200

func _physics_process(delta):
    velocity.y += delta * GRAVITY

    if Input.is_action_pressed("ui_left"):
        velocity.x = -WALK_SPEED
    elif Input.is_action_pressed("ui_right"):
        velocity.x =  WALK_SPEED
    else:
        velocity.x = 0

    # "move_and_slide" already takes delta time into account.
    move_and_slide()

Et essayez-le.

C'est un bon point de départ pour un plateformer. Une démo plus complète se trouve dans le zip de démonstration distribué avec le moteur, ou sur le site https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character.