3 puntos por GN⁺ 2024-05-25 | 1 comentarios | Compartir por WhatsApp
  • La resolución de colisiones de cuerpos rígidos en la física 2D de juegos es el problema de calcular cambios de velocidad para que objetos que ya se tocaron o se superpusieron no se atraviesen entre sí en el siguiente frame
  • El bucle del juego actualiza la posición en cada frame usando la velocidad y Δt, así que si la geometría se superpone en la nueva posición, los objetos se atravesarán entre sí si no hay un manejo adicional
  • Una colisión no es solo una cuestión de si hay contacto, sino también de si los objetos en contacto siguen moviéndose uno hacia el otro con sus velocidades actuales
  • Se puede determinar si se alejan de la superficie con el signo del producto punto entre la normal y la velocidad: si es positivo, hay un componente en la misma dirección; si es negativo, hay un componente en dirección opuesta
  • En el caso de dos objetos, importan más la velocidad relativa y la normal de colisión que las velocidades individuales, y si la velocidad normal relativa es negativa mientras están en contacto, puede considerarse una colisión

Alcance de la física de cuerpos rígidos y de la resolución de colisiones

  • El tema es la física de cuerpos rígidos (rigid body physics), que trata objetos que no se deforman aunque reciban fuerzas
    • En la realidad, todos los objetos se deforman a nivel molecular, así que no existen cuerpos perfectamente rígidos
    • En la mayoría de las simulaciones físicas, calcular ese nivel de deformación es muy difícil o muy costoso
    • Si el objeto se ve lo bastante realista, simplificarlo como un cuerpo rígido es una estrategia práctica
  • El manejo de colisiones en un motor de juego normalmente se divide en dos etapas
    • Detección de colisiones (collision detection): determina qué objetos de la escena están colisionando
    • Resolución de colisiones (collision resolution): decide el estado posterior a partir de la dirección de movimiento, velocidad, material, etc. de los objetos en colisión
  • El foco aquí no es la etapa de encontrar si hay intersección geométrica, sino la resolución de colisiones, que determina el movimiento después de la colisión

Cómo se producen las colisiones en el game loop

  • La mayoría de los juegos recalculan repetidamente la posición de los objetos de la escena dentro de un bucle grande
  • En cada iteración, la posición del objeto se actualiza con base en su velocidad (velocity) actual
    • La velocidad es una magnitud vectorial con magnitud y dirección
    • La longitud de la flecha representa la rapidez, y la dirección de la flecha representa la dirección del movimiento
  • El cambio de posición durante un intervalo de tiempo Δt se expresa como desplazamiento (displacement)
    • El desplazamiento también es una magnitud vectorial con magnitud y dirección
    • Si el game loop se ejecuta 60 veces por segundo, Δt es 1/60 de segundo
  • La nueva posición se obtiene sumando a la posición previa el desplazamiento calculado a partir de la velocidad actual
  • Si las nuevas posiciones de dos objetos hacen que sus geometrías se superpongan, los objetos se penetrarán y se atravesarán si no hay un manejo adicional

Qué valor busca encontrar la resolución de colisiones

  • El objetivo de la resolución de colisiones es determinar el cambio de velocidad de cada objeto para que, a medida que avanza la simulación, dejen de penetrarse entre sí
  • Las velocidades antes y después de la colisión se expresan con la siguiente notación
    • v_a,i, v_b,i: velocidades de los objetos a y b antes de la colisión
    • v_a,f, v_b,f: velocidades de los objetos a y b después de la colisión
    • Δv_a, Δv_b: cambio de velocidad de cada objeto causado por la colisión
  • En última instancia, resolver la colisión es encontrar los valores de Δv_a y Δv_b
  • Para que la colisión se vea realista, los cambios de velocidad elegidos deben cumplir las leyes físicas relevantes

El contacto por sí solo no basta para identificar una colisión

  • Que dos objetos estén tocándose no significa siempre que estén en colisión
  • Hay colisión cuando, si siguen moviéndose con sus velocidades actuales, los objetos terminarían penetrándose entre sí
  • Incluso con la misma escena de contacto, según la dirección de las velocidades de ambos objetos puede haber colisión o no
  • Por eso, la condición de colisión necesita dos cosas al mismo tiempo
    • Las geometrías de los objetos deben tocarse o superponerse
    • Los objetos deben seguir moviéndose en la dirección de colisión

