Colisão não é só testar se dois objetos se sobrepõem
A teoria das colisões resolve um problema aparentemente simples: determinar se dois objetos geométricos ocupam o mesmo espaço e, se ocuparem, calcular o momento do impacto e as informações resultantes. O que muita gente não leva a sério desde o início é que existem camadas de complexidade que mudam completamente como você aborda o problema. Não adianta começar com detecção de interseção de mesh e esperar performance. Você precisa estruturar o pipeline em fases. O pipeline padrão funciona assim:Broad-phase, onde você descarta rapidamente pares que estão longe demais para colidir usando estruturas como BVH, SAT ou grid spatial. Em seguida vem o narrow-phase, que é onde a geometria real é checada com precisão. E por último, resposta à colisão, que aplica impulso, fricção e resolve sobreposições. A maioria dos tutoriais para o ensino médio foca na parte física — conservação de momento, energia cinética, coeficiente de restituição. Mas se você está implementando isso em software, a coisa muda de figura.
Coeficiente de restituição e a teoria das colisoes na prática
O coeficiente de restituição (e) é o que diferencia um simulation interessante de um que parece plástico. Um valor de e = 1 significa colisão perfeitamente elástica, onde toda a energia cinética se conserva. e = 0 é perfeitamente inelástica, os objetos permanecem grudados após o impacto. Na prática, bola de tênis contra raquete fica em torno de 0,7. Um martelo de aço batendo em concreto é próximo de 0,2. O erro comum aqui é tratar todos os materiais como se tivessem o mesmo e. Isso faz o sistema parecer irreconhecível. A equação fundamental para colisão 1D entre duas massas é:
v1f = ((m1 - e*m2)*v1 + (1+e)*m2*v2) / (m1 + m2) v2f = ((m2 - e*m1)*v2 + (1+e)*m1*v1) / (m1 + m2)
Em 2D e 3D, você decompõe as velocidades ao longo da normal de colisão e aplica a mesma lógica vetorial. A componente tangencial fica responsável pelo atrito, que é outra camada de complexidade que raramente é bem explicada.
👉 Clique no botão abaixo para saber mais sobre o assunto!
O problema que ninguém avisa antes
Uma vez eu estava implementando detecção de colisão para um jogo de plataforma 2D simples, com sprites retangulares. Funcionava bem até que um personagem rápido passou por dentro de uma parede fina em um único frame. O problema se chamava tunneling. O objeto estava se movendo tão rápido que a detecção por frame discrete falhava — ele simplesmente "pulava" pela geometria sem nunca ser detectado colidindo. A solução parecida com "aumentar a resolução do teste" não funcionava porque o custo era proibitivo. O workaround foi usar CCD (Continuous Collision Detection) por swept-volume. Em vez de verificar posição em dois instantes discretos, eu calculei a trajetória linear entre frames e encontrei o tempo exato de interseção ao longo desse segmento. Para AABBs, isso reduziu drasticamente e ainda era computacionalmente viável. Para formas complexas, aí sim a coisa entra em território de GJK/EPA e o custo sobe.
Insights que aparecem só depois de quebrar a cabeça
Primeiro: SAT (Separating Axis Theorem) é o método mais útil para polygones convexos em 2D, mas muita gente implementa de forma ingênua testando todos os eixos de ambos os polígonos. Para dois convexos com n e m lados, você só precisa testar os normals de cada aresta de cada forma. Isso já é otimização básica. O que ninguém menciona é que para formas com muitos vértices, o custo de calcular e normalizar todos os eixos pode sair caro se você não armazenar em cache as normals pré-computadas por shape. Segundo: a ordem importa mais do que o óbvio. Executar broad-phase antes de qualquer cálculo de narrow-phase reduz o número de pares examinados de O(n²) para algo perto de O(n log n) com uma BVH bem construída. Testei em cena com 500 objetos estáticos e 200 dinâmicos: sem broad-phase, rodava 8ms por frame. Com BVH, caía para 0,4ms. A diferença não é marginal.
Limitações reais que você vai encontrar
A teoria das colisões, quando aplicada em tempo real, tem gargalos sérios. Simulações com milhares de objetos deformáveis quebram qualquer abordagem baseada em formas fixas. Molhos de partículas, tecidos, fluidos exigem técnicas completamente diferentes — SPH, Verlet integration com restrições, grids Eulerianos. Colisão de mesh convexo contra mesh convexo usando GJK funciona, mas a taxa de repetição necessária para manter estabilidade numérica consome CPU de forma significativa. Outro ponto: jitter em superfícies inclinadas. Objetos em repouso em encostas têm tendência a vibrar porque a resolução numérica do impulse solver gera pequenas oscilações. A solução prática é implementar sleep thresholds — quando a velocidade de um corpo cai abaixo de um limiar por alguns frames, o solver o ignora. Isso resolve 90% dos problemas de instabilidade.
Se você precisa de alta precisão com formas complexas, considere usar uma biblioteca consolidada como Bullet, PhysX ou Godot's built-in physics. Implementar do zero é útil para aprendizado, mas em produção você vai passar por problemas de estabilidade numérica que levaram décadas para serem resolvidos e documentados. O custo de desenvolvimento própria raramente compensa o ganho, a menos que seu problema seja suficientemente específico para justificar.