Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Programming

Cómo crear un motor de física 2D personalizado: Fricción, escena y tabla de salto

by
Read Time:13 minsLanguages:
This post is part of a series called How to Create a Custom Physics Engine.
How to Create a Custom 2D Physics Engine: The Core Engine
How to Create a Custom 2D Physics Engine: Oriented Rigid Bodies

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

En los dos primeros tutoriales de esta serie, cubri los temas de Resolución de Impulsos y Arquitectura Central. Ahora es el momento de añadir algunos de los toques finales a nuestro motor de física basado en impulsos 2D.

Los temas que veremos en este artículo son:

  •      Fricción
  •     Escena
  •      Tabla de salto de colisión

Le recomiendo leer en los dos artículos anteriores de la serie antes de tratar de abordar este. En este artículo se basa alguna información clave de los artículos anteriores.

Nota: Aunque este tutorial está escrito usando C ++, debería ser capaz de usar las mismas técnicas y conceptos en casi cualquier entorno de desarrollo de juegos.


Video Demo

Aquí está una demostración rápida de lo que estamos trabajando hacia en esta parte:


Fricción

La fricción es una parte de la resolución de la colisión. La fricción aplica siempre una fuerza sobre los objetos en la dirección opuesta al movimiento en el que deben viajar.

En la vida real, la fricción es una interacción increíblemente compleja entre diferentes sustancias, y con el fin de modelarla, se hacen amplias suposiciones y aproximaciones. Estas suposiciones están implícitas dentro de la matemática y suelen ser algo así como "la fricción puede ser aproximada por un solo vector" - similarmente a cómo la dinámica del cuerpo rígido simula las interacciones de la vida real asumiendo cuerpos con una densidad uniforme que no puede deformarse.

Echa un vistazo a la demostración de vídeo del primer artículo de esta serie:

Las interacciones entre los cuerpos son bastante interesantes, y el rebote durante las colisiones se siente realista. Sin embargo, una vez que los objetos aterrizan en la plataforma sólida, que sólo tipo de todos los presionar lejos y deriva de los bordes de la pantalla. Esto se debe a una falta de simulación de fricción.

¿Impulsos, otra vez?

Como se debe recordar del primer artículo de esta serie, un valor particular, j, representó la magnitud de un impulso requerido para separar la penetración de dos objetos durante una colisión. Esta magnitud se puede referir como jnormal o jN como se utiliza para modificar la velocidad a lo largo de la colisión normal.

La incorporación de una respuesta de fricción implica el cálculo de otra magnitud, denominada jtangent o jT. La fricción se modelará como un impulso. Esta magnitud modificará la velocidad de un objeto a lo largo del vector tangente negativo de la colisión, o en otras palabras a lo largo del vector de fricción. En dos dimensiones, la resolución de este vector de fricción es un problema solucionable, pero en 3D el problema se vuelve mucho más complejo.

La fricción es bastante simple, y podemos hacer uso de nuestra ecuación anterior para j, excepto que reemplazaremos todas las instancias de la normal n por un vector tangente t.

\[ Ecuacion 1:\\
j = \frac{-(1 + e)(V^{B}-V^{A})\cdot n)}
{\frac{1}{masa^A} + \frac{1}{masa^B}}\]

Reemplazar n por t:

\[ Ecuacion 2:\\
j = \frac{-(1 + e)((V^{B}-V^{A})\cdot t)}
{\frac{1}{masa^A} + \frac{1}{masa^B}}\]

Aunque sólo una instancia de n se reemplazó por t en esta ecuación, una vez que se introducen las rotaciones, se deben sustituir algunas instancias más que la única en el numerador de la Ecuación 2.

Ahora surge la cuestión de cómo calcular t. El vector tangente es un vector perpendicular a la colisión normal que se enfrenta más hacia lo normal. Esto puede sonar confuso - no te preocupes, ¡tengo un diagrama!

A continuación se puede ver el vector tangente perpendicular a la normal. El vector tangente puede apuntar a la izquierda oa la derecha. A la izquierda estaría "más lejos" de la velocidad relativa. Sin embargo, se define como la perpendicular a la normal que está apuntando "más hacia" la velocidad relativa.

Vectors of various types within the timeframe of a collision of rigid bodies.Vectors of various types within the timeframe of a collision of rigid bodies.Vectors of various types within the timeframe of a collision of rigid bodies.
Vectores de varios tipos dentro del marco temporal de una colisión de cuerpos rígidos.

Como se ha dicho brevemente anteriormente, la fricción será un vector frente al vector tangente. Esto significa que la dirección en la que se aplica la fricción se puede calcular directamente, ya que el vector normal se encontró durante la detección de la colisión.

Sabiendo esto, el vector tangente es (donde n es la colisión normal):

\[ V^R = V^{B}-V^{A} \\
t = V^R - (V^R \cdot n) * n \]