Normal de la superficie y dirección de alejamiento

  • Se puede determinar si un objeto se aleja de una superficie usando la dirección normal (normal direction) de esa superficie
  • La dirección normal es perpendicular a la superficie y apunta directamente hacia afuera desde ella
    • En una superficie plana, la dirección normal es la misma en todos los puntos
    • En una superficie curva, la normal cambia según el punto
    • En la circunferencia de un círculo, la dirección desde el centro hacia el punto de la circunferencia es la dirección normal
  • La dirección normal se expresa como un vector normalizado de longitud 1
    • Un vector de longitud 1 también se conoce como vector unitario (unit vector)
    • Para indicar que un vector está normalizado, se puede usar el símbolo ^ sobre la variable
  • La dirección normal en un punto es perpendicular a la tangente (tangent) de la superficie en ese punto

Usar el producto punto para determinar componentes de dirección

  • Cuando se quiere calcular cuánto apunta un vector en la misma dirección que otro, se puede usar el producto punto (dot product)
  • En vectores bidimensionales, el producto punto es la suma de los productos de las componentes correspondientes, y el resultado no es un vector sino un escalar
  • Geométricamente, el producto punto puede verse como la longitud de la proyección escalar de un vector sobre la dirección del otro, multiplicada por la longitud del vector sobre el que se proyecta
  • El signo del producto punto indica la relación de dirección entre los dos vectores
    • Si el ángulo entre dos vectores es menor que 90°, el producto punto es positivo y, en general, apuntan en la misma dirección
    • Si el ángulo es mayor que 90°, el producto punto es negativo y, en general, apuntan en direcciones opuestas
    • Si el ángulo es exactamente 90°, el producto punto es 0
  • Si el producto punto entre el vector de velocidad de un objeto y la normal de una superficie es positivo, el objeto se está alejando de esa superficie

Aplicarlo a la colisión entre dos objetos

  • Si hay dos objetos, como dos cajas, entonces existe un vector de velocidad para cada uno
  • En ese caso, en lugar de las velocidades individuales se usa la velocidad relativa (relative velocity) de ambos objetos
    • La velocidad relativa es la diferencia entre las velocidades de los dos objetos
    • Geométricamente, es el vector que va desde la punta de v_b hasta la punta de v_a
    • Por ejemplo, si dos autos chocan de frente a 50 km/h cada uno, bajo las mismas condiciones eso equivale a que un auto choque contra otro detenido a 100 km/h
  • La dirección correspondiente a la superficie se expresa como la normal de colisión (collision normal)
    • La forma de calcular la normal de colisión cambia según la forma o geometría de los objetos que colisionan
    • El ejemplo aquí es una colisión vértice-arista (vertex-edge collision), donde un punto o vértice de un objeto choca con el borde de otro
    • En una colisión vértice-arista, la normal de colisión es perpendicular a esa arista
  • Por convención, si los objetos se representan como a y b, la normal de colisión apunta hacia el objeto a
    • No importa cuál objeto se llame a o b, siempre que se mantenga la consistencia en todo el cálculo

Definir la colisión mediante la velocidad normal relativa

  • Si se calcula el producto punto entre la velocidad relativa v_ab y la normal de colisión n^, se puede determinar si los dos objetos se están moviendo en la dirección de colisión
  • A este valor se le llama velocidad normal relativa (relative normal velocity)
    • Es la componente de la velocidad relativa en la dirección de la normal de colisión
    • Aquí el signo es importante, pero también cumple un papel clave más adelante al calcular las fuerzas que actúan durante la colisión
  • El signo de la velocidad normal relativa distingue el estado de la colisión
    • Si el valor es positivo, los dos objetos ya se están alejando entre sí
    • Si el valor es negativo, los dos objetos todavía se están golpeando entre sí
  • En conclusión, hay colisión cuando un punto de un objeto está en contacto con el otro objeto y la velocidad normal relativa es negativa

