Simulacion de sistemas complejos I - Partículas

12 07 2006

Despues de haber estado bastante tiempo trabajando en entender el motor de físicas usada en el videojuego “Hitman - CodeName 47″, me decidí por dar comienzo de una buena vez a escribir, ya que el algorítmo tiene tantas pero tantas posibilidades, que tengo miedo de que para cuando termine de explorarlas a todas se me vaya a ir de la cabeza algo importante, así es que en esta primera entrega les traigo las formulas para manejar la unidad básica de todo el asunto: La partícula.

Para comenzar creo que lo más correcto es dar crédito a quien crédito merece. Esta serie de tutoriales no hubiera sido posible sin el maravilloso artículo publicado en gamasutra cuyo autor es Thomas Jakobsen. La claridad con las que expone los conceptos fue la piedra angular sobre la cual esta basado este tutorial.

Breve reseña
Como se mencionó algunas lineas mas arriba, la técnica que se explica a contunuación es la usada en el juego "HITMAN - Codename 47" y sobre la misma se ha construido el game engine de IO-Interactive llamado GLACIER. Este algoritmo se usa en la saga Hitman para simular no solo el comportamiento de cadáveres humanos, sino también ropa, plantas tablas, cajas, todo!.

Entre las características que mas se destacan, como bien señala el autor, son su credibilidad(realmente los objetos se mueven como uno espera que lo hagan), su velocidad(solo una fracción del tiempo de proceso es utilizado en el)y su estabilidad(carece del stress típico de otras aproximaciones).

Cuando empecé a experimentar con la primera implementación me maravillé por las infinitas posibilidades que ofrece, el tiempo insumido puede ser regulado en tiempo real para adecuarlo al tipo de sistema. Con unas pequeñas modificaciones puede ser dotado de calculo de colisiones, spacewarps, etc. Y se puede construir con el sistemas realmente complejos, teniendo que meter mucho objeto en pantalla para que la representación comience a verse lenta, va en una Pentium 2 a lo mejor la performance baja un poco, pero sigue siendo asombrosamente rápido.

Bueno ahora que ya sabemos de que se trata todo esto damos comienzo con la primera entrega de lo que espero sea una útil saga de tutoriales.

Partículas
La aproximación que haremos hacia la simulación de objetos es a partir de sus partículas. Todos los cuerpos están formados por moléculas, que son las partículas mas pequeñas que podemos obtener del mismo sin que el material del cual esta conformado pierda sus características. Por ejemplo, a las moléculas del agua podemos dividirlas en átomos, pero la partícula obtenida ya no sería de agua, sería otra cosa.

Para simplificar nos vamos a imaginar que todas las partículas pesan lo mismo y son traídas hacia el centro de la tierra por la fuerza de gravedad.

Ahora bien, en la secundaria(para los que cursamos en un colegio técnico) nos dijeron que la posición de todo cuerpo que cae libremente en un instante t dado de tiempo responde a la formula...

x=xo+v.t+1/2.a.t2 (1)

Donde x es la posición y de la partícula en todo momento, xo es la posición inicial, v es la velocidad en ese instante del cuerpo, t es el intervalo de tiempo y a es la aceleración de la gravedad. A esta formula de le llama "Ecuación horaria de la posición en función del tiempo", pero nosotros la llamaremos Ecuación horaria para simplificar.

A continuación vemos un ejemplo del uso de dicha formula.

Descargar Ejemplo 1.

Fijate que podes arrastrar la partícula y dejarla caer desde donde quieras, para repetir la simulación de distintas alturas.

Ademas de lo antes mencionado, necesitamos que la bola este dentro del cuadro que representa nuestro "mundo", o al menos, que no siga cayendo mas allá de la linea de base, para eso usamos la siguiente fórmula:

y = Min(Max(y,0),200);

Min representa una funcion que compara 2 valores y devuelve el mínimo. Análogamente, Max compara 2 valores y devuelve el máximo. Lo que hace que la posición _y de la bola se mantenga siempre dentro de los límites del escenario.

Cabe mencionar, que para este tutorial no se toma en cuenta el rebote. Así es que las partículas que choquen contra el piso o las paredes quedarán quietas, como sucede cuando tiramos una bola de plastilina contra el piso con toda nuestra fuerza. A esto se le llama "Choque perfectamente plástico", en el cual toda la energía cinética de la bola, se pierde(disipa) en el choque contra el piso.

En el ejemplo 1 usamos la formula horaria para representar a una pelota que rebota.
Pero esta fórmula no la podemos meter así como así en el código, tiene varias cosas que van a jugar en nuestra contra cuando la tengamos que usar para un número grande de partículas. Así que vamos a comenzar a modificarla para dotarla de velocidad y hacerla más afí a nuetros propósitos, así es que, como primera medida, vamos dejar el tiempo transcurrido(t) fijo en 1 y a usar la formula siguiente:

x=x+xo+a.t2 (2)

Donde x es la posición actual, x o es la posición anterior de la partícula y t es el tiempo transcurrido desde que la partícula estaba en xo.
Ahora, como el tiempo esta fijo en uno, podemos quitar los t, ahorrandonos el calculo de un cuadrado, con lo que la fórmula quedaría de la siguiente forma:

x=x+xo+a (3)

Esta formula es llamada Integración Verlet, es usada intensamente en la simulación de dinámica molecular y es particularmente estable, ya que al tener la velocidad implícita(lo que viajo la partícula desde xo a x en una unidad de tiempo) es mas difícil que las variables se dessincronisen.

Ahora bien, para no hacer mas complejo el tutorial no vamos a tener en cuenta el rebote, es decir, cuando las partículas choquen contra los limites del escenario quedaran con velocidad cero.