Todo lo que queda por resolver para jt, la magnitud de la fricción, es calcular el valor directamente usando las ecuaciones anteriores. Hay algunas piezas muy difíciles después de que este valor se calcula que se cubrirá en breve, por lo que esta no es la última cosa necesaria en nuestro solucionador de colisión:

El código anterior sigue la ecuación 2 directamente. Una vez más, es importante darse cuenta de que el vector de fricción apunta en la dirección opuesta de nuestro vector tangente, y como tal debemos aplicar un signo negativo cuando punteamos la velocidad relativa a lo largo de la tangente para resolver la velocidad relativa a lo largo del vector tangente. Este signo negativo vuelca la velocidad tangente y de repente señala en la dirección en que la fricción debe ser aproximada como.

Ley de Coulomb

La ley de Coulomb es la parte de la simulación de fricción con la que la mayoría de los programadores tienen problemas. Yo mismo tenía que hacer un poco de estudio para averiguar la forma correcta de modelarla. El truco es que la ley de Coulomb es una desigualdad.

Los estados de fricción de Coulomb:

\[ Ecuacion 3: \\
F_f <= \mu F_n \]

En otras palabras, la fuerza de fricción es siempre menor o igual a la fuerza normal multiplicada por alguna constante μ (cuyo valor depende de los materiales de los objetos).

La fuerza normal es simplemente nuestra magnitud j antigua multiplicada por la colisión normal. Así que si nuestro jt resuelto (que representa la fuerza de fricción) es menor que μ veces la fuerza normal, entonces podemos usar nuestra magnitud jt como fricción. Si no es así, entonces debemos usar nuestros tiempos de fuerza normal μ en su lugar. Este caso "else" es una forma de fijar nuestra fricción por debajo de un valor máximo, siendo el máximo las fuerzas normales μ.

Todo el punto de la ley de Coulomb es realizar este procedimiento de sujeción. Esta fijación resulta ser la parte más difícil de la simulación de fricción para la resolución basada en impulsos para encontrar documentación en cualquier lugar - ¡hasta ahora, al menos! La mayoría de los libros blancos que pude encontrar sobre el tema o bien se saltaba la fricción por completo, o se detuvo en corto e implementado procedimientos de fijación inadecuados (o inexistentes). Esperemos que por ahora usted tiene un aprecio por la comprensión de que conseguir esta parte correcta es importante.

Deja apenas el plato hacia fuera la sujeción todo de una vez antes de explicar cualquier cosa. Este bloque de código siguiente es el ejemplo de código anterior con el procedimiento de sujeción terminado y la aplicación de impulso de fricción todo junto:

Decidí usar esta fórmula para resolver los coeficientes de fricción entre dos cuerpos, dado un coeficiente para cada cuerpo:

\[ Ecuacion 4: \\
Fricción = \sqrt[] {Fricción ^ 2_A + Fricción ^ 2_B} \]

En realidad vi a alguien hacer esto en su propio motor de física, y me gustó el resultado. Un promedio de los dos valores funcionaría perfectamente bien para deshacerse del uso de la raíz cuadrada. En realidad, cualquier forma de recoger el coeficiente de fricción funcionará; esto es justo lo que prefiero. Otra opción sería utilizar una tabla de búsqueda donde el tipo de cada cuerpo se utiliza como un índice en una tabla 2D.

Es importante que el valor absoluto de jt se utilice en la comparación, ya que la comparación teóricamente está sujetando magnitudes brutas por debajo de cierto umbral. Puesto que j es siempre positivo, se debe invertir para representar un vector de fricción adecuado, en el caso de que se utilice fricción dinámica.

Fricción estática y dinámica

En el fragmento de código pasado fricción estática y dinámica se introdujeron ¡sin ninguna explicación! Voy a dedicar toda esta sección a explicar la diferencia y la necesidad de estos dos tipos de valores.

Algo interesante sucede con la fricción: requiere una "energía de activación" para que los objetos comiencen a moverse cuando están en reposo completo. Cuando dos objetos están descansando uno sobre otro en la vida real, toma una cantidad justa de energía para empujar en uno y conseguirlo que se mueva. Sin embargo, una vez que se obtiene algo deslizamiento a menudo es más fácil mantenerlo deslizándose a partir de entonces.

Esto se debe a cómo funciona la fricción a nivel microscópico. Otra imagen ayuda aquí:

Microscopic view of what causes energy of activation due to friction.Microscopic view of what causes energy of activation due to friction.Microscopic view of what causes energy of activation due to friction.
Vista microscópica de lo que causa la energía de activación debido a la fricción.

Como puede ver, las deformidades pequeñas entre las superficies son realmente el principal culpable que crea fricción en el primer lugar. Cuando un objeto está en reposo en otro, las deformidades microscópicas descansan entre los objetos, enclavándose. Estos deben ser rotos o separados para que los objetos se deslizen uno contra el otro.