1 comentarios

 
GN⁺ 2024-05-25
Opiniones de Hacker News
  • ¡Hola, soy el autor! Para agregar un poco de contexto, este artículo es solo la primera parte de una serie de blog sobre física de cuerpos rígidos que quiero escribir.
    El artículo está dirigido a personas que, como yo, no son desarrolladoras de juegos ni tienen una base sólida en matemáticas. Por eso me tomé bastante tiempo para explicar conceptos que a alguien con experiencia en el área le parecerían casi obvios. Si tienen preguntas, con gusto las respondo.
    • Como feedback, el ejemplo de la introducción sobre “Mario rebotando sobre un Goomba…” me parece que puede prestarse un poco a malentendidos. La mayoría de los juegos clásicos tipo Super Mario de NES y SNES no necesitaban ni usaban la mayor parte de estos cálculos.
      Quienes empiezan en desarrollo de juegos suelen creer erróneamente que para manejar colisiones necesitan cálculos de colisiones de cuerpos rígidos o un motor de física 2D como Box2D. Eso es cierto si quieres hacer un juego de billar o uno como Angry Birds, donde se derrumban cajas; pero si haces un platformer 2D, suele bastar con detectar colisiones comparando rectángulos alineados a los ejes, y luego cambiar las coordenadas X/Y del personaje para deshacer el solapamiento, o ajustar la velocidad en Y después de saltar o aterrizar. Con este enfoque también es más fácil ajustar con precisión la sensación de control del personaje; incluye inercia, pero normalmente no una inercia físicamente realista. Cuando los principiantes intentan usar física realista, es fácil que el movimiento se sienta flotante y poco satisfactorio.
      Un ejemplo de tutorial para empezar con este enfoque simple, sin motor de física: https://www.love2d.org/wiki/Tutorial:Baseline_2D_Platformer
    • Muy bueno. La sección “A word about math” es realmente importante. Yo tampoco soy particularmente bueno en matemáticas, pero hace tiempo hice una simulación física muy básica simplificando al extremo algunos conceptos matemáticos.
      Fui apilando componentes como puntos y líneas de forma iterativa, y al agregar muchos pasos pequeños y líneas visuales de depuración, el resultado era muy tosco y lento, pero de todos modos funcionaba hasta cierto punto.
    • El artículo es excelente y fue divertido de leer. Yo tampoco tengo una base sólida en matemáticas, así que agradezco que expliques estos conceptos “obvios” :)
      ¿Tienes pensado leer y explicar también XPBD (Extended Position Based Dynamics - http://mmacklin.com/xpbd.pdf) más adelante? Parece que este concepto está recibiendo cada vez más atención, y yo lo usé con bastante éxito en Bevy mediante https://github.com/Jondolf/bevy_xpbd. Parece más estable que los enfoques comunes.
    • Disfruté mucho el artículo :) Incluso para alguien a quien le costaron temas similares en la escuela, fue fácil de entender.
      Sería genial que agregaras un feed RSS para poder seguirlo.
    • ¡La explicación es realmente buena!
      Por curiosidad, ¿qué herramientas usaste para crear esa página?
  • ¡Oh! Es un artículo bien investigado, explicado en profundidad e incluso interactivo.
    Sinceramente, al principio, cuando vi el nombre de dominio y noté que el dominio de nivel superior era “.ski”, pensé que era el sitio de la persona que escribió Mechanical Watch [1] y otros artículos geniales. Resultó ser alguien completamente distinto, pero la calidad es similar. ¿Cuál será la salsa secreta de este dominio de nivel superior “.ski”? :)
    1. https://news.ycombinator.com/item?id=31261533
    • La razón es muy simple. “ski” es el sufijo más común en los apellidos polacos, y el ejemplo más famoso es Kowalski. Hay muchas personas polacas o de ascendencia polaca.
      El autor de https://ciechanow.ski, que tanto nos gusta por aquí, también es un programador polaco que trabaja en Apple.
  • Ahora estoy haciendo con mi hijo, como proyecto paralelo, un shooter espacial 2D. La idea es una vista cenital, donde cada jugador controla una nave y vuela dentro de un espacio cerrado lleno de escombros espaciales, disparándole al rival.
    Un elemento importante del juego es que los escombros espaciales puedan moverse dentro de la arena, y que se puedan usar creativamente para encerrar al oponente o impedirle alcanzar objetivos. Como parte del proyecto, quería evitar por completo usar un motor de juegos. Quería enseñarle a mi hijo un poco más sobre la estructura de una aplicación y, aunque más adelante usemos un motor listo, quería que al menos una vez pasáramos por el proceso de implementarlo todo. Todo iba bien hasta que llegamos a la detección y resolución de colisiones. A partir de ahí, las cosas se deterioraron rápidamente. Incluso teniendo formación en matemáticas teóricas, me vi abrumado enseguida por la enorme cantidad de casos límite, y al final me rendí y decidimos usar Box2D. No soy desarrollador profesional de juegos, pero tengo más de 20 años de carrera como desarrollador y formación matemática; aun así cometí el error de subestimar este problema. En palabras parece fácil, pero cuanto más entras en los detalles, la complejidad parece crecer de forma exponencial.
    • ¿Ese juego realmente necesitaba colisiones físicas realistas? Si no, puede ser una complejidad innecesaria. Casi todos los shooters 2D anteriores al 2000, y solo una pequeña parte de los posteriores, usan ese enfoque.
      Aquí hay una forma común de hacer un shooter con comparaciones de rectángulos muy simples: https://kidscancode.org/blog/2016/08/pygame_shmup_part_3/
      Dicho eso, si los objetos de escombros espaciales tienen que chocar y agruparse de forma realista, y quieres que a la nave del jugador le cueste empujar grupos de objetos pesados, entonces tiene sentido usar una biblioteca de física.
    • ¿Viste la integración de Verlet [1]? Es bastante convincente y práctica para muchos usos, y en realidad es bastante simple. Me sorprendió poder armar un sistema físico básico en unas horas siguiendo este excelente tutorial [2].
      [1]https://m.youtube.com/watch?v=lS_qeBy3aQI&pp=ygUSVmVybGV0IGl...

