Colisões Formula - PPT - Momento Linear e Colisões PowerPoint Presentation, free download ...
PPT - Momento Linear e Colisões PowerPoint Presentation, free download ...

Por que a maioria dos jogos brega na detecção de colisão

A maioria dos desenvolvedores iniciantes começa com detecção por caixa delimitadora simples e acha que isso é suficiente. Funciona até o primeiro cenário em que dois objetos se movem em diagonal e um atravessa o outro como se não existisse. Isso acontece porque a colisões fórmula básica que você encontra em tutoriais assumes velocidades baixas e frames bem espaçados. Na prática, isso raramente é verdade. Depois de passar uns três anos corrigindo bugs de colisão em projetos reais, cheguei a uma conclusão chatissima: não existe uma única fórmula mágica. O que existe são trade-offs que você aprende a escolher sem sofrimento após errar umas vinte vezes. Vou explicar o que funciona, o que quebra e onde a teoria falha completamente.

O que funciona de verdade: a abordagem por separação de eixos

O SAT (Separating Axis Theorem) ainda é o padrão da indústria para polígonos convexos. A ideia central é simples em papel — projetar dois polígonos em vários eixos e verificar se há interseção em todos eles. Se encontrar pelo menos um eixo onde as projeções não se sobrepõem, os objetos não colidiram. O problema é que a implementação ingênua tem uma pegadinha que ninguém menciona nos tutoriais: Você precisa calcular os vetores normais das arestas, não usar os eixos cartesianos padrão. Testar apenas em X e Y funciona para retângulos alinhados ao mundo, mas assim que seu polígono gira, a coisa desaba. Li isso na teoria, mas só percebi na prática quando um projeto meu tinha um sistema de colisão que parecia funcionar perfeitamente até um inimigo começar a girar. De repente, objetos que claramente estavam se tocando nunca mais disparavam colisão.

A correção é trivial quando você sabe o que procurar. Para cada aresta de cada polígono envolvido, calcule o vetor perpendicular normalizado e use ele como eixo de projeção. O número de eixos testados é igual ao número total de arestas de ambos os polígonos. Para dois retângulos rotacionados, isso significa quatro eixos no total, não dois. Custa quase nada a mais em computação e resolve o problema completamente.

A armadilha que ninguém conta sobre velocidade relativa

Aqui é onde a maioria dos desenvolvedores trava. Detecção frame a frame com testes estáticos funciona para objetos lentos. Quando a velocidade média excede cerca de 30% do menor dimensão do objeto mais pequeno na cena, você precisa de detecção contínua de colisão (CCD). Sem isso, objetos rápidos simplesmente passam uns pelos outros entre frames. Isso é chamado de tunneling e é um dos bugs mais embaraçosos de encontrar porque o jogo parece normal até acontecer algo específico. A colisões fórmula para CCD com esferas é relativamente direta. Você calcula o tempo de impacto ao longo do frame atual usando a velocidade relativa entre os dois objetos e o raio combinado. Se o tempo de impacto estiver entre zero e um, houve colisão dentro daquele frame. O cálculo leva cerca de oito a doze operações de ponto flutuante por par de objetos. Não é muito, mas multiplique por cem objetos na cena e você vê onde o problema aparece.

Meu caso específico: em um jogo de plataforma 2D com projéteis rápidos, o tunneling era insuportável. Projéteis com velocidade superior a oitenta pixels por frame atravessavam paredes às vezes. TesteiSAT com várias iterações, ajuste de timestep, tudo. A solução final foi um híbrido: detecção estática com SAT para objetos lentos e CCD baseado em trajetória linear para objetos rápidos. O custo total aumentou cerca de 15% no perfilador, mas eliminou o bug completamente. Levei duas semanas para implementar e outras duas para perceber que o gargalo não estava na detecção em si, mas na maneira como eu organizava os dados para o teste de interseção.

Octrees versus spatial hashing: quando cada um vence

Isso é algo que quase nenhum tutorial menciona de forma honesta. A abordagem mais recomendada para otimização é um octree (ou quadtree em 2D). A teoria diz que você divide o espaço recursivamente até que cada célula contenha poucos objetos, e aí a detecção de colisão só acontece entre objetos na mesma célula ou células vizinhas. O ganho teórico é enorme: de O(n²) para algo perto de O(n log n). A realidade é diferente. Octrees têm um problema sério com inserção e remoção dinâmica. Quando um objeto se move rápido de uma célula para outra, você precisa remover e reinserir recursivamente. Em cenas com centenas de objetos em movimento constante, esse overhead pode superar o ganho da redução de pares para testar. Meu perfil mostrou isso claramente: o octree era mais lento que uma lista plana de varredura quando havia mais de duzentos objetos em movimento.

