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

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:

../../_images/kbscene.png

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:

../../_images/kbradius.png

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.

../../_images/kbinstance.png

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

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)

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.
    velocity = move_and_slide(velocity, 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.