¿Cómo calculo la respuesta de colisión entre una esfera y un plano?

9

Estoy tratando de crear un juego 3D simple y necesito restringir al jugador dentro de los límites del mundo del juego. Cuando el jugador llega a los lados del mundo, quiero que la nave del jugador rebote ligeramente.

En efecto, estoy tratando de atrapar al jugador dentro de una caja y evitar que escapen por los lados ...

He logrado definir los límites del mundo del juego como una colección de aviones, con normales y distancias desde el origen. El jugador tiene una esfera esférica delimitadora y, siguiendo este sitio web, http://www.gamasutra.com/view/feature/3383/simple_intersection_tests_for_games.php , he logrado detectar colisiones.

Ahora no puedo entender qué hacer cuando se detecta una colisión. Lo mejor que puedo manejar es que el jugador se quede atascado en el avión, lo atraviese o salte repetidamente a un ritmo realmente rápido.

El sentido común me dice que necesito calcular el ángulo reflejado del avión, usando su normalidad y aplicarlo a la velocidad del jugador, sin embargo, creo que primero necesito ver si el jugador ha pasado por el avión, que es el bit que no puedo rutina de ejercicio.

Piku
fuente

Respuestas:

4

Tendrá que aplicar un impulso a su objeto, que es un cambio inmediato en su velocidad. En el mundo real, se aplicaría una fuerza poderosa al objeto en un lapso de tiempo muy corto, invirtiendo su aceleración y haciendo que cambie su velocidad. Sin embargo, dado que estamos trabajando en un mundo discreto, tenemos que hacer un poco de trampa para simular este cambio abrupto de dirección. Para una esfera y un plano, es bastante sencillo. La respuesta de colisión más básica es reflejar la velocidad de la esfera alrededor de la normal del avión, y luego el resultado es la nueva velocidad de la esfera. El pseudocódigo se vería así:

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

A partir de ahí, puede agregar algo de amortiguación (multiplicar por algún coeficiente, como 0.9) para tener en cuenta la energía perdida por el calor o la fricción. Si desea involucrar la velocidad angular (tal vez su esfera está girando), entonces las ecuaciones se vuelven un poco más complicadas.

Para obtener más información, lo remitiré a los artículos de Chris Hecker sobre Rigid Body Dynamics . Si no has oído hablar de Chris Hecker antes, es conocido por la física del juego y por su trabajo en la generación y animación de personajes de procedimiento en Spore.

kevintodisco
fuente
44
Este es esencialmente el camino correcto, sin embargo, calcular el tiempo de impacto (TOI) puede hacer que las cosas sean más precisas a medida que las tasas de cuadros fluctúan o caen. Saber, con base en la velocidad actual, cuánto tiempo hace que ocurrió el impacto puede ayudarlo a calcular un tiempo de impacto, y usar eso puede mover la esfera a su posición en el momento del impacto y ajustar la velocidad desde allí. Después de ajustar la posición y la velocidad desde el punto de impacto, en el momento del impacto, se mueve a lo largo de la nueva velocidad por la cantidad de tiempo que resta para llegar al TOI.
Nic Foster,
OK, esto parece funcionar principalmente, pero es un poco ... extraño. Creo que podría estar haciendo esto en el punto incorrecto de mi código. ¿Debo recorrer todos mis objetos y probar si van a colisionar antes de moverlos (en función de dónde van a estar el próximo cuadro) o moverlos y luego buscar colisiones después?
Piku
@ Piku, no, no detectes si van a chocar. Si ocurre una colisión, recuerde que hay una muy buena posibilidad de que los dos objetos ahora se superpongan mucho más allá de donde habría ocurrido la colisión real. Esencialmente, necesita averiguar dónde se produjo la colisión como si tuviera una velocidad de fotogramas infinita (que no tiene) y mover el objeto de nuevo a la posición donde se habría producido inicialmente la colisión. Si no separa los objetos como este, reaccionará continuamente a la misma colisión y el objeto se atascará.
Jonathan Dickinson
@Piku y para hacer eso calculamos el tiempo en el pasado donde ocurrió la colisión (llamado TOI / tiempo de impacto). Una vez que tengamos eso, podemos usar la velocidad del objeto para moverlo hacia atrás ( distance = speed * timegeneralmente con una pequeña distancia adicional para evitar errores) y luego actualizar su velocidad a cuál es el resultado de la colisión.
Jonathan Dickinson
@Piku tampoco nos damos cuenta de dónde estaremos en el próximo cuadro (nunca he visto eso personalmente), pero, en general, hacemos detección y respuesta de colisión: DESPUÉS de calcular la nueva posición para ESTE cuadro, pero ANTES Aplicamos la nueva posición para ESTE cuadro.
Jonathan Dickinson
1

F = ma, o a = F / m. Calcule el punto de colisión entre la esfera y el plano. Este suele ser el centro de Esfera - radio normal *. Si desea más precisión, calcule cuánto ha penetrado la esfera en el plano y ajuste su cálculo. Esto es en gran medida opcional, por supuesto, a menos que desee una física realmente precisa. Ahora calcule la velocidad relativa a lo largo de la normal. Para un plano estático, esto es: Vball Dot N. Luego multiplique VballDotN por -1 y multiplique por masa. En física en esta etapa también multiplicaría esto por el coeficiente de restitución (factor de rebote). Multiplica este escalar por N y tendrás tu fuerza.

Al ajustar Vball, divida la fuerza por la masa nuevamente y tendrá la aceleración final, así que simplemente agregue esto a la velocidad y tendrá su velocidad final posterior a la colisión.

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 es preciso para las fórmulas de respuesta de colisión. Si quieres aún más precisión, deberás tener en cuenta la fricción, lo que hará que la pelota gire, pero no sé si quieres eso en tu juego. En caso de que lo haga, así es como calcula la fuerza 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)));

Asegúrese de calcular Ft antes de aplicar cualquier efecto lineal, o la fricción no será precisa.

Ian Young
fuente
¿No debería ser la línea 3 vec3 F = -CollisionNormal * (1.0f+Ball.getRestitution())*vDotN;:?
Shital Shah
De hecho sí, me perdí esa parte. Gracias por mencionarlo.
Ian Young
0

Sugeriría calcular primero la distancia desde el avión; y luego cuando la distancia <= al radio lleva a cabo la reacción de colisión.

Luego puede modificar esto para calcular la distancia y si la distancia es menor que el radio (lo que significa que el objeto se superpone) cambie la posición de las bolas y luego realice la reacción de colisión.

Cielo Rojo
fuente