Voltar para o Blog
Quest Log

Objeto Atravessando Parede na Unity: Collision Detection

Sequência de quadros de uma bala antes e depois de uma parede fina, sem nenhum quadro tocando a parede

Bala atravessando parede? Unity collision detection continuous: tunneling, modos Discrete, Continuous e Speculative, transform e raycast para bala rápida.

Ferramenta:UnityC#

Objeto Atravessando Parede na Unity: Collision Detection

Quando um objeto atravessa a parede na Unity, a causa quase sempre é uma destas três: o objeto é rápido demais para a espessura da parede (o chamado tunneling), ele está sendo movido pelo transform em vez de pelo Rigidbody, ou os colliders são finos ou pequenos demais. A correção para o primeiro caso é a opção Unity collision detection continuous no Rigidbody; para o segundo, mover pelos métodos de física; para bala muito rápida, trocar a física por raycast.

Este post mostra o tunneling acontecendo quadro a quadro, explica os quatro modos de Collision Detection do Unity 6.3 LTS e quanto cada um custa, e traz o código de uma bala que nunca atravessa parede, em C#.

Por que o objeto atravessa a parede

A física da Unity não simula o tempo de forma contínua. Ela tira "fotos" em intervalos fixos, o passo de física, que por padrão acontece 50 vezes por segundo (Fixed Timestep de 0,02 s em Project Settings > Time). Entre uma foto e outra, o objeto não existe em lugar nenhum: ele salta da posição antiga para a nova.

No modo padrão de colisão, Discrete, a Unity só verifica se os colliders se sobrepõem na foto nova. Se o objeto estava antes da parede numa foto e depois dela na seguinte, sem nenhuma foto em que estivesse sobrepondo, a física nunca vê o contato.

Esse é o tunneling. Ele não é bug: é a consequência de simular em passos.

Tunneling explicado frame a frame

Um exemplo com números:

  • Bala com velocidade de 100 m/s.
  • Passo de física de 0,02 s.
  • Em cada passo, a bala anda 100 × 0,02 = 2 metros.
  • Parede com 0,3 m de espessura. Bala com collider de 0,1 m.
PassoPosição da bala (X)Parede de X = 5,0 a 5,3Contato?
12,0AntesNão
24,0AntesNão
36,0DepoisNão

A bala esteve antes da parede no passo 2 e depois dela no passo 3. Nenhuma foto mostra sobreposição. A bala atravessou.

A regra prática: se o objeto anda por passo mais do que a soma da espessura dele com a da parede, o Discrete pode falhar. A distância por passo é velocidade × Fixed Timestep, o mesmo raciocínio de movimento por frame que o post de delta time e movimento por frame explica para o Update.

Reduzir o Fixed Timestep (mais passos por segundo) diminui a distância por passo, mas aumenta o custo de toda a física do jogo. Raramente é a melhor correção.

Unity collision detection continuous e os outros modos

O campo Collision Detection do Rigidbody tem quatro opções, descritas na referência do CollisionDetectionMode no Unity 6.3:

Discrete (padrão). Verifica só a posição final de cada passo. Mais barato. Suficiente para tudo que não é rápido.

Continuous. Detecção contínua por varredura contra colliders estáticos (sem Rigidbody). O objeto não atravessa parede e chão do cenário, mas pode atravessar outros corpos dinâmicos.

Continuous Dynamic. Varredura contra estáticos e contra corpos dinâmicos. A documentação recomenda este modo para o objeto rápido e Continuous nos objetos dinâmicos com que ele precisa colidir.

Continuous Speculative. Outro algoritmo: em vez de varrer a trajetória, ele amplia a área de busca de contatos com base no movimento e resolve os contatos possíveis antes de acontecerem. É o modo que funciona em corpos Kinematic, e também pega colisões causadas por rotação (uma pá de pinball girando), que os modos por varredura não pegam.

Duas limitações do manual que valem para todos os modos contínuos:

  • Só funcionam com Box, Sphere e Capsule Collider no Rigidbody. Uma bala com Mesh Collider fica no Discrete na prática.
  • Não são infalíveis. O manual descreve a detecção contínua como uma rede de segurança, não como garantia.

