Skip to main content

Físicas de Roblox: Construyendo un mejor sistema de suspensión

julio

30, 2020

by kleptonaut


Tecnología

Hablemos de «suspensión» en un motor de física.

El propósito de un sistema de suspensión es suspender el trabajo en los cuerpos que no se están moviendo y reactivarlo cuando se vuelvan a mover. El desafío es elegir la mejor heurística para la transición entre estos estados. Un enfoque común en otros motores es monitorear la velocidad de un cuerpo, y cuando la velocidad está por debajo de un determinado umbral por varios fotogramas, el cuerpo se inactiva. Puede activarse de nuevo chocando con un cuerpo activo o tal vez con otros eventos. Hasta hace poco, Roblox también usaba una variación de este método.

Algunos problemas conocidos de este método son las interacciones físicas en las que un cuerpo puede tener una velocidad cercana a cero durante unos pocos fotogramas, sin haber terminado su trayectoria actual. Podemos pensar en un caso simple en el que una bola roda por un plano inclinado y es impulsada por su propio movimiento. Finalmente a la bola se le acaba su energía cinética y alcanza la cima de su trayectoria (la ha convertido toda en energía potencial al empujarla hacia arriba de la pendiente), y vuelve a rodar por la pendiente. Sin embargo, si es lo suficientemente lenta, el movimiento de la bola se suspenderá en la pendiente en el ápice de su trayectoria antes de que pueda rodar de nuevo.

Si un desarrollador se topa con este tipo de caso límite en su juego, el motor a menudo proporciona una solución. Podría ajustar los umbrales de cuándo algo debe inactivarse, sacrificando el rendimiento en todas las interacciones. O puede desactivar la suspensión en un objeto en específico. Tal vez solo quieran desactivar un objeto por un periodo de tiempo definido. De cualquier manera, depende del desarrollador descubrir y trabajar alrededor de cualquier caso de suspensión que encuentre. En Roblox, queríamos encontrar una manera de evitar todos estos casos, reduciendo la carga de trabajo de los desarrolladores. El objetivo de este sistema de suspensión es quitarle ayudar a los desarrolladores.

En el motor de Roblox, los cuerpos rígidos se llaman «montajes». Estos son una colección de uno o más cuerpos primitivos rígidamente conectados, o «partes», pero este es el objeto que está activo o suspendido.

Los montajes pueden estar conectados a otros ensamblajes a través de uniones simuladas, como bisagras o cuerdas, que forman lo que se llama «mecanismo». Esto es importante porque los montajes pueden inferir su estado de suspensión basándose en el estado de otros ensamblajes en su mecanismo, del cual hablaremos más adelante.

Al igual que otros motores, Roblox ha estado usando una heurística tradicional para determinar cuando los cuerpos se suspenden. Cada montaje guarda un «historial» de sus posiciones y orientaciones pasadas, y mientras la desviación de la posición promedio se mantenga dentro del umbral, el montaje se mantiene activo. Si la velocidad está por debajo de un valor establecido para unos pocos fotogramas, el montaje se suspende. Me referiré a este valor como el umbral de velocidad.

Los montajes activos se marcan en rojo. Los montajes desactivos se marcan en negro. Los montajes estáticos no se marcan.

El ejemplo anterior ilustra la bola atascada en una rampa como aquella descrita anteriormente. Aquí, una pieza giratoria empuja el cilindro por una subida, manteniendo el cilindro inmóvil durante un tiempo. La velocidad del cilindro está por debajo del umbral de velocidad durante un tiempo suficiente para suspenderse. Esto podría ilustrarse con diversos ejemplos: como una pelota que rebota en baja gravedad; una que se suspende en el aire; un bloque alto que se inclina en su lugar; o que se desactiva antes de tocar el suelo.

Nuestro objetivo era ver si había mejor información que el cambio de posición de un montaje en los últimos fotogramas para determinar si debía suspenderse. ¿Por qué no seguir una de la leyes básica del movimiento? «Un objeto o permanece en reposo o continúa moviéndose a una velocidad constante, a menos que se accione por una fuerza.» Sabemos que la gravedad actúa sobre el cilindro y este debería seguir rodando. Queremos que el objeto esté activo cuando se mueva a una velocidad constante, o que tenga una fuerza desequilibrada que actúa sobre él. Si se presenta uno de estos casos, el cuerpo debe permanecer activo. Podemos tomar los últimos valores de impulso en un ensamblaje del solucionador de física y comprobarlos contra un umbral de impulso separado.