[2]https://m.youtube.com/watch?v=3HjO_RGIjCU&pp=ygUSdmVybGV0IGl...

  • Aun así, creo que será una buena lección para su hijo. No siempre vale la pena seguir el sueño de hacer absolutamente todo por cuenta propia en cada parte del proyecto.
  • Aunque no sea ahora, más adelante será una referencia útil. http://www.jeffreythompson.org/collision-detection/table_of_... cubre la detección de colisiones entre puntos, círculos, rectángulos, líneas, polígonos y triángulos.
  • Siempre me gustó la explicación del juego N: https://www.metanetsoftware.com/technique/tutorialA.html
    Eran los tiempos en que Flash estaba en todas partes.
  • Me divertí creando una demo en TypeScript con pelotas que rebotan y chocan sobre este tema. Aprendí mucho.
    Código: https://github.com/vandrieu/canvas-bouncing-ball
    La lógica de colisiones está en src/collision.ts
    Resultado/demo: https://vandrieu.github.io/canvas-bouncing-ball/
    • Es una demo realmente buena, ¡muy bien hecha! Si te parece bien, me gustaría convertirla en un pequeño juego multijugador.
      ¿Podrías agregarle una licencia, si es posible?
  • Si quieren profundizar más en dinámica de cuerpos rígidos y restricciones, esta serie de posts me resultó muy útil: https://www.toptal.com/game/video-game-physics-part-i-an-int...
  • Una colisión es la violación de una restricción de no intersección por pares entre objetos. La fuerza de colisión es el multiplicador de Lagrange de esa restricción. La normal de colisión es la derivada parcial normalizada de la función de restricción respecto de la configuración de uno de los objetos.
    • Ese enfoque parece encajar bien cuando calculas la física a 1 kHz o más y usas un algoritmo de integración numéricamente estable que respeta la conservación de la energía.
      Pero en juegos, a menudo se usan actualizaciones de física que bajan hasta 30 Hz con un método Euler-Cromer arbitrario, así que hace falta un enfoque bastante distinto.
    • ¡Interesante! ¿Hay algún recurso que explique más esta perspectiva?
  • ¡Realmente genial! Me gustaron la explicación, la interacción y, en especial, el tono cercano del artículo. Espero con ganas los próximos.
  • Crear un motor de física 2D de cuerpos rígidos es un proyecto realmente divertido. Yo hice uno en JavaScript antes de aprender álgebra lineal, y terminé profundizando mucho en las matemáticas para lograr que funcionara.
    Le dediqué meses, pero apenas rasqué la superficie un poco más allá de los fundamentos más conocidos. Hacer un motor estable en el que los objetos no se atraviesen ni tiemblen es una madriguera de conejo sin fondo, y casi ninguno de los artículos cargados de matemáticas que pude encontrar lo trataba. Entendí las matemáticas con la vieja serie de artículos de Chris Hecker.
    http://www.chrishecker.com/Rigid_Body_Dynamics
    • ¡Exacto! “Part 3: Collision Response” es, en la práctica, lo que estoy usando como referencia para estos artículos.
  • Empecé con canvas para aprender JavaScript y, sin experiencia en desarrollo de juegos, hice unos cuantos jueguitos lindos para navegador. Uno de ellos es un clon de Galaga, y en general funciona bien.
    La parte difícil son las colisiones de proyectiles. Tenía que tomar la posición actual de la bala y su posición en el siguiente paso de tiempo, y hacer lo mismo con la hitbox del enemigo para comprobar si se cruzan, pero yo solo revisaba el paso de tiempo actual. ¡Así que las balas pueden pasar mágicamente esquivando a los enemigos! Una tontería. Tal vez algún día vuelva y lo arregle.