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.
Checking the stable version of the documentation...
Introduction à la physique
Dans le développement d'un jeu, vous avez souvent besoin de savoir quand deux objets du jeu se croisent ou entrent en contact. C'est ce qu'on appelle la détection des collisions. Lorsqu'une collision est détectée, vous voulez généralement que quelque chose se produise. C'est ce qu'on appelle la réponse à la collision.
Godot offre un certain nombre d'objets de collision en 2D et 3D pour fournir à la fois la détection et la réponse aux collisions. Essayer de décider lequel utiliser pour votre projet peut prêter à confusion. Vous pouvez éviter les problèmes et simplifier le développement si vous comprenez comment chacun fonctionne et quels sont ses avantages et ses inconvénients.
Dans ce guide, vous apprendrez :
Les quatre types d'objets de collision de Godot
Comment chaque objet de collision fonctionne
Quand et pourquoi choisir un type plutôt qu'un autre
Note
Les exemples de ce document utilisent des objets 2D. Chaque objet physique 2D et chaque forme de collision a un équivalent direct en 3D et dans la plupart des cas, ils fonctionnent de la même manière.
Avertissement
Physics in Godot, regardless of physics engine, is not deterministic, the nature of physics engine determinism is very complex and has to do with many factors, this means physics is not guaranteed to run the same way for seemingly identical situations.
Objets de collisions
Godot offers four kinds of collision objects which all extend CollisionObject2D. The last three listed below are physics bodies and additionally extend PhysicsBody2D.
- Area2D
Les nœuds
Area2Dfournissent la détection et l'influence. Ils peuvent détecter quand les objets se chevauchent et peuvent émettre des signaux quand des corps entrent ou sortent. UnArea2Dpeut aussi être utilisé pour outrepasser les propriétés physiques, telles que la gravité ou l'amortissement, dans une zone définie.
- StaticBody2D
Un corps statique est un corps qui n'est pas déplacé par le moteur physique. Il participe à la détection des collisions, mais ne bouge pas en réponse à la collision. Ils sont le plus souvent utilisés pour des objets qui font partie de l'environnement ou qui n'ont pas besoin d'avoir un comportement dynamique.
- RigidBody2D
C'est le nœud qui implémente la physique 2D simulée. Vous ne contrôlez pas directement un
RigidBody2D, mais vous lui appliquez des forces (gravité, impulsions, etc.) et le moteur physique calcule le mouvement résultant. En savoir plus sur l'utilisation des RigidBody.
- CharacterBody2D
Un corps qui fournit la détection de collision, mais n'a pas de physique. Toute réponse au mouvement ou à la collision doit être implémentée dans le code.
Physics material
Static bodies and rigid bodies can be configured to use a PhysicsMaterial. This allows adjusting the friction and bounce of an object, and set if it's absorbent and/or rough.
Formes de collisions
Un corps physique peut contenir n'importe quel nombre d'objets Shape2D comme enfants. Ces formes sont utilisées pour définir les limites de collision de l'objet et pour détecter le contact avec d'autres objets.
Note
Afin de détecter les collisions, au moins un Shape2D doit être assigné à l'objet.
La façon la plus courante d'assigner une forme est d'ajouter un CollisionShape2D ou CollisionPolygon2D comme un enfant de l'objet. Ces nœuds vous permettent de dessiner la forme directement dans l'espace de travail de l'éditeur.
Important
Faites attention à ne jamais mettre à l'échelle vos formes de collision dans l'éditeur. La propriété "Scale" de l'Inspecteur doit rester à (1, 1). Lorsque vous changez la taille de la forme de la collision, vous devez toujours utiliser les poignées de taille, pas les poignées d'échelle de Node2D. La mise à l'échelle d'une forme peut entraîner un comportement de collision inattendu.
Rappel des processus physiques
The physics engine runs at a fixed rate (a default of 60 iterations per second). This rate is typically different from the frame rate which fluctuates based on what is rendered and available resources.
It is important that all physics related code runs at this fixed rate. Therefore Godot differentiates between physics and idle processing. Code that runs each frame is called idle processing and code that runs on each physics tick is called physics processing. Godot provides two different callbacks, one for each of those processing rates.
The physics callback, Node._physics_process(),
is called before each physics step. Any code that needs to access a body's properties should
be run in here. This method will be passed a delta
parameter, which is a floating-point number equal to the time passed in
seconds since the last step. When using the default 60 Hz physics update rate,
it will typically be equal to 0.01666... (but not always, see below).
Note
Il est recommandé de toujours utiliser le paramètre delta lorsque cela est pertinent dans vos calculs physiques, afin que le jeu se comporte correctement si vous modifiez le taux de mise à jour physique ou si l'appareil du joueur ne peut pas suivre.
Couches et masques de collisions
One of the most powerful, but frequently misunderstood, collision features
is the collision layer system. This system allows you to build up complex
interactions between a variety of objects. The key concepts are layers
and masks. Each CollisionObject2D has 32 different physics layers
it can interact with.
Examinons chacune des propriétés à tour de rôle :
- collision_layer
Elle décrit les calques dans lesquels l'objet apparaît in. Par défaut, tous les corps sont sur la couche
1.
- collision_mask
Cette section décrit les couches que le corps va scanner pour détecter les collisions. Si un objet n'est pas dans l'une des couches du masque, le corps l'ignore. Par défaut, tous les corps scannent la couche
1.
Ces propriétés peuvent être configurées via le code ou en les modifiant dans l'inspecteur.
Keeping track of what you're using each layer for can be difficult, so you may find it useful to assign names to the layers you're using. Names can be assigned in Project Settings > Layer Names > 2D Physics.
Exemple dans l'interface graphique
Vous avez quatre types de nœuds dans votre jeu : Murs, joueur, ennemi et pièce de monnaie. Le joueur et l'ennemi doivent entrer en collision avec les murs. Le nœud Player doit détecter les collisions avec l'ennemi et la pièce, mais l'ennemi et la pièce doivent s'ignorer mutuellement.
Commencez par nommer les couches 1-4 "murs", "joueur", "ennemis" et "pièces" et placez chaque type de nœud dans sa couche respective en utilisant la propriété "Layer". Définissez ensuite la propriété "Mask" de chaque nœud en sélectionnant les calques avec lesquels il doit interagir. Par exemple, les paramètres du joueur ressemblent à ceci :
Exemple en code
In function calls, layers are specified as a bitmask. Where a function enables
all layers by default, the layer mask will be given as 0xffffffff. Your code
can use binary, hexadecimal, or decimal notation for layer masks, depending
on your preference.
The code equivalent of the above example where layers 1, 3 and 4 were enabled would be as follows:
# Example: Setting mask value for enabling layers 1, 3 and 4
# Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0.
# Note: Layer 32 is the first bit, layer 1 is the last. The mask for layers 4, 3 and 1 is therefore:
0b00000000_00000000_00000000_00001101
# (This can be shortened to 0b1101)
# Hexadecimal equivalent (1101 binary converted to hexadecimal).
0x000d
# (This value can be shortened to 0xd.)
# Decimal - Add the results of 2 to the power of (layer to be enabled - 1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
#
# We can use the `<<` operator to shift the bit to the left by the layer number we want to enable.
# This is a faster way to multiply by powers of 2 than `pow()`.
# Additionally, we use the `|` (binary OR) operator to combine the results of each layer.
# This ensures we don't add the same layer multiple times, which would behave incorrectly.
(1 << 1 - 1) | (1 << 3 - 1) | (1 << 4 - 1)
# The above can alternatively be written as:
# pow(2, 1 - 1) + pow(2, 3 - 1) + pow(2, 4 - 1)
You can also set bits independently by calling set_collision_layer_value(layer_number, value)
or set_collision_mask_value(layer_number, value) on any given CollisionObject2D as follows:
# Example: Setting mask value to enable layers 1, 3, and 4.
var collider: CollisionObject2D = $CollisionObject2D # Any given collider.
collider.set_collision_mask_value(1, true)
collider.set_collision_mask_value(3, true)
collider.set_collision_mask_value(4, true)
Export annotations can be used to export bitmasks in the editor with a user-friendly GUI:
@export_flags_2d_physics var layers_2d_physics
Additional export annotations are available for render and navigation layers, in both 2D and 3D. See Exportation de bit flags.
Area2D
Les nœuds de zone assurent la détection et l'influence. Ils peuvent détecter quand les objets se chevauchent et émettre des signaux quand les corps entrent ou sortent. Les zones peuvent également être utilisées pour outrepasser les propriétés physiques, telles que la gravité ou l'amortissement, dans une zone définie.
Il y a trois utilisations principales pour Area2D :
Remplacer les paramètres physiques primordiaux (comme la gravité) dans une région donnée.
Détecter quand d'autres corps entrent ou sortent d'une région ou quels corps se trouvent actuellement dans une région.
Vérification des chevauchements avec d'autres zones.
Par défaut, les zones reçoivent également les entrées de la souris et de l'écran tactile.
StaticBody2D
Un corps statique est un corps qui n'est pas déplacé par le moteur physique. Il participe à la détection des collisions, mais ne bouge pas en réponse à la collision. Cependant, il peut donner du mouvement ou de la rotation à un corps en collision comme si il se déplaçait, en utilisant ses propriétés constant_linear_velocity et constant_angular_velocity.
Les nœuds StaticBody2D sont le plus souvent utilisés pour des objets qui font partie de l'environnement ou qui n'ont pas besoin d'avoir un comportement dynamique.
Exemples d'utilisation de StaticBody2D :
Plates-formes (y compris les plates-formes mobiles)
Convoyeur à courroies
Murs et autres obstacles
RigidBody2D
C'est le nœud qui implémente la physique 2D simulée. Vous ne contrôlez pas un RigidBody2D directement. Au lieu de cela, vous lui appliquez des forces et le moteur physique calcule le mouvement résultant, y compris les collisions avec d'autres corps, et les réactions de collision, telles que le rebondissement, la rotation, etc.
Vous pouvez modifier le comportement d'un corps rigide via des propriétés telles que "Mass", "Friction" ou "Bounce", qui peuvent être définies dans l'inspecteur.
The body's behavior is also affected by the world's properties, as set in Project Settings > Physics, or by entering an Area2D that is overriding the global physics properties.
Quand un corps rigide est au repos et n'a pas bougé depuis un moment, il s'endort. Un corps endormi agit comme un corps statique, et ses forces ne sont pas calculées par le moteur physique. Le corps se réveille lorsque des forces sont appliquées, soit par une collision, soit par un code.
Utiliser RigidBody2D
Un des avantages de l'utilisation d'un corps rigide est qu'il est possible d'avoir beaucoup de comportement "gratuitement" sans écrire de code. Par exemple, si vous créez un jeu de type "Angry Birds" avec des blocs tombants, vous n'aurez qu'à créer RigidBody2Ds et ajuster leurs propriétés. L'empilement, la chute et le rebondissement seraient automatiquement calculés par le moteur physique.
Cependant, si vous souhaitez avoir un certain contrôle sur le corps, vous devriez faire attention - modifier la position, la linear_velocity ou d'autres propriétés physiques d'un corps rigide peut entraîner un comportement inattendu. Si vous avez besoin de modifier l'une des propriétés liées à la physique, vous devez utiliser le rappel _integrate_forces() au lieu de _physics_process(). Dans ce rappel, vous avez accès au PhysicsDirectBodyState2D, qui permet de modifier en toute sécurité les propriétés et les synchroniser avec le moteur physique.
Par exemple, voici le code d'un vaisseau spatial de type "Astéroïdes" :
extends RigidBody2D
var thrust = Vector2(0, -250)
var torque = 20000
func _integrate_forces(state):
if Input.is_action_pressed("ui_up"):
state.apply_force(thrust.rotated(rotation))
else:
state.apply_force(Vector2())
var rotation_direction = 0
if Input.is_action_pressed("ui_right"):
rotation_direction += 1
if Input.is_action_pressed("ui_left"):
rotation_direction -= 1
state.apply_torque(rotation_direction * torque)
using Godot;
public partial class Spaceship : RigidBody2D
{
private Vector2 _thrust = new Vector2(0, -250);
private float _torque = 20000;
public override void _IntegrateForces(PhysicsDirectBodyState2D state)
{
if (Input.IsActionPressed("ui_up"))
{
state.ApplyForce(_thrust.Rotated(Rotation));
}
else
{
state.ApplyForce(new Vector2());
}
var rotationDir = 0;
if (Input.IsActionPressed("ui_right"))
{
rotationDir += 1;
}
if (Input.IsActionPressed("ui_left"))
{
rotationDir -= 1;
}
state.ApplyTorque(rotationDir * _torque);
}
}
Notez que nous ne réglons pas directement les propriétés linear_velocity ou angular_velocity, mais appliquons plutôt des forces (thrust et torque) au corps et laissons le moteur physique calculer le mouvement résultant.
Note
Quand un corps rigide s'endort, la fonction _integrate_forces() ne sera pas appelée. Pour outrepasser ce comportement, vous devrez garder le corps éveillé en créant une collision, en lui appliquant une force ou en désactivant la propriété can_sleep. Sachez que cela peut avoir un effet négatif sur la performance.
Rapport de contact
Par défaut, les corps rigides ne gardent pas la trace des contacts, car cela peut nécessiter une énorme quantité de mémoire si de nombreux corps sont dans la scène. Pour activer la déclaration des contacts, définissez la propriété max_contacts_reported sur une valeur non nulle. Les contacts peuvent alors être obtenus via PhysicsDirectBodyState2D.get_contact_count() et les fonctions associées.
La surveillance des contacts par signaux peut être activée via la propriété contact_monitor. Voir RigidBody2D pour la liste des signaux disponibles.
CharacterBody2D
Les corps CharacterBody2D détectent les collisions avec d'autres corps, mais ne sont pas affectés par des propriétés physiques comme la gravité ou la friction. Ils doivent plutôt être contrôlés par l'utilisateur à l'aide de code. Le moteur physique ne déplacera pas un corps de personnage.
When moving a character body, you should not set its position directly.
Instead, you use the move_and_collide() or move_and_slide() methods.
These methods move the body along a given vector, and it will instantly stop
if a collision is detected with another body. After the body has collided,
any collision response must be coded manually.
Character collision response
Après une collision, vous pouvez vouloir que le corps rebondisse, glisse le long d'un mur ou modifie les propriétés de l'objet touché. La façon dont vous réagissez à la collision dépend de la méthode que vous avez utilisée pour déplacer le CharacterBody2D.
move_and_collide
Lorsque vous utilisez move_and_collide(), la fonction retourne un objet KinematicCollision2D, qui contient des informations sur la collision et le corps en collision. Vous pouvez utiliser ces informations pour déterminer la réponse.
Par exemple, si vous voulez trouver le point dans l'espace où la collision s'est produite :
extends PhysicsBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
var collision_point = collision_info.get_position()
using Godot;
public partial class Body : PhysicsBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(double delta)
{
var collisionInfo = MoveAndCollide(_velocity * (float)delta);
if (collisionInfo != null)
{
var collisionPoint = collisionInfo.GetPosition();
}
}
}
Ou pour rebondir sur l'objet en collision :
extends PhysicsBody2D
var velocity = Vector2(250, 250)
func _physics_process(delta):
var collision_info = move_and_collide(velocity * delta)
if collision_info:
velocity = velocity.bounce(collision_info.get_normal())
using Godot;
public partial class Body : PhysicsBody2D
{
private Vector2 _velocity = new Vector2(250, 250);
public override void _PhysicsProcess(double delta)
{
var collisionInfo = MoveAndCollide(_velocity * (float)delta);
if (collisionInfo != null)
{
_velocity = _velocity.Bounce(collisionInfo.GetNormal());
}
}
}
move_and_slide
Le glissement est une réaction de collision courante ; imaginez un joueur se déplaçant le long des murs dans un jeu descendant ou courant le long des pentes dans un jeu de plates-formes. Bien qu'il soit possible de coder cette réponse vous-même après avoir utilisé move_and_collide(), move_and_slide() offre un moyen pratique d'implémenter un mouvement glissant sans écrire beaucoup de code.
Avertissement
move_and_slide() automatically includes the timestep in its
calculation, so you should not multiply the velocity vector
by delta. This does not apply to gravity as it is an
acceleration and is time dependent, and needs to be scaled by
delta.
Par exemple, utilisez le code suivant pour créer un personnage capable de marcher sur le sol (y compris sur les pentes) et de sauter debout sur le sol :
extends CharacterBody2D
var run_speed = 350
var jump_speed = -1000
var gravity = 2500
func get_input():
velocity.x = 0
var right = Input.is_action_pressed('ui_right')
var left = Input.is_action_pressed('ui_left')
var jump = Input.is_action_just_pressed('ui_select')
if is_on_floor() and jump:
velocity.y = jump_speed
if right:
velocity.x += run_speed
if left:
velocity.x -= run_speed
func _physics_process(delta):
velocity.y += gravity * delta
get_input()
move_and_slide()
using Godot;
public partial class Body : CharacterBody2D
{
private float _runSpeed = 350;
private float _jumpSpeed = -1000;
private float _gravity = 2500;
private void GetInput()
{
var velocity = Velocity;
velocity.X = 0;
var right = Input.IsActionPressed("ui_right");
var left = Input.IsActionPressed("ui_left");
var jump = Input.IsActionPressed("ui_select");
if (IsOnFloor() && jump)
{
velocity.Y = _jumpSpeed;
}
if (right)
{
velocity.X += _runSpeed;
}
if (left)
{
velocity.X -= _runSpeed;
}
Velocity = velocity;
}
public override void _PhysicsProcess(double delta)
{
var velocity = Velocity;
velocity.Y += _gravity * (float)delta;
Velocity = velocity;
GetInput();
MoveAndSlide();
}
}
Voir Caractère cinématique (2D) pour plus de détails sur l'utilisation de move_and_slide(), qui inclut un projet de démonstration avec le code détaillé.