Voltar para o Blog
Quest Log

Rigidbody2D na Unity: Mover Personagem 2D com Física

Personagem 2D em pixel art no meio de um salto sobre plataformas, com setas indicando gravidade e velocidade

Unity Rigidbody2D explicado pelo problema que cada opção resolve: Body Type, Gravity Scale, linearVelocity, Collision Detection, Freeze Rotation e Interpolate.

Ferramenta:UnityC#

Rigidbody2D na Unity: Mover Personagem 2D com Física

Unity Rigidbody2D é o componente que coloca um sprite sob a física 2D: ele faz o personagem cair, parar no chão, bater em paredes e ser empurrado. O movimento em si sai de uma linha (rb.linearVelocity = ...), mas o comportamento que o jogador sente vem das configurações do componente: Body Type, Gravity Scale, Collision Detection, Constraints e Interpolate. Configurar errado gera personagem que gira, cai devagar, atravessa plataforma fina ou treme quando a câmera segue.

Este post passa por cada configuração que importa explicando o problema que ela resolve, com código C# para Unity 6.3 LTS. O script completo de andar e pular fica no post irmão de movimento de personagem 2D na Unity; aqui o foco é o componente.

Unity Rigidbody2D: configurações que importam

Adicione o componente em Add Component > Physics 2D > Rigidbody 2D. O Inspector mostra mais de dez campos, mas para um personagem de plataforma você mexe em seis:

CampoValor inicial para personagemProblema que resolve
Body TypeDynamicPersonagem sofre gravidade e colide
Gravity Scale3 a 4Queda com peso, não flutuante
Collision DetectionContinuousNão atravessar plataforma fina em queda rápida
Sleeping ModeStart AwakePersonagem responde desde o primeiro frame
InterpolateInterpolateMovimento suave com câmera seguindo
Constraints > Freeze RotationMarcadoPersonagem não tomba

Os valores de Gravity Scale são ponto de partida, não regra: dependem do tamanho do personagem em unidades e da altura de pulo que você quer. O resto das seções explica o porquê de cada linha.

Um aviso de API antes do código: no Unity 6 a velocidade do Rigidbody2D se chama linearVelocity, com os atalhos linearVelocityX e linearVelocityY, e o arrasto se chama linearDamping. Código que usa rb.velocity e rb.drag aparece como obsoleto. A referência do Rigidbody2D no Unity 6.3 lista só os nomes novos.

Dynamic, Kinematic e Static

O Body Type decide quem manda no corpo.

Dynamic. A física manda: gravidade, forças, colisões e empurrões de outros corpos. É o tipo do personagem, de inimigos que caem, de caixas e projéteis. É o único tipo que reage sozinho a colisões.

Kinematic. O seu código manda. O corpo não sofre gravidade, não é empurrado e só se move quando você altera linearVelocity ou chama MovePosition. Ele empurra corpos Dynamic que estão no caminho. Use em plataforma móvel, elevador, inimigo que segue trilho e qualquer coisa que precisa de trajetória exata.

Por padrão, um Kinematic só gera contato com corpos Dynamic. Se você precisa que ele detecte outro Kinematic ou um Static (por exemplo, um inimigo Kinematic que precisa saber que encostou na parede), marque Use Full Kinematic Contacts (useFullKinematicContacts no código). Os callbacks de colisão passam a disparar, mas sem resposta física: o corpo continua podendo sobrepor o outro, e quem decide o que fazer é o seu script.

Static. Não se move. Na prática, chão e parede normalmente nem precisam de Rigidbody2D: um Collider2D sozinho já é tratado como estático. O Body Type Static é útil quando você quer agrupar vários colliders num corpo só ou trocar o tipo em tempo de jogo (uma ponte que é Static até o jogador ativar e vira Dynamic para desabar).

Trocar por código é uma linha:

rb.bodyType = RigidbodyType2D.Dynamic;

linearVelocity vs AddForce em 2D

Em 2D você tem as mesmas duas famílias do 3D: atribuir velocidade ou somar força.

linearVelocity substitui a velocidade atual. É o padrão para personagem de plataforma porque dá resposta imediata: soltou o direcional, parou. AddForce soma ao que já existe e deixa a física acelerar. Funciona para nave, bola, personagem com inércia de propósito (gelo, jetpack) e para empurrões pontuais.

Os modos de força em 2D são só dois, ForceMode2D.Force (contínua, aplicada a cada passo) e ForceMode2D.Impulse (instantânea). Os dois consideram a massa.