Arquivo ConfigurarProjetil.cs, que ajusta o modo pela velocidade do objeto no momento do disparo:

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class ConfigurarProjetil : MonoBehaviour
{
    [SerializeField] private float velocidadeInicial = 60f;
    [SerializeField] private float limiteParaContinuo = 20f;

    private Rigidbody rb;

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
    }

    public void Disparar(Vector3 direcao)
    {
        rb.collisionDetectionMode = velocidadeInicial > limiteParaContinuo
            ? CollisionDetectionMode.ContinuousDynamic
            : CollisionDetectionMode.Discrete;

        rb.interpolation = RigidbodyInterpolation.Interpolate;
        rb.linearVelocity = direcao.normalized * velocidadeInicial;
    }
}

Em 2D, o Rigidbody2D tem só dois modos: Discrete e Continuous. O Continuous encontra o primeiro ponto de impacto entre as duas posições e move o corpo até ali, com custo maior de CPU. Para o jogador num jogo de plataforma com quedas rápidas e plataformas finas, ligue Continuous.

Mover pelo transform quebra a física

Mesmo com Continuous Dynamic, o objeto atravessa a parede se você mover assim:

transform.position += direcao * velocidade * Time.deltaTime;

Mudar o transform de um objeto com Rigidbody é um teleporte. A física não sabe de onde o objeto veio, então não há trajetória para varrer. No passo seguinte, ela encontra o objeto dentro da parede e tenta empurrar para fora, às vezes para o lado errado. O resultado é o objeto que atravessa, ou que treme encostado na parede.

As formas corretas de mover um Rigidbody:

SituaçãoMétodo
Corpo dinâmico, controle diretorb.linearVelocity = ...
Corpo dinâmico, forçarb.AddForce(...)
Corpo Kinematicrb.MovePosition(...) no FixedUpdate
Teleporte intencional (respawn)rb.position = ...

transform.Translate e transform.position ficam para objetos sem Rigidbody. O post de Rigidbody na Unity com AddForce, velocity e MovePosition mostra cada um desses métodos em código.

O mesmo vale para personagem com Rigidbody arrastado pela animação com Apply Root Motion sem configuração de física: a animação move o transform e o personagem entra na parede.

Tunneling é o tipo de problema que só aparece quando o jogo começa a ficar rápido: a primeira arma de verdade, o primeiro dash, a primeira queda longa. Diagnosticar exige saber como a física, o movimento e a geometria da fase se combinam, e é esse olhar de sistema que o curso de Unity treina ao construir um jogo inteiro, em vez de exemplos soltos.

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

Parede fina e collider pequeno

Mesmo com velocidades razoáveis, geometria ruim facilita o tunneling:

  • Parede plana sem espessura (um Plane ou um Mesh Collider de uma face). Para a física, ela tem espessura praticamente zero. Use Box Collider com alguma espessura nas paredes, mesmo que o modelo visual seja fino. O collider não precisa ter o formato exato da arte.
  • Bala com collider minúsculo. Um collider de 0,02 m numa bala que anda 1 m por passo é o pior caso. O collider da bala pode ser maior que o modelo; ninguém vai notar.
  • Mesh Collider no cenário inteiro. Funciona, mas pequenas frestas entre triângulos e faces viradas para dentro criam pontos por onde objetos escapam. Use colliders primitivos nas paredes principais.
  • Escala do mundo. Um personagem com 50 unidades de altura cai a velocidades enormes em unidades por segundo, e tudo vira "rápido demais". Mantenha a escala próxima de 1 unidade = 1 metro.

Existe também o Default Contact Offset em Project Settings > Physics: uma pequena margem de distância em que os colliders já são considerados em contato. Ele ajuda a estabilidade de objetos encostados, mas não é ferramenta contra tunneling; mexer nele raramente resolve e pode criar outros problemas.

Bala rápida: raycast em vez de física

Para projéteis muito rápidos, a solução mais robusta é não depender da colisão física. A cada passo, a bala lança um raycast da posição atual até a posição em que estará no próximo passo. Se o raio acerta algo, a bala acertou. Como o raio cobre o caminho inteiro, não há como pular a parede.

Arquivo BalaComRaycast.cs, sem Rigidbody nem collider na bala:

using UnityEngine;

public class BalaComRaycast : MonoBehaviour
{
    [SerializeField] private float velocidade = 150f;
    [SerializeField] private float tempoDeVida = 3f;
    [SerializeField] private LayerMask layersAtingiveis;