Necesitamos una manera de modelar esto dentro de nuestro motor. Una solución simple consiste en proporcionar a cada tipo de material dos valores de fricción: uno para la estática y otro para la dinámica.

La fricción estática se utiliza para sujetar nuestra magnitud jt. Si la magnitud jt resuelta es suficientemente baja (por debajo de nuestro umbral), entonces podemos suponer que el objeto está en reposo, o casi como reposo y usar todo el jt como un impulso.

En el flipside, si nuestra resolución jt está por encima del umbral, se puede suponer que el objeto ya ha roto la "energía de activación", y en tal situación un impulso de fricción inferior se utiliza, que se representa por un menor coeficiente de fricción y un cálculo de impulso ligeramente diferente.


Escena

Asumiendo que no se saltó ninguna parte de la sección de Fricción, ¡bien hecho! Has completado la parte más difícil de toda esta serie (en mi opinión).

La clase Scene actúa como un contenedor para todo lo que involucra un escenario de simulación física. Llama y utiliza los resultados de cualquier fase amplia, contiene todos los cuerpos rígidos, ejecuta verificaciones de colisión y llama a resolución. También integra todos los objetos vivos. La escena también interactúa con el usuario (como en el programador que usa el motor de física).

A continuación se muestra un ejemplo de cómo puede verse una estructura de escena:

No hay nada particularmente complejo en la clase de escena Scene. La idea es permitir que el usuario agregue y quite cuerpos rígidos fácilmente. El BodyDef es una estructura que contiene toda la información sobre un cuerpo rígido y puede utilizarse para permitir al usuario insertar valores como una especie de estructura de configuración.

La otra función importante es Step(). Esta función realiza una sola ronda de comprobaciones de colisión, resolución e integración. Esto debe ser llamado desde dentro del bucle de temporización delineado en el segundo artículo de esta serie.

Consultar un punto o AABB implica comprobar para ver qué objetos chocan realmente con un puntero o AABB dentro de la escena. Esto hace que sea fácil para la lógica relacionada con el juego ver cómo se colocan las cosas en el mundo.


Tabla de salto

Necesitamos una manera fácil de seleccionar qué función de colisión debe llamarse, en función del tipo de dos objetos diferentes.

En C ++ hay dos maneras principales de las cuales soy consciente: el despacho doble y una tabla de salto 2D. En mis propias pruebas personales encontré la tabla de salto 2D a superior, así que entraré en detalles sobre cómo implementar eso. Si usted está planeando usar un lenguaje que no sea C o C ++, estoy seguro de que una matriz de funciones o objetos de functor se pueden construir de forma similar a una tabla de punteros de funciones (que es otra razón por la que decidí hablar de tablas de salto en lugar de otras opciones que son más específicos de C + +).

Una tabla de salto en C o C ++ es una tabla de punteros de funciones. Los índices que representan nombres o constantes arbitrarios se utilizan para indexar en la tabla y llamar a una función específica. El uso podría ser algo como esto para una tabla de salto 1D:

El código anterior imita lo que el lenguaje C ++ implementa con llamadas a funciones virtuales y herencia virtual function calls and inheritance. Sin embargo, C ++ sólo implementa llamadas virtuales unidimensionales. Una tabla 2D se puede construir a mano.

Aquí hay algunos psuedocode para una tabla de saltos 2D para llamar a las rutinas de colisión:

¡Y ahí lo tenemos! Los tipos reales de cada colisionador se pueden utilizar para indexar en una matriz 2D y elegir una función para resolver la colisión.

Tenga en cuenta, sin embargo, que AABBvsCircle y CirclevsAABB son casi duplicados. ¡Esto es necesario! Lo normal debe voltearse para una de estas dos funciones, y esa es la única diferencia entre ellas. Esto permite una resolución de colisión consistente, independientemente de la combinación de objetos que se puedan resolver.


Conclusión

Por ahora hemos cubierto una gran cantidad de temas en la creación de una costumbre cuerpo rígido física motor totalmente desde cero! La resolución de colisiones, la fricción y la arquitectura del motor son todos los temas que han sido cubiertos hasta el momento. Un motor de física completamente exitoso adecuado para muchos juegos de dos dimensiones de producción-nivel se puede construir con el conocimiento presentado en esta serie hasta ahora.

Mirando hacia adelante en el futuro, pienso escribir un artículo más dedicado enteramente a una característica muy deseable: rotación y orientación. Objetos orientados son muy atractivos para ver interactúan entre sí, y son la pieza final que nuestro motor de física personalizado requiere.

Resolución de rotación resulta ser bastante simple, aunque la detección de colisiones tiene un éxito en la complejidad. Buena suerte hasta la próxima vez, y ¡por favor haga preguntas o publique comentarios abajo!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.