Al visitar de nuevo el mismo ejemplo, vemos el cilindro rodar correctamente por la pendiente. Es imposible que un montaje se suspenda si una fuerza desequilibrada lo acciona. Para que un montaje se suspenda, debe estar por debajo del umbral de velocidad e impulso.

Si usamos esta misma lógica para hacer que los montajes se activen, nos encontramos con un pequeño problema. No podemos comprobar el valor del último impulso de un ensamblaje suspendido, ya que el objetivo de que algo esté desactivado es que no se encuentra en el solucionador. Para eso, todavía necesitamos confiar en los contactos y los eventos de cambio de propiedad. Sin embargo, la relación de mecanismo puede ayudar en una variedad de casos. De hecho, el historial de un ensamblaje también se utiliza para ayudar a determinar cuándo deben activarse otros montajes del mismo mecanismo.

Cuando un mecanismo tiene un montaje activo, todos los vecinos inmediatos de ese montaje se ponen en un estado de «comprobación» (si no están también activos). Los ensamblajes en el estado de «comprobación» siguen inactivos, pero comprueban el historial del montaje activo en cada fotograma, y si su desviación está por encima de otro umbral más grande, establecimos que el ensamblaje de «comprobación» también debe estar activo. A esto se le conoce como el umbral de velocidad del vecino.

Asumimos que si uno de estos montajes se está moviendo mucho, entonces los otros ensamblajes deben estar activos y en movimiento también. Esto no siempre es el caso, así que termina siendo un poco más agresivo de lo necesario. Por ejemplo, supongamos que tienes un molino de viento hecho de dos ensamblajes: las aspas giratorias y la base. Lo ideal sería que las aspas giratorias estuvieran activas mientras la base permanece suspendida, ya que esta no se mueve. Sin embargo, mientras las aspas giratorias se muevan a una velocidad superior a la del umbral de velocidad del vecino, la base se mantendrá activa de manera incorrecta.

Este comportamiento ideal puede causar problemas con otros tipos de mecanismos. Puedes hacer que un montaje se mueva lo suficientemente rápido para mantenerse activo, pero lo suficientemente lento para mantener a sus vecinos en el estado de inactividad. Si el montaje activo se mueve a un punto que requiere que su vecino inactivo se moviera también, ambos se atascan y terminan inactivándose.

Aquí hay una demostración de ese caso. Tenemos dos bloques conectados por una cuerda en gravedad cero. El bloque grande está activo y en movimiento, mientras que la parte estacionaria está en estado de suspensión. Esto es correcto hasta que el bloque grande se mueva lo suficiente para que la cuerda se enseñe.

Los montajes que están en estado de suspensión se marcan en naranja.

No hay manera de que el montaje de control de suspensión sepa que la cuerda se enseñó; en lo que a ella concierne, su vecino aún se mueve lo suficientemente lento como para permanecer inactivo. El bloque móvil tira de la cuerda pero ya no puede moverse, así que se detiene y se desactiva.

Como ya se puede esperar, un nuevo umbral de impulso del vecino también se podría usar para despertar a los vecinos inactivos. En este caso de la cuerda, sabemos que el bloque sufre un cambio en la fuerza neta tan pronto como la cuerda se enseñe. Usamos ese cambio en la fuerza como un evento para despertar el ensamblaje de la «comprobación». En el ejemplo, vemos que una vez que la cuerda se enseña, la parte inactiva se despierta. No se necesita hacer cálculos adicionales sobre la parte inactiva en sí.

No solo se puede usar para activar a los ensamblajes inactivos, sino que también puede mantenerlos suspendidos por más tiempo. Ya no tenemos que asumir que un montaje necesita activarse solo porque su vecino activo se mueve muy rápido.

Al final, con solo un par de pequeños cambios mejoramos enormemente el sistema de suspensión del motor. En lugar de utilizar umbrales de velocidad ajustados, se aplica directamente la Primera Ley de Newton. Ahora miramos las fuerzas en su lugar, inactivando los cuerpos cuando realmente están en reposo. Hay muchos casos que el motor ahora puede controlar correctamente, evitando la necesidad de que los desarrolladores encuentren errores y los arreglen independientemente. A veces, si abordamos los problemas de la física volviendo a los fundamentos de la física podemos encontrar soluciones.


Ni la Corporación Roblox ni este blog respaldan o apoyan a ninguna empresa o servicio. Además, no se ofrecen garantías ni promesas sobre la exactitud, fiabilidad o integridad de la información contenida en este blog.

Este blog se publicó originalmente en Roblox Tech Blog.