Personagem cinemático (2D)¶
Introdução¶
Sim, o nome soa estranho. "Personagem cinemático". O que é aquilo? A razão para o nome é que, quando os motores de física surgiram, eles eram chamados de motores "Dinâmicos" (porque lidavam principalmente com respostas de colisão). Muitas tentativas foram feitas para criar um controlador de personagem usando os motores de dinâmica, mas não foi tão fácil quanto parecia. Godot tem uma das melhores implementações de controlador de personagem dinâmico que você pode encontrar (como pode ser visto na demo 2d/platformer), mas usá-lo requer um nível considerável de habilidade e compreensão dos motores de física (ou muita paciência com testes e erro).
Alguns motores de física, como o Havok, parecem defender os controladores de personagens dinâmicos como a melhor opção, enquanto outros (PhysX) preferem promover o cinemático.
Então, qual é a diferença?:
Um controlador dinâmico de personagem usa um corpo rígido com um tensor de inércia infinito. É um corpo rígido que não pode girar. Os motores de física sempre permitem que os objetos se movam e colidam, então resolvem suas colisões todas juntas. Isso torna os controladores de personagem dinâmicos capazes de interagir perfeitamente com outros objetos físicos, como visto na demonstração do jogo de plataforma. No entanto, essas interações nem sempre são previsíveis. As colisões podem levar mais de um quadro para serem resolvidas, então algumas colisões podem parecer deslocar um pouquinho. Esses problemas podem ser corrigidos, mas requerem uma certa habilidade.
Presume-se que um controlador de caractere cinemático comece sempre em um estado sem colisão e sempre se moverá para um estado sem colisão. Se começar em estado de colisão, tentará se libertar como fazem os corpos rígidos, mas esta é a exceção, não a regra. Isso torna seu controle e movimento muito mais previsível e fácil de programar. No entanto, como desvantagem, eles não podem interagir diretamente com outros objetos físicos, a menos que sejam feitos à mão em código.
Este breve tutorial se concentrará no controlador de personagem cinemático. Basicamente, a maneira antiga de lidar com colisões (que não é necessariamente mais simples por baixo dos panos, mas bem escondida e apresentada como uma API simples e agradável).
Processo físico¶
Para gerenciar a lógica de um corpo ou personagem cinemático, é sempre aconselhável usar o processo de física, pois é chamado antes da simulação de física e sua execução está em sincronia com o servidor de física, também é chamado a mesma quantidade de vezes por segundo, sempre. Isso faz com que a física e o cálculo de movimento funcionem de maneira mais previsível do que usando o processo regular, que pode ter picos ou perder a precisão se a taxa de quadros for muito alta ou muito baixa.
extends KinematicBody2D
func _physics_process(delta):
pass
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
}
}
Configuração da cena¶
Para ter algo para testar, aqui está a cena (do tutorial do tilemap): kbscene.zip
. Estaremos criando uma nova cena para o personagem. Use o sprite do robô e crie uma cena como esta:
Você notará que há um ícone de aviso próximo ao nosso nó CollisionShape2D; isso porque não definimos uma forma para ele. Crie um novo CircleShape2D na propriedade de forma de CollisionShape2D. Clique em <CircleShape2D> para acessar as opções e defina o raio como 30:
Observação: como mencionado anteriormente no tutorial de física, o engine de física não pode lidar com a escala na maioria dos tipos de formas (somente polígonos de colisão, planos e segmentos funcionam), portanto, sempre altere os parâmetros (como o raio) da forma em vez de dimensioná-la. O mesmo também é verdade para os próprios corpos cinemáticos/rígidos/estáticos, pois sua escala afeta a escala da forma.
Agora, crie um script para o personagem, o usado como exemplo acima deve servir de base.
Por fim, crie uma instância dessa cena de personagem no tilemap e torne a cena do mapa a principal, para que seja executada ao pressionar o play.
Movendo o personagem cinemático¶
Volte para a cena do personagem e abra o script, a mágica começa agora! O corpo cinemático não fará nada por padrão, mas tem uma função útil chamada KinematicBody2D.move_and_collide(). Esta função recebe um Vector2 como argumento e tenta aplicar esse movimento ao corpo cinemático. Se ocorrer uma colisão, ele para no momento da colisão.
Então, vamos mover nosso sprite para baixo até atingir o chão:
extends KinematicBody2D
func _physics_process(delta):
move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
public override void _PhysicsProcess(float delta)
{
// Move down 1 pixel per physics frame
MoveAndCollide(new Vector2(0, 1));
}
}
O resultado é que o personagem vai se mover, mas parar ao bater no chão. Bem legal, né?
O próximo passo será adicionar gravidade à mistura, de forma que ela se comporte um pouco mais como um personagem normal do jogo:
extends KinematicBody2D
const GRAVITY = 200.0
var velocity = Vector2()
func _physics_process(delta):
velocity.y += delta * GRAVITY
var motion = velocity * delta
move_and_collide(motion)
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
const float gravity = 200.0f;
Vector2 velocity;
public override void _PhysicsProcess(float delta)
{
velocity.y += delta * gravity;
var motion = velocity * delta;
MoveAndCollide(motion);
}
}
Agora o personagem cai sem problemas. Vamos fazê-lo andar para os lados, esquerda e direita ao tocar nas teclas direcionais. Lembre-se de que os valores usados (pelo menos para velocidade) são pixels/segundo.
Isso adiciona um suporte simples para andar pressionando esquerda e direita:
extends KinematicBody2D
const GRAVITY = 200.0
const WALK_SPEED = 200
var velocity = Vector2()
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
# We don't need to multiply velocity by delta because "move_and_slide" already takes delta time into account.
# The second parameter of "move_and_slide" is the normal pointing up.
# In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
move_and_slide(velocity, Vector2(0, -1))
using Godot;
using System;
public class PhysicsScript : KinematicBody2D
{
const float gravity = 200.0f;
const int walkSpeed = 200;
Vector2 velocity;
public override void _PhysicsProcess(float delta)
{
velocity.y += delta * gravity;
if (Input.IsActionPressed("ui_left"))
{
velocity.x = -walkSpeed;
}
else if (Input.IsActionPressed("ui_right"))
{
velocity.x = walkSpeed;
}
else
{
velocity.x = 0;
}
// We don't need to multiply velocity by delta because "MoveAndSlide" already takes delta time into account.
// The second parameter of "MoveAndSlide" is the normal pointing up.
// In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
MoveAndSlide(velocity, new Vector2(0, -1));
}
}
E experimente.
Este é um bom ponto de partida para um jogo de plataforma. Uma demonstração mais completa pode ser encontrada no zip de demonstração distribuído com o mecanismo ou no https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character.