Sistema de Partículas II : Explosiones
- Set up y movimiento de las partículas
Para modelar una explosión lo que hago primero que nada es inicializar cada partícula estableciendo las siguientes características:
- Punto en común de orígen
- Dirección
- Energía
En las explosiones aéreas la dirección de cada partícula es distribuída en forma esférica centrada en el punto de orígen. Este tipo de explosiones podrían modelarse con la distribución de direcciones de cada partícula de manera uniforme. Si cortamos la esfera que forma esta distribución con un plano donde ocurre la explosión, una pared por ejemplo, la dirección e cada partíucula será distribuida en forma semi-esférica dado que cada una es disparada en la dirección normal al plano de impacto. Habría que adaptar esto ya que si la explosión surge en un ángulo las partículas deberían explotar correctamente para dar mas realismo, dado que necesariamente la distribución será esférica o semi-esférica.
Veamos el código de inicialización para cada partícula (set up). Como parámetros de entrada ingresamos las padrtículas, el vector coordenada de impacto y la cantidad total de partículas. Este último campo es para una implementación estática del sistema, podría obviarse con una celda dummy en el caso de implementación dinámica (recomendado). Minimizar la cantidad de operaciones con memoria (new y delete) hace mas eficiente el sistema, en el caso de la implementacion dinámica a medida que se necesitan partículas se siguen los siguientes pasos: Determinar si hay partículas muertas pero con alojo en memoria, en este caso resucitar las necesarias. Sin no hay muyertas,, entonces empezar a crear las que sean necesarias, pero claro, esto tampoco es barato. El alojamiento en memoria tiende a crecer dependiendo de la cantidad de eventos simultáneos, probablmenete sean partículas muertas las que estén alojadas. Mediante la implementacoión estática también logramos eficiencia, pero a un costo bastante mayor de entrada, dado que aunque haya solo 1 partícula viva en el sistema, siempre se tendrá el máximo de partículas alojado en memoria. Para las explosiones por lo general se conoce de entrada la cantidad de partículas máxima del sistema, y todas interactúan en la explosión (inicialmente). Sin embargo no siempre las explosiones toman el mismo tamaño y energía, entonces en necesario acotar conjuntos independientes del total para adaptar la explosión deseada. Es aquí donde entra en utilidad el campo active que indica si la partícula está activa (viva) o si no lo está (muerta). En nuestro caso usaremos arreglos dinámicos, y este campo solo servirá para descartar partículas muertas, no para acotar sistemas, por ahora almenos.
NOTA (sobre los vectores): en las imlementaciones aparecen directamente funciones de la clase vector. No vale la pena detenerese en esta clase, es muy intuitiva y las funciones también lo son, de hecho hablan por si solas.
// constantes del sistema
#define PARTICLES 1000
#define GRAVITY 9.8
#define ENERGY 7
...
// set up del sistema
particle* p= new particle*[PARTICLES];
...
void explosion::setup(particle* &p, vector coord, long tpar)=
{
float len, ie, dx, dy;
particle* paux=p;
for(long i=0; i < tpar; i++)
{
//activamos la partícula
paux->active= true;
//coordenadas del orígen
paux->pos.setx(coord.getx());
paux->pos.sety(coord.gety());
//dirección aleatoria al explotar
//negativa o positiva (a suerte)
paux->dir.setx(((rand()%4)-1.5));
paux->dir.sety(((rand()%4)-1.5));
//setup de energía inicial
ie= (rand() % ENERGY) + 1;
//registramos las coord para el cálculo
//del módulo, esto debe ir en la clase vector
dx= paux->pos.getx();
dy= paux->pos.gety();
//cálculo del módulo
len= sqrt(dx*dx + dy*dy);
//norma
if (len != 0.0)
len= 1.0 / len;
//normalizar para encontrar la dir.
//del vector, la distr. deja de ser uniforme
paux->pos.setx((paux->pos.getx())*len*ie);
paux->pos.sety((paux->pos.gety())*len*ie);
//color inicial
paux->color= 255;
paux++
}
}
Consideraremos una explosión en una habitación, para observar las colisiones. La habitación será un cadrado de 100x100 pixeles cuyo vértice superior izquierdo se ubica en el punto de coordenadas (350,250). También consideraremos la aceleración de la gravedad, y un efecto muy simple de "chispa pixelada" para indicar las partículas próximas a la muerte.
Veamos ahora la implementación para el movimiento.
void explosion::move_particles(particle*p, long tpar)
{
particle* paux= p;
for(long i=0; i < tpar; i++)
{
if (paux->active) {
//movemos la partícula de acuerdo al
//vector dir
paux->pos.setx(paux->pos.getx()+paux->dir.getx());
paux->pos.sety(paux->pos.gety()+paux->dir.gety());
//chequeamos colisiones con piso: muerte
if (paux->pos.gety() > 350)
paux->active= false;
else
//colisiones con techo
if (paux->pos.gety()) < 250)
{
paux->pos.sety(250);
paux->dir.setx(paux->dir.getx()/4);
paux->dir.sety(-(paux->dir.gety())/2);
}
else
//no actúan fuerzas en el eje x -> 0
//gravedad en el eje y -> 9.8
//en update sumo gravedad a la dirección en y
paux.update(0, GRAVITY);
//colisiones con paredes
if (paux->pos.getx() < 350)
{
paux->pos.setx(350);
paux->dir.setx(-(paux->dir.getx())/2);
paux->dir.sety(paux->dir.gety()/4);
}
else
if (paux->pos.getx() > 450) {
paux->pos.setx(450);
paux->pos.setx(-(paux->dir.getx())/2);
paux->dir.sety(paux->dir.gety()/4);
}
//si la partícula está próxima al piso
//hacemos un efecto de chispa cambiando
//el color del pixel
if (paux->pos.gety() >= 348)
paux->color= (rand() % 128) + 128;
}
paux++;
}
}
inline Uint32 lapsedTime(Uint32 &t)
{
return t= SDL_GetTicks()-t;
}