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
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.
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á umabinary tree
caixa delimitadora com a caixa delimitadora da curva principal nos segmentos raiz e linha nas folhas. pesquisar na árvore fornecerá você emO(log n)
vez deO(n)
(onden
= número de segmentos de linha para a curva)fonte
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.
fonte