Hace un tiempo atrás tenía implementado un sistema de partículas simple, lo había hecho antes de empezar con un juego de aviones, para modelar explosiones aéreas. A mitad de proyecto volví a cambiar la implementación del sistema agregando explosiones terrestres, pérdidas de combustible desde el mismo avión y otra clase para manejo de vectores. Eso en un principio, hoy mi sistema tiene ya varios efectos diferentes. Cada vez que se agrega un efecto es muy probable que haya que modificar varias clases, sobre todo la misma clase 'partícula'. El objetivo de esta serie 'Sistema de partículas' es comentar como he empezado mi sistema de partículas, desde los efectos iniciales. La idea es repasar mas que nada fragmentos de código (en C++) y algunos puntos teóricos. El código del sistema original no es corto, y está adaptado a mis necesidades en cuanto a lo que haga, asi que los fragmentos de código que use en esta serie pueden verse reducidos para no confundirse.
Humo, explosiones, nubes, chispas, lluvia, sangre son algunos efectos que se benefician de un sistema de partículas.
El sistema de partículas debe ser del mismo grado del motor, es decir, poder actuar sobre los mismos objetos. Por ejemplo disparar un misil y a su vez agregar el efecto de humo en la tobera del mismo, ó que la sangre corra por el cuerpo del personaje mientras este también corre. En estos casos, cuando el objeto se mueve la posición del sistema de partículas que está unido al objeto en cuestión debe ser actualizado correctamente. Lo mismo pasa al considerar la aceleración de la gravedad sobre cada objeto.
Algunos sistemas de partículas deberán renderizar sus partículas de distinta manera. Un ejemplo, un chorro de sangre puede estar volando por el aire pero al tocar una pared deja de volar para mancharla, y dejar salpicones. Por tanto podemos diferenciar 2 sistemas a renderizar, "chorro de sangre" y "sangre chorreada" para pisos y paredes. También dependiendo de la situación las partículas pueden ser sólo pixeles, en el caso de las 2D un sprite, ó un modelo 3D. En este caso, una de las cosas a tener en cuenta es que el sistema de partículas incrementa enormemente el número de polígonos visibles por frame.
Existen muchos requerimientos, pero para empezar no es necesario saberselos todos. En esta serie siempre se hará referencia a un sistema de partículas en 2 dimensiones, dado que llevarla a 3 es simplemente agregar una componente, para el caso de los algoritmos (renderizar cada partícula es muy distinto, claro está). Como dije antes el código está escrito en C++ y usaré el motor SDL para la tarea simple de visualizar las partículas. La elección del motor depende únicamente del efecto visual que se desee para las partículas, en esta serie el cometido es el sistema de partículas en sí y no la parte de visualización de las mismas, por este motivo el motor a usar da lo mismo. También voy a considerar las partículas como pixeles, para facilitar el código.
Las Partículas
Una partícula es, en general, un solo punto en el espacio. A este punto se le asigna atributos o características. Las características más comunes de una partícula son:
- posición
- energía
- velocidad
- dirección
- tiempo de vida
A medida que avanza la animación de la partícula las características son requeridas para ello, a la vez que se modifican y se actualizan. Tenemos entonces la clase partícula:
class particle {
public:
bool active;
int color;
vector pos;
vector dir;
vector oldpos;
vector vel;
float energy;
reload();
update(float& friction, float& gravity);
rendering();
}
active:
La partícula puede estar cargada en memoria, aún cuando muerta. Este campo indica cada caso.
color:
Define el color como un RGB (para los pixeles). Puede usarse para almacenar el índice de un arreglo de bitmaps creado previamente para colorear cada partícula.
energy:
Como la misma etiqueta, registra la energía actual de la partícula. Evita el tener 2 campos como 'lifetime' y 'age', a medida que la partícula actúa en el sistema la energía se va agotando o incrementando; cuando energy<=o la partícula ha muerto. Pero si esto es así, entonces para qué está la etiqueta 'active' ??? Bueno, por algo la puse ;) . Este campo será reemplzado por una constante en las explosiones simples (sin considerar aceleración).
oldpos: Es el vector posición anterior de la partícula, antes de haber sido modificado. No será usado en las explosiones (almenos en las mas comunes). Este campo es muy usado por ejemplo para modelar chispas y en algunos casos, llamaradas de fuego. El resto de los campos, hablan por sí solos.
Ahora veamos las funciones:
reload( ): Optimiza el rendimiento del sistema, evita las operaciones new y delete. Resucita una partícula muerta sin pedir memoria.
update( ): Actualiza la física de la partícula, sus valores pueden ser modificados en base a los parámetros que recibe (gravedad y fricción).
rendering( ): Es el encargado de renderizar el comportamiento de la partícula. La clase vector debe incluir, aparte de lo básico como vector de 2 componentes, una función para normalizar el vector, que será usada en los algoritmos de explosión para normalizar el vector dirección, como se verá mas adelante.
Esto es todo por el primer número, suficiente para empezar con las explosiones en el siguiente capítulo. Si al avanzar veo que quedó algo colgado, actualizaré el capítulo correspondiente para agregarlo, asi que hasta no terminar la serie los capítulos pueden ser modificados.