Arquivo CorpoPersonagem2D.cs, que mostra os dois usos no mesmo corpo: andar com velocidade direta e levar um empurrão de impulso quando é atingido:

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class CorpoPersonagem2D : MonoBehaviour
{
    [SerializeField] private float velocidade = 7f;
    [SerializeField] private float forcaEmpurrao = 8f;

    private Rigidbody2D rb;
    private InputAction acaoMover;
    private float entradaX;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        acaoMover = InputSystem.actions.FindAction("Move");
    }

    private void Update()
    {
        entradaX = acaoMover.ReadValue<Vector2>().x;
    }

    private void FixedUpdate()
    {
        // Só o X é nosso; o Y continua com a gravidade.
        rb.linearVelocityX = entradaX * velocidade;
    }

    public void LevarEmpurrao(Vector2 origemDoGolpe)
    {
        Vector2 direcao = ((Vector2)transform.position - origemDoGolpe).normalized;
        direcao.y = 0.5f; // levanta um pouco para sair do chão
        rb.linearVelocity = Vector2.zero;
        rb.AddForce(direcao.normalized * forcaEmpurrao, ForceMode2D.Impulse);
    }
}

Esse script tem um conflito de propósito, para você ver acontecer: o FixedUpdate sobrescreve o X a cada passo, então o empurrão some no passo seguinte. Em jogo, a correção é um pequeno tempo de "atordoado" em que o FixedUpdate não mexe no X. É o tipo de detalhe que separa um personagem que funciona sozinho de um que funciona dentro de um jogo com inimigos.

Gravity Scale e queda mais rápida

Gravity Scale multiplica a gravidade global (Project Settings > Physics 2D, padrão Y = -9,81) só para este corpo. Com escala 1, um personagem de 1 unidade de altura cai com a sensação de uma pena: o mundo real é lento para a escala de um jogo de plataforma.

Duas técnicas de game feel saem desse campo:

  1. Gravidade mais forte na descida. O pulo sobe com uma gravidade e cai com outra maior. O arco fica assimétrico, que é o que a maioria dos jogos de plataforma faz.
  2. Pulo variável. Soltou o botão no meio da subida, a gravidade aumenta e o pulo fica curto.

Arquivo GravidadePulo2D.cs:

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Rigidbody2D))]
public class GravidadePulo2D : MonoBehaviour
{
    [SerializeField] private float gravidadeNormal = 3f;
    [SerializeField] private float gravidadeQueda = 5f;
    [SerializeField] private float gravidadePuloCurto = 6f;
    [SerializeField] private float velocidadeMaximaQueda = 18f;

    private Rigidbody2D rb;
    private InputAction acaoPular;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
        acaoPular = InputSystem.actions.FindAction("Jump");
    }

    private void FixedUpdate()
    {
        if (rb.linearVelocityY < 0f)
        {
            rb.gravityScale = gravidadeQueda;
        }
        else if (rb.linearVelocityY > 0f && !acaoPular.IsPressed())
        {
            rb.gravityScale = gravidadePuloCurto;
        }
        else
        {
            rb.gravityScale = gravidadeNormal;
        }

        // Limita a velocidade terminal para a queda não ficar incontrolável.
        if (rb.linearVelocityY < -velocidadeMaximaQueda)
        {
            rb.linearVelocityY = -velocidadeMaximaQueda;
        }
    }
}

O limite de queda importa mais do que parece: sem ele, uma queda longa chega numa velocidade em que o jogador não consegue mais reagir, e a chance de atravessar plataforma fina aumenta.

Configurar o Rigidbody2D é a parte rápida. O que consome semanas num projeto solo é fazer essas peças conversarem: o empurrão que briga com o movimento, a gravidade de queda que precisa respeitar o coyote time, a animação que tem que saber se o personagem está subindo ou caindo. O curso de Unity constrói esse personagem dentro de um jogo completo, então você vê cada conflito aparecer e ser resolvido no lugar certo.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Freeze Rotation e Interpolate

Freeze Rotation. Um Rigidbody2D Dynamic calcula torque: se a quina do collider bate num degrau, o corpo gira. Para caixa, ótimo. Para personagem, é ele caindo de lado na primeira rampa. Em Constraints, marque Freeze Rotation (o eixo Z, único eixo de rotação em 2D). Por código: rb.freezeRotation = true;.

Se você quer o personagem acompanhando a inclinação da rampa, deixe a rotação congelada no corpo e gire só o sprite filho, calculado a partir da normal do chão. Física estável e visual inclinado.