Organizando el Código
Ahora que ya enunciamos las formulas que vamos a usar vamos a organizarnos para que podamos reutilizar el código que vallamos escribiendo. Primeramente crearemos, en una librería .as la siguiente clase.

Actionscript:
  1. class Particula {
  2.     var _gravedad:Number = 1;
  3.     var _x_old:Number;
  4.     var _y_old:Number;
  5.     var _masaInfinita:Boolean = false;
  6.     var _x:Number;
  7.     var _y:Number;
  8.     function Particula(x, y) {
  9.         this._x = x;
  10.         this._y = y;
  11.         this._x_old = x;
  12.         this._y_old = y;
  13.     }
  14.     function calcularReaccionesFuerzas() {
  15.         //no implementado
  16.     }
  17.     function calcularPosicion() {
  18.         var temp_y:Number = this._y;
  19.         var temp_x:Number = this._x;
  20.         if (!this._masaInfinita) {
  21.             this._y += this._y-this._y_old+this._gravedad;
  22.             this._x += this._x-this._x_old;
  23.         }
  24.         this._x_old = temp_x;
  25.         this._y_old = temp_y;
  26.     }
  27.     /*
  28.     Este netodo evita que la bola se salga del cuadrado
  29.     */
  30.     function constrainToWorld(bounder) {
  31.         /*La formula de abajo es el valor mínimo entre la posicion de
  32.         la particula y el valor máximo que esta puede tomar(o sea, que tan abajo
  33.         puede llegar), si la particula se pasa del “piso”, entonces su posicion
  34.         pasa a ser el extremo inferior del cuadrado.
  35.         Luego toma el valor obtenido y saca el valor máximo entre el vorde superior del
  36.         cuadrado y la posicion de la particula, si la partícula esta por encima del cuadrado
  37.         su posicion será 0, esta combinacion asegura que la bola siempre esta dentro de los limites del
  38.         cuadrado.
  39.         */
  40.         _x = Math.min(Math.max(_x, bounder._x), bounder._width);
  41.         _y = Math.min(Math.max(_y, bounder._y), bounder._height);
  42.     }
  43. }

Examinemos un poco el código. Notaran que existe una variable llamada _gravedad, esta variable dice con aceleración se le aplicará a las partículas. Mientras mas grande el valor, mas rápido caerán.

Después tenemos la variables que almacenan las posiciones x e y del frame anterior, llamadas _x_old e _y_old.

La próxima variable, llamada _masa_infinita, sirve para hacer que la partícula no sea afectada por las fuerzas que actúan sobre ella, esto es útil cuando la queremos arrastrar con el mouse y no queremos un comportamiento errático de la misma, como por ejemplo, un temblequeo.

Finalmente, las posiciones _x e _y, tienen puestos los "_" al principio de los nombres por compatibilidad, ya que resulta mas fácil, pasar un movieclip como parámetro, que andar dibujando en un papel las posiciones y después ponerlas en el código. Con lo anterior quiero decir que lo que hago a la hora de generar sistemas complejos de partículas es, en lugar de dibujar en un papel cuadriculado para tratar de adivinar la posición correcta de las partículas, simplemente genero tantos movieclips como sean necesarios, en las posiciones convenientes, luego genero con ellas un array de partículas que serán inicializadas cada una con las posiciones de los movieclips que hay en el escenario. Esto lo veremos con mas detalle mas adelante.

Ahora, el constructor de la clase es bastante simple así que no vamos a perder tiempo con el, fijaremos nuestra atención entonces en la función "calcularPosicion", lo que hace esta formula es poner en práctica la ecuación (2), esta solo afecta la posición _y, ya que todas las partículas son afectadas por la gravedad, a no ser que tengan masa infinita.

Ahora, hay una ultima función que merece nuestra atención, que lleva como nombre "constrainToWorld", esta función lo que hace es mantener a las partículas dentro de un cuadrado que será nuestro lienzo de presentación, las mismas no podrán salir del mismo y cada vez que su posición este fuera del mismo, será ajustada para conservarlas dentro de sus límites. El parámetro que recibe es un Movieclip o cualquier objeto que tenga las propiedades width y height, yo prefiero usar un movieclip cuadrado de bordes negros con fondo blanco, así es mas fácil de ver el cuadrado donde las partículas estarán.

Descargar Ejemplo 2.

Algo muy importante que hay que resaltar sobre la integración de Verlet, es que solita nos calcula el vector velocidad (o sea, para donde va la partícula y que tan rápido tiene que moverse), cosa que en el ejemplo que dimos al principio (página 1), la velocidad es una variable, si choca contra algo o arrastramos la partícula (como se puede ver en el código fuente) tenemos que anularla(setearla a cero) y si queremos revolear(agarrar con el mouse y arrojarla) la partícula como en este ejemplo, tenemos que calcular nosotros el ángulo de la velocidad(omitido en el ejemplo 1 por practicidad), de manera que el código quedaría mas abultado y menos performante.Bueno, por ahora los dejo con esto, en nuestra próxima entrega veremos el segundo concepto que sirve para esto de la simulación de sistemas complejos, los constrains.

Hasta entonces!!!!
bye!!!


Acciones

Informacion

Una respuesta a “Simulacion de sistemas complejos I - Partículas”

25 07 2007
.: WEREMSOFT :. » ¿Como funciona el sistema de detección de colisión del "Engines Of Creation"? (01:34:51) :

[...] Para comenzar, notá que el cuerpo no es solido, sino que las colisiones se detectan solo con los vértices. La forma de simular dinámica de partículas la explico en mi tutorial Simulación de Sistemas Complejos I. [...]