Detecção de colisão com curvas

12

Estou trabalhando em um jogo 2D no qual gostaria de fazer uma detecção de colisão entre um círculo em movimento e algum tipo de curva estática (talvez curvas de Bezier).

Atualmente, meu jogo apresenta apenas linhas retas como a geometria estática e estou fazendo a detecção de colisão calculando a distância do círculo às linhas e projetando o círculo para fora da linha, caso a distância seja menor que o raio dos círculos.

Como posso fazer esse tipo de detecção de colisão de maneira relativamente direta? Eu sei, por exemplo, que o Box2D possui detecção de colisão com curvas de Bezier. Não preciso de um mecanismo de detecção de colisão com todos os recursos, apenas algo que possa fazer o que descrevi.


ATUALIZAÇÃO: Muito obrigado pelas ótimas respostas! Vou ter que ler as curvas de Bezier para entender completamente o método que você descreveu. Então eu voltarei para você.

paldepind
fonte

Respostas:

6

29/09/2012 - 23:20

Criei um repositório git aqui: https://github.com/ArthurWulfWhite/Bezier-Distance/

Você pode baixar os arquivos de origem como um zip a partir daí. Ele também inclui uma demonstração que você pode compilar usando o FlashDevelop. Para usar a demonstração, abra o projeto no Flash Develop e clique em 'Test Project'. Durante a execução da demonstração, clique no LMB para selecionar aleatoriamente uma nova curva de Bezier e um novo círculo.

Boa sorte!

É difícil ver o link do zip - basta usar Ctrl + F e digite zip. Essa fonte representa algumas semanas de pesquisa e programação, espero que você goste.


Se você planeja dividir o bezier recursivamente em segmentos e verificar colisões com eles, sugiro fazer uma matriz de 100.100 (grade) e colocar cada segmento nos quatro quadrados mais próximos, para que você só precise verificar colisões com 4 / 10.000 do segmenta cada quadro.

Eu acho que você se beneficiará do box2d tanto como programador quanto como criador de jogos, já que existem muitos pequenos obstáculos ocultos na criação de um mecanismo de física 'simples' que faz o movimento parecer um pouco esburacado e menos fluido do que poderia ser.

Antiga resposta: O caminho puro.

Você pode realmente ver se um círculo está colidindo com uma curva de Bezier, verificando a distância entre a distância entre o centro do círculo e o ponto mais próximo da curva.

A equação para a distância (em geral)

explicado:

Equação de Bezier:

q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

Isso pode ser resumido em (com alguma álgebra) - vou omitir (x, y) para facilitar a leitura (eles ainda são pontos, não um número)

q(t) = (start -2 * cont + end) t^2 + (-2 * start + 2 * control) + start

A distância do ponto (x, y) é:

sqrt ((q(t).x - point.x)^2 + (q(t).y - point.y)^2)

Para encontrar o ponto mais próximo do bezier da bola, você precisa derivar e encontrar todos os pontos em que a derivada é igual a zero (as raízes). É um polinômio de terceiro grau, portanto, você pode usar uma fórmula fechada, mas pode não ser confiável, pois a precisão das frações representadas pelo ponto flutuante do computador pode não ser suficiente. É muito melhor usar Newton ou algo dessa natureza.

A derivada para a qual você precisa encontrar as raízes é:

Supondo: a = início b = controle c = final d = ponto central do cirlce

Derivada usando wolfram alfa

A parte complicada é multiplicar esses pontos, você precisa usar o produto com pontos.

Se você preferir, eu tenho o código para isso e posso compartilhá-lo aqui na forma de uma função que simplesmente retorna um booleano se houver uma colisão ou não e um ângulo de colisão. Alguns problemas podem surgir na implementação ingênua de um mecanismo de colisão como este, por exemplo, uma bola em movimento rápido pode ficar presa entre duas curvas.

Eu recomendo evitá-lo por enquanto, basta somar os coeficientes para o eixo xe para o eixo y e adicioná-los.

Use qualquer método confiável que você possa escolher como Newton para encontrar as raízes, verifique a distância dos pontos da raiz no bezier, 0 <= t <= 1 até o centro do círculo e verifique a distância para as duas extremidades do bezier (início e final) até o centro do círculo, o que estiver mais próximo, informará se há uma colisão.

Se o raio for menor que a distância mínima, haverá uma colisão.

O ângulo é aproximadamente aquele entre o centro do círculo e o ponto mais próximo do bezier.

Dito isto, se você realmente deseja fazer um jogo com a física de colisão, sugiro que você repita o processo

    q(t) = (1-t) * ((1-t) * start.(x,y) + t * control.(x,y)) + t*(t * control.(x,y) + (1 - t) * end.(x,y))