Interpolate. A física 2D roda no passo fixo (Time > Fixed Timestep, 0,02 s no padrão, 50 vezes por segundo). O monitor pode mostrar 60, 120 ou 144 quadros. Sem interpolação, o sprite só muda de lugar quando a física roda, e uma câmera seguindo suave denuncia os degraus como tremedeira. As opções:

  • None: sem suavização. Bom para objetos parados ou que a câmera não segue.
  • Interpolate: usa as posições dos passos anteriores. Fica um passo atrás, mas é estável. Padrão recomendado para o jogador.
  • Extrapolate: estima a próxima posição. Responde antes, mas erra em mudanças bruscas (bater na parede) e pode mostrar o sprite entrando no obstáculo por um quadro.

Com Interpolate ligado, leia e escreva posição pelo rb.position ou pela velocidade, nunca pelo transform.position, que quebra a suavização.

Collision Detection contínuo

O modo Discrete checa colisão só na posição nova do corpo a cada passo. Se o personagem cai a 18 unidades por segundo e a plataforma tem 0,2 de espessura, em um passo de 0,02 s ele anda 0,36: pode estar acima da plataforma num passo e abaixo no seguinte, sem nunca encostar. É o tunneling.

O modo Continuous calcula o primeiro ponto de impacto entre as duas posições e para o corpo ali. Custa mais CPU, então use no que é rápido e importa: o jogador, projéteis físicos, a bola do jogo. Inimigos lentos e caixas ficam em Discrete. Para bala muito rápida, nem o Continuous resolve tudo, e o caminho é trocar a física por raycast.

Erros comuns de física 2D

Misturar 2D e 3D. Rigidbody2D só conversa com Collider2D (BoxCollider2D, CapsuleCollider2D). Um BoxCollider 3D num objeto com Rigidbody2D é ignorado pela física 2D, e OnCollisionEnter (3D) nunca dispara num jogo 2D: o certo é OnCollisionEnter2D.

Mover pelo transform. transform.Translate num corpo Dynamic teleporta por fora da física: o personagem entra na parede e é cuspido de volta. Use linearVelocity, AddForce ou MovePosition.

Zerar o Y sem querer. rb.linearVelocity = new Vector2(x * velocidade, 0f); a cada passo apaga a gravidade, e o personagem desce flutuando. Use linearVelocityX para mexer só no horizontal.

Personagem grudando na parede. O atrito do collider segura o corpo enquanto o jogador empurra contra a parede no ar. A correção é um Physics Material 2D com Friction 0 no personagem.

Escala do mundo errada. Sprites importados com Pixels Per Unit baixo viram objetos gigantes em unidades, e a gravidade parece lenta porque o personagem tem 20 unidades de altura. Ajuste o PPU para o personagem ficar com cerca de 1 a 2 unidades de altura e a física passa a se comportar como esperado.

Esperar o Box2D v3 nos componentes. O Unity 6.3 LTS trouxe uma API de física 2D de baixo nível baseada no Box2D v3, mas ela é um sistema separado, sem componentes prontos, que não afeta Rigidbody 2D e Collider 2D. Tudo deste post vale para os componentes que você usa no Inspector.

Próximo passo

Monte uma cena com um personagem usando a tabela do início e um chão com uma plataforma fina no alto. Solte o personagem de cima com Collision Detection em Discrete, aumente o limite de queda até ver o tunneling, e troque para Continuous. Depois ligue e desligue Interpolate com uma câmera seguindo para enxergar a diferença.

Com o componente configurado, o passo seguinte é o controle: o post de movimento de personagem de plataforma 2D cobre aceleração, desaceleração e as decisões de game feel que deixam o controle bom de jogar. Se você também mexe com Godot, o guia de física de jogos no Godot mostra os mesmos tipos de corpo com os nomes de lá.

Perguntas frequentes

Rigidbody2D.velocity não funciona no Unity 6?

No Unity 6 a propriedade se chama Rigidbody2D.linearVelocity, com os atalhos linearVelocityX e linearVelocityY. O nome velocity aparece como obsoleto. O comportamento é o mesmo, só o nome mudou.

Qual a diferença entre Dynamic, Kinematic e Static no Rigidbody2D?

Dynamic é movido pela física: gravidade, forças e colisões. Kinematic só se move quando o seu código manda e não sofre gravidade nem empurrão. Static não se move e serve para cenário. Personagem de plataforma quase sempre é Dynamic; plataforma móvel é Kinematic.

Por que meu personagem 2D gira e cai de lado?

O Rigidbody2D Dynamic calcula rotação quando o collider bate numa quina. Marque Freeze Rotation em Constraints (ou rb.freezeRotation = true no código) e o personagem fica sempre em pé.

A Unity usa Box2D v3 na física 2D?

No Unity 6.3 LTS existe uma API de física 2D de baixo nível baseada no Box2D v3, no namespace UnityEngine.LowLevelPhysics2D. Ela é separada dos componentes Rigidbody 2D e Collider 2D, que continuam funcionando como sempre e são o que este post configura.