    private float tempoRestante;

    private void OnEnable()
    {
        tempoRestante = tempoDeVida;
    }

    private void FixedUpdate()
    {
        float passo = velocidade * Time.fixedDeltaTime;
        Vector3 origem = transform.position;
        Vector3 direcao = transform.forward;

        if (Physics.Raycast(origem, direcao, out RaycastHit hit, passo, layersAtingiveis, QueryTriggerInteraction.Ignore))
        {
            Debug.Log($"Bala acertou {hit.collider.name} em {hit.point}");
            Destroy(gameObject);
            return;
        }

        transform.position = origem + direcao * passo;

        tempoRestante -= Time.fixedDeltaTime;
        if (tempoRestante <= 0f)
        {
            Destroy(gameObject);
        }
    }
}

Aqui mover pelo transform está certo, porque a bala não tem Rigidbody: quem garante a colisão é o raycast, não a física. Para bala com gravidade (flecha, granada de lançador), some uma velocidade vertical a cada passo e lance o raio da posição antiga até a nova com Physics.Linecast.

Se a bala nem precisa ser vista viajando (arma de fogo comum), dispense o objeto e faça um único raycast no momento do tiro.

Custo de performance de cada modo

Do mais barato para o mais caro, segundo o manual do Unity 6.3:

ModoCustoQuando usar
DiscreteMenorQuase tudo: caixas, inimigos, objetos lentos
Continuous SpeculativeBaixo a médioObjetos rápidos em geral, Kinematic rápido, coisas que giram rápido
ContinuousAltoObjetos dinâmicos que precisam ser acertados por algo rápido
Continuous DynamicMais altoO objeto rápido que não pode atravessar nada

O manual descreve os modos por varredura (Continuous e Continuous Dynamic) como os mais precisos e os mais exigentes, com impacto significativo de performance quando usados em muitos objetos, principalmente objetos rápidos próximos uns dos outros. O Speculative é descrito como menos exigente, mas menos preciso, com o risco de colisões fantasma: um objeto rápido pode tratar uma quina como rampa e pular.

A estratégia que funciona: Discrete em tudo por padrão, contínuo só no que você viu atravessar. Para projéteis, raycast. Abra o Profiler (Window > Analysis > Profiler) e compare o tempo de física antes e depois de mudar os modos numa cena cheia.

Próximo passo

Monte uma cena com uma parede de 0,1 m de espessura e um canhão que dispara esferas com Rigidbody a 30, 60 e 120 m/s. Teste cada velocidade em Discrete, Continuous Dynamic e Continuous Speculative e anote onde cada um começa a falhar. Depois troque a esfera pela BalaComRaycast e confirme que nenhuma velocidade atravessa.

Se você também usa Godot, o guia de física de jogos no Godot cobre os mesmos conceitos de corpos e passos de física, e lá a opção equivalente se chama Continuous CD no RigidBody.

Perguntas frequentes

Por que meu Rigidbody atravessa a parede na Unity?

As causas mais comuns são o objeto rápido demais para a espessura da parede no modo Discrete (tunneling), mover o objeto pelo transform em vez de pelo Rigidbody, ou colliders finos e mal configurados. Troque o Collision Detection para Continuous Dynamic ou Continuous Speculative e mova sempre pelo Rigidbody.

Qual a diferença entre Continuous e Continuous Dynamic?

Continuous previne tunneling contra colliders estáticos, sem Rigidbody. Continuous Dynamic previne também contra corpos dinâmicos em movimento. A documentação recomenda Continuous Dynamic para o objeto rápido e Continuous para os objetos dinâmicos com que ele precisa colidir.

Continuous Speculative é melhor que Continuous?

É mais barato, funciona com corpos Kinematic e pega colisões causadas por rotação, que os modos por varredura não pegam. Em troca, pode gerar colisões fantasma, em que o objeto rápido reage a uma superfície que não deveria tocar, como pular ao passar por uma quina.

Continuous Collision Detection funciona com Mesh Collider?

Segundo a documentação do Unity 6.3, a detecção contínua só é suportada para Rigidbodies com Sphere, Capsule ou Box Collider. Para o objeto rápido, use um collider primitivo.