Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
Introdução à física¶
É frequente, no desenvolvimento de jogos, precisar saber quando dois objetos se interceptam ou fazem contato durante o jogo. Isso é conhecido como detecção de colisão. Quando uma colisão é detectada, você tipicamente quer que alguma coisa aconteça. Isso é chamado de resposta à colisão.
Godot oferece uma variedade de objetos de colisão em 2D e 3D para prover tanto detecção de quanto resposta a colisões. Tentar decidir qual usar em seu projeto pode ser confuso. Você pode evitar problemas e simplificar o desenvolvimento se entender como cada um funciona e quais são suas vantagens e desvantagens.
Neste guia, você aprenderá:
Os quatro tipos de objeto de colisão do Godot
Como cada objeto de colisão funciona
Quando e por que escolher um tipo em detrimento de outro
Nota
Os exemplos deste documento usarão objetos 2D. Todo objeto de física e forma de colisão 2D tem seu equivalente direto em 3D e, na maioria dos casos, funcionam exatamente da mesma forma.
Objetos de Colisão¶
Godot offers four kinds of collision objects which all extend CollisionObject2D. The last three listed below are physics bodies and additionally extend PhysicsBody2D.
- Area2D
Os nós
Area2D
fornecem detecção e influência. Eles podem detectar quando os objetos se sobrepõem e podem emitir sinais quando os corpos entram ou saem. UmaArea2D
também pode ser usado para substituir propriedades físicas, como gravidade ou amortecimento, em uma área definida.
- StaticBody2D
Um corpo estático é aquele que não é movido pelo motor de física. Ele participa da detecção de colisão, mas não se move em resposta a uma. É mais comumente usado para objetos que sejam parte do ambiente ou que não necessitam de ter qualquer comportamento dinâmico.
- RigidBody2D
Este é o nó que implementa a física 2D simulada. Você não controla um
RigidBody2D
diretamente, e sim aplica forças nele (gravidade, impulsos, etc.) e o motor de física calcula o movimento resultante. Leia mais sobre o uso de corpos rígidos.
- CharacterBody2D
Um corpo que provê detecção de colisão, mas nenhuma física. Todos os movimentos e respostas a colisões devem ser implementados em código.
Material físico¶
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.
Formas de colisão¶
Um corpo físico por conter qualquer quantidade de objetos Shape2D como filhos. Essas formas são usadas para definir os limites de colisão do objeto e detectar contato com outros.
Nota
Para detectar colisões, pelo menos um Shape2D
deve ser atribuído ao objeto.
A maneira mais comum de atribuir uma forma é adicionar um CollisionShape2D ou CollisionPolygon2D como filho do objeto. Esses nós lhe permitem desenhar o formato diretamente no espaço de trabalho do editor.
Importante
Tenha cuidado para nunca escalar suas formas de colisão no editor. A propriedade "Escala" no Inspetor deve permanecer (1, 1)
. Ao alterar o tamanho da forma da colisão, você deve sempre utilizar os manipuladores de tamanho, e ** não** os manipuladores de escala Node2D
. A escala de uma forma pode resultar em um comportamento de colisão inesperado.
Chamadas de retorno do processamento da física¶
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).
Nota
It's recommended to always use the delta
parameter when relevant in your
physics calculations, so that the game behaves correctly if you change the
physics update rate or if the player's device can't keep up.
Camadas e Máscaras de colisão¶
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.
Vamos olhar cada propriedade por vez:
- collision_layer
Descreve as camadas que onde o objeto aparece. Por padrão, todos os corpos estão na camada
1
.
- collision_mask
Descreve quais camadas o corpo irá varrer em busca de colisões. Se um objeto não estiver em uma camada da máscara, o corpo o ignorará. Por padrão, todos corpos varrem a camada
1
.
Essas propriedades podem ser configurada por código ou por edição no Inspetor.
Manter registro da finalidade que você está dando para cada camada pode ser difícil. Então, pode ser útil atribuir nomes às camadas em uso. Você pode nomeá-las nas Configurações do Projeto -> Nomes das Camadas.
Exemplo de GUI¶
Você tem quatro tipos de nós no seu jogo: Muros, Jogador, Inimigo e Moeda. Tanto o Jogador quanto o Inimigo colidem com Muros. O nó Jogador deveria detectar colisões com Inimigo e Moeda, mas Inimigo e Moeda devem se ignorar.
Comece nomeando as camadas 1 a 4 como "muros", "jogador", "inimigos" e "moedas" e coloque cada tipo de nó em sua camada respectiva usando a propriedade "Camada" (Layer). Então configure a propriedade "Máscara" (Mask) de cada nó selecionando as camadas com que ele deveria interagir. Por exemplo, as configurações do Jogador (Player) deveriam se parecer com isto:
Exemplo de código¶
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
pow(2, 1-1) + pow(2, 3-1) + pow(2, 4-1)
Area2D¶
Os nós de área fornecem detecção e influência. Eles podem detectar quando os objetos se sobrepõem e emitir sinais quando os corpos entram ou saem. As áreas também podem ser usadas para anular propriedades físicas, tais como gravidade ou amortecimento, em uma área definida.
Existem três usos principais para Area2D:
Substituição dos parâmetros físicos (como gravidade) em uma dada região.
Detectar quando outros corpos entram em ou saem de uma região ou quais estão atualmente em um região.
Verificar se outras áreas se sobrepõem.
Por padrão, áreas também recebem entradas de mouse e tela de toque.
StaticBody2D¶
Um corpo estático é aquele que não é movido pelo motor de física. Ele participa da detecção de colisão, mas não se move em resposta à ela. Entretanto, ele pode transmitir movimento e rotação a um corpo rígido como se estivesse se movendo, usando suas propriedades constant_linear_velocity
e constant_angular_velocity
(velocidades lineares e angulares constantes, respectivamente).
Nós StaticBody2D
são mais comumente usado em objetos que são parte do ambiente ou que não precisam ter um comportamento dinâmico.
Exemplos de uso para StaticBody2D
:
Plataformas (inclusive as móveis)
Esteiras transportadoras
Paredes e outros obstáculos
RigidBody2D¶
Este é o nó que um implementa física 2D simulada. Você não controla um RigidBody2D diretamente. Em vez disso, você lhe aplica forças e o motor da física calcula o movimento resultante, inclusive colisões com outros corpos, além de respostas a colisões como quicar, girar, etc.
Você pode modificar o comportamento de um corpo rígido através de propriedades como "Mass" (massa), "Friction" (atrito) ou "Bounce" (restituição/quique), configuráveis no Inspetor.
O comportamento do corpo também é afetado pelas propriedades do mundo, definidas em Configurações de projeto -> Física, ou entrando em uma Area2D que sobrepõe as propriedades da física global.
Quando um corpo rígido está em repouso e não se move por um tempo, ele adormece. Um corpo adormecido age como um corpo estático, e suas forças não são calculadas pelo motor de física. O corpo acordará quando forças forem aplicadas, seja por colisão ou por código.
Usando RigidBody2D¶
Um dos benefícios de usar um corpo rígido é que muito do comportamento dele pode ser obtido "de graça" sem escrever código algum. Por exemplo, se você está fazendo um jogo no estilo "Angry Birds" com blocos caindo, você só precisaria criar RigidBody2Ds e ajustar suas propriedades. Empilhamento, queda e quique seriam calculados automaticamente pelor motor de física.
However, if you do wish to have some control over the body, you should take
care - altering the position
, linear_velocity
, or other physics properties
of a rigid body can result in unexpected behavior. If you need to alter any
of the physics-related properties, you should use the _integrate_forces()
callback instead of _physics_process()
. In this callback, you have access
to the body's PhysicsDirectBodyState2D,
which allows for safely changing properties and synchronizing them with
the physics engine.
Por exemplo, aqui está o código para uma nave no estilo de "Asteroids":
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);
}
}
Note que nós não estamos configurando as propriedades linear_velocity
ou angular_velocity
diretamente, mas sim aplicando forças (thrust
(impulso) e torque
) ao corpo, e deixando o motor de física calcular o movimento resultante.
Nota
Quando um corpo rígido adormece, a função _integrate_forces()
não será chamada. Para sobrepor este comportamento, você precisará manter o corpo acordado, criando uma colisão, aplicando forças a ele ou desabilitando a propriedade can_sleep. Fique ciente que isso pode ter um efeito negativo no desempenho.
Relato de contato¶
By default, rigid bodies do not keep track of contacts, because this can require a huge amount of memory if many bodies are in the scene. To enable contact reporting, set the max_contacts_reported property to a non-zero value. The contacts can then be obtained via PhysicsDirectBodyState2D.get_contact_count() and related functions.
Monitoramento de contato via sinais pode ser habilitado através da propriedade contact_monitor. Veja RigidBody2D para a lista de sinais disponíveis.
CharacterBody2D¶
CharacterBody2D bodies detect collisions with other bodies, but are not affected by physics properties like gravity or friction. Instead, they must be controlled by the user via code. The physics engine will not move a character body.
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¶
After a collision, you may want the body to bounce, to slide along a wall, or to alter the properties of the object it hit. The way you handle collision response depends on which method you used to move the CharacterBody2D.
move_and_collide¶
When using move_and_collide()
, the function returns a
KinematicCollision2D object, which contains
information about the collision and the colliding body. You can use this
information to determine the response.
For example, if you want to find the point in space where the collision occurred:
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();
}
}
}
Or to bounce off of the colliding object:
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¶
Sliding is a common collision response; imagine a player moving along walls
in a top-down game or running up and down slopes in a platformer. While it's
possible to code this response yourself after using move_and_collide()
,
move_and_slide()
provides a convenient way to implement sliding movement
without writing much code.
Aviso
move_and_slide()
automatically includes the timestep in its
calculation, so you should not multiply the velocity vector
by delta
.
For example, use the following code to make a character that can walk along the ground (including slopes) and jump when standing on the ground:
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();
}
}
See Personagem cinemático (2D) for more details on using move_and_slide()
,
including a demo project with detailed code.