Divida cada peça no meio recursivamente até que seja pequena o suficiente, digamos 10 pixels ou menos, então construa o bezier aproximadamente a partir de caixas e use o Box2d para a física, pois é possível que escrever todo esse código de detecção de colisão seja ótimo tempo que não melhora muito a jogabilidade. O uso do Box2d provou-se em inúmeros projetos no passado.

AturSams
fonte
O método que você descreve para calcular o ponto mais curto da curva é exatamente o que estou usando atualmente com linhas em vez de curvas. Mas fazer o mesmo para curvas com o método que você explica parece um pouco complicado demais. O que, pelo que entendi, também é o que você pensa. E em relação ao Box2D. Estou certo de que é um ótimo trabalho. Mas a física do meu jogo é honestamente muito simples e, portanto, decidi que um mecanismo de física completo é um exagero.
paldepind
Quantos objetos estão no seu jogo? Quantos podem colidir entre si? Às vezes, o uso de um mecanismo de física pode trazer grandes benefícios, pois calcula com precisão o tempo de colisão. (causa quadros são discretos e colisões são real (não acontecem exatamente quando você processar um frame)
AturSams
Muitas vezes, além dos desafios inesperados ao implementar algo novo e a beleza de usar uma API física em 2D, é que é como usar qualquer linguagem de programação, não exige esforço especial da sua parte, além de investir algumas horas para aprendê-la e os resultados são muito satisfatórios.
AturSams 27/09/12
Adicionei mais alguns detalhes agora, boa sorte. :)
AturSams
Estou criando um jogo simples como o Elasto Mania. Apenas três círculos em movimento e geometria estática. Todo o mecanismo está pronto e funciona muito bem. A única coisa que resta é permitir curvas que estou prestes a resolver, graças à ajuda nesta resposta :) Fique à vontade para postar o código que você mencionou. Quão apropriado você acha que seria usar na vida real? Melhor do que converter o bezier em pequenas linhas?
paldepind
7

Para fazer isso, eu:

  • Divida a curva de bezier em segmentos de linha de cortes e armazene-os.

  • Coloque todos esses segmentos em uma caixa delimitadora alinhada ao eixo para toda a curva.

Detecção de colisão:

1) verifique se a esfera está dentro da caixa delimitadora principal. se não, nenhuma colisão.

2) caso contrário, verifique se algum dos segmentos individuais calculados acima colide com a esfera. Consulte o artigo Interseção linha-esfera da Wikipedia .

EDIT: se você precisar de alta precisão e desejar um bom desempenho, também poderá criar uma caixa delimitadora principal para toda a curva e subdividir a curva em dois segmentos (por exemplo: [0.0 - 0.5]e [0.5 - 1.0]). Crie uma caixa de agrupamento para cada um deles e, em seguida, subdivida cada um desses segmentos em dois segmentos (fornecendo [0 - 0.25], [0.25 - 0.5]e [0.5 - 0.75], [0.75 - 1.0]). Continue assim até atingir precisão suficiente. no final, você terá uma binary treecaixa delimitadora com a caixa delimitadora da curva principal nos segmentos raiz e linha nas folhas. pesquisar na árvore fornecerá você em O(log n)vez de O(n)(onde n= número de segmentos de linha para a curva)

tigrou
fonte
Esta solução faz sentido para mim e é definitivamente a mais fácil de entender e eu posso me contentar com ela. Mas estou curioso para saber se existe uma opção mais "pura".
paldepind
5

A interseção entre uma linha e uma curva de Bezier é alcançada matematicamente subdividindo a curva. Isso significa confiar na propriedade convexa do casco da curva e dividi-la em arcos menores com diferentes polígonos de controle, de maneira semelhante a dividir e impera.

Este artigo abrange até certo ponto: http://students.cs.byu.edu/~tom/557/text/cic.pdf .

A parte boa é que o algoritmo funciona com qualquer linha, basta aplicar uma transformação rígida à curva para que você possa considerar sua linha de destino como paralela ao eixo Ox.

Da mesma forma, você pode verificar o círculo e o polígono de cada arco bezier quando subdividir um arco de Bezier em dois sub-arcos. O círculo deve cruzar o polígono de controle de um arco para que um teste de curva a círculo faça sentido.

Gabriel Conrad
fonte
Ainda não li o artigo. Mas como faço para ir da interseção entre uma linha e uma curva de Bezier até a interseção entre um círculo e uma Bezier? Verificar a colisão contra um círculo e um polígono parece um pouco complicado demais para mim.
paldepind