👉 Clique no botão abaixo para saber mais sobre o assunto!

O spatial hashing (hashing espacial) é a alternativa que eu recomendo nesses casos. A ideia é dividir o mundo em uma grade fixa de células de tamanho constante e usar uma função de hash para mapear coordenadas para buckets. A vantagem é que a complexidade de inserção e remoção é O(1). Não há recursão, não há redistribuição. O custo é um pouco mais de memória para o array de hash e colisões de hash, mas na prática isso raramente é problema. Configurei várias vezes essa diferença em projetos diferentes. Para cenas estáticas ou com pouca movimentação, octree é superior. Para cenas dinâmicas com muitos objetos em movimento rápido, spatial hashing vence com folga. A regra prática que eu uso agora: se o número médio de objetos por célula no octree não ficar abaixo de cinco, o spatial hashing provavelmente performa melhor. Teste sempre com seus próprios dados. Benchmarks genéricos não aplicam.

Colisões formula e a realidade dos arredondamentos de ponto flutuante

Outro problema que mata projetos novos: erros numéricos. Comparar pontos flutuantes com igualdade direta é uma das piores coisas que você pode fazer. Duas coordenadas que deveriam ser exatamente iguais podem diferir em 0.000001 devido à acumulação de erros de arredondamento durante cálculos sucessivos. Isso causa jitter visual, objetos que vibram quando encostados um no outro, e colisões que não disparam quando deveriam. A correção padrão é usar epsilon comparativo. Em vez de verificar se a == b, você verifica se |a - b|

epsilon. O valor de epsilon depende da escala do seu mundo. Para um jogo 2D com coordenadas na casa das centenas, epsilon de 1e-6 funciona. Para simulações com coordenadas astronômicas, você precisa de algo na casa de 1e-9 ou menos. Não adianta copiar valores de outros projetos. Meça a escala das suas coordenadas e ajuste.

Tive um problema assim em um simulador orbital onde planetas aparentemente se tocavam mas o sistema de colisão não registrava. A causa foi epsilon zero em comparações diretas com valores de coordenada na escala de milhões. Reduzir o epsilon para 1e-7 resolveu. Mas aí novos problemas apareceram: jitter em órbitas baixas. A solução final foi uma combinação de epsilon adaptativo — maior para objetos distantes, menor para próximos — junto com uma correção pós-cálculo que empurra objetos levemente para fora de sobreposição quando detectada.

Quando a colisões fórmula não é a resposta

Existe um cenário onde toda a matemática acima se torna irrelevante: quando você está lidando com corpos rígidos que precisam responder fisicamente a colisões, não apenas detectá-las. Detecção de colisão é apenas a primeira metade do problema. A segunda metade é resolução — calcular forças, impulsos, respostas. E aqui é onde muitos sistemas começam a quebrar de forma sutil e difícil de debugar. Resolvedores de restrições iterativas, como o Algoritmo de Constraint Solver usado em motores como Bullet ou PhysX, podem levar de dez a cinquenta iterações por frame para convergir. Cada iteração melhora a precisão, mas o ganho é marginal após as primeiras dez. Se seu jogo tem apenas sessenta frames por segundo e você permite vinte iterações, está gastando um terço do tempo do frame apenas resolvendo restrições. Isso é comum em simulações com muitas peças empilhadas ou correntes longas.

Se o seu projeto não precisa de física realista — um jogo de plataforma 2D, um RPG com combate por turno — não implemente um motor de física completo. Use detecção de colisão pura com respostas manuais. Você terá controle total, performance previsível e bugs muito mais fáceis de rastrear. Motores de física completos são ferramentas poderosas, mas introduzem complexidade que muitos projetos não justificam. Eu já vi três colegas perderem semanas debugando problemas que eram simples ajustes manuais de resposta a colisão. Resumo prático: teste SAT para polígonos convexos com eixos normais das arestas, use CCD para objetos rápidos, prefira spatial hashing a octrees em cenas dinâmicas, nunca compare floats diretamente e só use resolver de física quando realmente precisar. Qualquer coisa além disso é otimização prematura ou complexidade desnecessária.