Como calculo a resposta de colisão entre uma esfera e um plano?

9

Estou tentando criar um jogo 3D simples e preciso restringir o jogador dentro dos limites do mundo do jogo. Quando o jogador atinge os lados do mundo, quero que a nave do jogador ricocheteie um pouco.

Na verdade, estou tentando prender o jogador dentro de uma caixa e impedi-lo de escapar pelos lados ...

Consegui definir os limites do mundo do jogo como uma coleção de aviões, com normais e distâncias da origem. O jogador tem uma esfera delimitadora esférica e, seguindo este site http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php , consegui detectar colisões.

Agora não consigo entender o que fazer quando uma colisão é detectada. O melhor que consigo controlar é o jogador ficar preso no avião, atravessá-lo diretamente ou saltar repetidamente em um ritmo muito rápido.

O senso comum me diz que preciso calcular o ângulo refletido do avião, usando o normal e aplicá-lo à velocidade do jogador, no entanto, acho que preciso primeiro ver se o jogador passou pelo avião, o que não é possível. exercite-se.

Piku
fonte

Respostas:

4

Você terá que aplicar um impulso ao seu objeto, que é uma mudança imediata em sua velocidade. No mundo real, uma força poderosa seria aplicada ao objeto em um curto espaço de tempo, revertendo sua aceleração e fazendo com que sua velocidade mudasse. No entanto, como estamos trabalhando em um mundo discreto, precisamos trapacear um pouco para simular essa mudança abrupta de direção. Para uma esfera e um avião, é bem direto. A resposta de colisão mais básica é refletir a velocidade da esfera em torno da normal do plano e, em seguida, o resultado é a nova velocidade da esfera. O pseudocódigo seria algo parecido com isto:

reflected = 2 * plane.normal * (plane.normal * sphere.velocity)
sphere.velocity -= reflected

A partir daí, você pode adicionar um pouco de amortecimento (multiplicar por algum coeficiente, como 0,9) para contabilizar a energia perdida pelo calor ou atrito. Se você deseja envolver a velocidade angular (talvez sua esfera esteja girando), as equações ficam um pouco mais complicadas.

Para mais informações, encaminhá-lo-ei aos artigos de Chris Hecker sobre Rigid Body Dynamics . Se você nunca ouviu falar de Chris Hecker antes, ele é bem conhecido pela física dos jogos, bem como por seu trabalho na geração de personagens processuais e animação em Spore.

kevintodisco
fonte
4
Este é essencialmente o caminho certo a seguir, no entanto, calcular o tempo de impacto (TOI) pode tornar as coisas mais precisas à medida que as taxas de quadros flutuam ou caem. Saber, com base na velocidade atual, há quanto tempo o impacto ocorreu pode ajudá-lo a calcular um tempo de impacto e, usando isso, você pode mover a esfera de volta à sua posição no momento do impacto e ajustar a velocidade a partir daí. Depois de ajustar a posição e a velocidade do ponto de impacto, no momento do impacto, você move a nova velocidade pela quantidade de tempo subtraída para chegar ao TOI.
Nic Foster
OK, isso parece funcionar principalmente, mas é um pouco ... estranho. Eu acho que posso estar fazendo isso no ponto errado no meu código. Devo percorrer todos os meus objetos e testar se eles vão colidir antes de movê-los (com base em onde eles serão o próximo quadro) ou movê-los e depois testar as colisões depois?
Piku
@Piku, não, não detecte se eles colidirão. Se ocorrer uma colisão, lembre-se de que há uma chance muito boa de os dois objetos estarem sobrepostos muito além de onde a colisão real teria ocorrido. Essencialmente, você precisa descobrir onde a colisão ocorreu como se tivesse uma taxa de quadros infinita (o que não ocorre) e mover o objeto de volta à posição em que a colisão teria ocorrido inicialmente. Se você não separar os objetos como este, reagirá continuamente à mesma colisão e o objeto ficará preso.
Jonathan Dickinson
@Piku e, para isso, descobrimos o tempo no qual a colisão ocorreu (chamada TOI / tempo de impacto). Quando tivermos isso, podemos usar a velocidade do objeto para movê-lo de volta ( distance = speed * timegeralmente com uma pequena distância extra para evitar erros) e, em seguida, atualizar sua velocidade para qual é o resultado da colisão.
Jonathan Dickinson
@Piku também não descobrimos onde estaremos no próximo quadro (nunca vi isso pessoalmente), mas geralmente detectamos e respondemos a colisões: DEPOIS calculamos a nova posição para ESTE quadro, mas ANTES aplicamos a nova posição para ESTE quadro.
Jonathan Dickinson
1

F = ma ou a = F / m. Calcule o ponto de colisão entre a esfera e o plano. Geralmente é o centro da esfera - raio normal *. Se você quiser mais precisão, calcule até que ponto a esfera penetrou no plano e ajuste seu cálculo. Isso é amplamente opcional, é claro, a menos que você queira uma física realmente precisa. Agora calcule a velocidade relativa ao longo do normal. Para um plano estático, este é: Vball Dot N. Em seguida, multiplique VballDotN por -1 e multiplique por massa. Na física, nesta fase, você também multiplicaria isso pelo coeficiente de restituição (fator de rejeição). Multiplique esse escalar por N e você terá sua força.

Ao ajustar o Vball, divida a força pela massa novamente e você terá a aceleração final; basta adicionar isso à velocidade e sua velocidade final após a colisão.

vec3 Vrel = Ball.getVelocity();
float vDotN = Vrel.Dot(CollisionNormal);
vec3 F = -(1.0f+Ball.getRestitution())*vDotN;
F*=Ball.getMass();
Ball.accelerate(F/Ball.getMass());

Este método é preciso para as fórmulas de resposta a colisões. Se você quiser ainda mais precisão, leve em consideração o atrito, o que fará com que a bola gire, mas não sei se você quer isso no seu jogo. Nesse caso, é assim que você calcula a força tangencial:

vec3 Ft = -(Ball.getvelocity()+(vDotN*CollisionNormal));
Ft*=Ball.getKineticFriction()+Wall.getKineticFriction(); //you could fudge these numbers
Ft*=Ball.getMass();
vec3 vec2Centre = Ball.getPosition()-ContactPoint;
vec3 Torque = cross(vec2Centre,Ft);
Ball.AngularAccelerate(Torque/Ball.getMomentofInertia(glm::normalize(Torque)));

Certifique-se de calcular Ft antes de aplicar efeitos lineares, ou o atrito não será preciso.

Ian Young
fonte
A linha 3 não deve ser vec3 F = -CollisionNormal * (1.0f+Ball.getRestitution())*vDotN;:?
Shital Shah
De fato, sim, eu perdi essa parte. Obrigado por apontar isso.
23617 Ian Young
0

Eu sugeriria calcular primeiro a distância do avião; e então quando a distância <= ao raio realiza a reação de colisão.

Você pode alterar isso para calcular a distância e se a distância for menor que o raio (o que significa que o objeto está sobreposto) muda a posição das bolas e, em seguida, executa a reação de colisão.

Céu vermelho
fonte