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.
| Passo | Posição da bala (X) | Parede de X = 5,0 a 5,3 | Contato? |
|---|---|---|---|
| 1 | 2,0 | Antes | Não |
| 2 | 4,0 | Antes | Não |
| 3 | 6,0 | Depois | Nã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ção | Método |
|---|---|
| Corpo dinâmico, controle direto | rb.linearVelocity = ... |
| Corpo dinâmico, força | rb.AddForce(...) |
| Corpo Kinematic | rb.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.
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:
| Modo | Custo | Quando usar |
|---|---|---|
| Discrete | Menor | Quase tudo: caixas, inimigos, objetos lentos |
| Continuous Speculative | Baixo a médio | Objetos rápidos em geral, Kinematic rápido, coisas que giram rápido |
| Continuous | Alto | Objetos dinâmicos que precisam ser acertados por algo rápido |
| Continuous Dynamic | Mais alto | O 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.



