particle system effects

Started by
5 comments, last by vaneger 20 years ago

//particles.h

#ifndef _PART_H_
#define _PART_H_

#include"allegro.h"
#include<mRandom.h>
class C_Particle
{
public:
	C_Particle(){RANDOMIZE;x=0;y=0;z=0;xVel=0;yVel=0;lifeTime=0;decay=0;active=false;}
	C_Particle(int X,int Y,int Z,float XVEL,float YVEL,float LIFETIME,
				float DECAY,bool ACTIVE,int RED,int GREEN,int BLUE,int ALPHA)
	{x=X;y=Y;z=Z;xVel=XVEL,yVel=YVEL;lifeTime=LIFETIME;
	 decay=DECAY;active=ACTIVE;r=RED;g=GREEN;b=BLUE;a=ALPHA;}
	~C_Particle(){}
	int x,y,z;// coords

	float xVel,yVel;//speeds

	float lifeTime, decay;//decay setup in milliseconds

	bool active;//is the particle active 

	int r,g,b,a;//colors and alpha transparency


	void iniParticle(int X,int Y,int Z,float XVEL,float YVEL,float LIFETIME,
				float DECAY,bool ACTIVE,int RED,int GREEN,int BLUE,int ALPHA)
	{x=X;y=Y;z=Z;xVel=XVEL,yVel=YVEL;lifeTime=LIFETIME;
	 decay=DECAY;active=ACTIVE;r=RED;g=GREEN;b=BLUE;a=ALPHA;}

	void update(BITMAP* vid,int dir = 1,bool alpha = false)
	{
		if(!active)
			return;
		switch(dir)
		{
			case 0:
				x -= xVel *random(15,20);
				y -= yVel *random(5,10);
				yVel *= 0.6;
				break;
			case 1:
				x += xVel *random(15,20);
				y += yVel *random(5,10);
				yVel -= 0.6;
			case 2:
				x -= xVel *random(15,20);
				y += yVel *random(5,10);
				yVel /= 0.6;
			case 4:
				x += xVel *random(15,20);
				y -= yVel *random(5,10);
				yVel += 0.6;
		}
		
		lifeTime -= decay;
		//if(alpha)

		//	a*=0.05;

		putpixel(vid,x,y,makecol(r,g,b));
		if (lifeTime <= 0)
			active = false;

	}
};
thats my current startings of a particle system, but im not sure how to do particular effects like fire, water, explosions,electricity etc any good ideas or links to tutorials for this kinda stuff? (im using allegro if you''ll didnt know)
Advertisement
Fire can be as simple as a collection of particles emitted with an upward velocity (strong force in the Y direction, weaker forces in the X and Z directions), and random (rather short) life spans. Possibly give them a color scale that ranges from yellow to red as the particle ages, and bingo.

However, this works best with additive-blending particles, as might be implemented with an API such as Direct3D or OpenGL. The particles add color to the pixels already there, and particles layered on particles further increase the brightness, giving the appearance that the flame is glowing, with a hot center that tends toward white. The particles themselves, rather than just being a single pixel, is a soft dot that fades out toward the edges, giving soft fuzzy edges to the particles, and varied blending strength. Good for flames, sparks, lightning (just scatter additive particles along a random polyline), etc... I am not certain what capabilities Allegro has for additive blending or particles that are more than single pixels, so I can''t give many specific suggestions.

Golem
Blender--The Gimp--Python--Lua--SDL
Nethack--Crawl--ADOM--Angband--Dungeondweller
well what about the differnt effects made by changing how the particles move? (altering the xVel and yVel values etc) what kind of formulas make given effects? the blending i can look at later.for example how could i cause circular effects or effects that spiral?

[edited by - vaneger on March 27, 2004 7:37:58 PM]
anybody have helpful insights into my problem?
Just experiment with stuff. For instance, to get a swirly pattern you can try applying acceleration factors cross-wise to a particle''s velocity, to cause it to veer in a curve. Or you can hook the position of a particle to a non-linear function. Any time you apply velocity to move a particle, you are moving it by means of a linear function. Hook it''s position to a non-linear function instead, and you can move in spirals, waves, circles, or whatever functions you can come up with.

If you consider the total maximum lifespan of a particle as a value (t) in the range of [0,1] and calculated as life/maxlife, then you can feed (t) into any manner of parametric equations. For instance, if the parametric form of a circle on the x/y plane is (a*cos(t), a*sin(t)), you can scale (t) into the range [0, 2pi] and plug it into the equation with the radius (a). Then translate by the particle''s relative center (perhaps the emitter location) to move the circular path from center=(0,0) to center=(emitterx, emittery). This will give you the location on the circle the particle should be at. The range of (t) can be varied to cover an arc of the circle over the life of t, to cover multiple circles, etc... It''s a little more complicated for an arbitrary circle in 3D space, but the concept is still the same. Pre-calculated step or increment look-up tables can also come in handy to avoid excessive use of cos() and sin() or curve evaluations, and use of linear interpolation between samples can help give better speed.

Acceleration is used in all of those standard fountain demos you see all over the place. Particles are ejected from the emitter with a randomized upward vector, and acceleration of gravity is applied each tick over the lifetime of the particle. Planes can be specified to bounce the particles or to collect particles that have gone beyond the working area. If you modify the acceleration parameters, you can coax a particle into all manner of wavy, curvy paths.

It also helps to have a good set of noise functions for randomness. One type of noise generator I like to use looks sorta like this:
t += increment;if(t >= 1.0){  previoussample=nextsample;  nextsample=RandFloat();  delta=nextsample-previoussample;  phase -= 1.0;}  //return (LinearInterp(previoussample,nextsample,t);  // linear interpolation for better speed  return (CosineInterp(previoussample,nextsample,t));  // more curvy cosine interpolation, but slower 


This algorithm can be tuned using the value of (increment). Basically, what it does is it sets up a waveform whose frequency is determined by increment. The lower increment is, the longer the wavelength of the function. The amplitude of the function is randomized between [-1,1], and interpolation (linear for speed, cosine for curve approximation) is used to fill in the wave between samples. I encapsulate this algorithm into a class.

Such a noise generator (or a sum of noise generators, each with different frequencies, for multi-octave noise) is useful for perturbing the path of a particle to add a little randomness when needed. It can also be useful for modifying the color of a particle across it''s color scale, or for other effects. It can be useful for generating lightning bolt paths, flicker of flash brightness, etc...

You can also make use of attractors and repulsors to further affect the movement of particles. These entities are placed at strategic locations within the effect area, and act as gravity or reverse gravity wells.

Other effects can be achieved through the appearance of the particle. For instance, a larger particle texture with a more diffuse, noisy pattern (as opposed to a fuzzy sphere or dot) can be used with dark colors and perhaps even subtractive blending to create smoke. String out a series of such particles in the wake of a moving object, and give them very slight upward velocity (and maybe some acceleration due to wind factors), and you can build a smoke trail. Animate the noise clouds on the textures for an even better effect.

I like to specify color-scales for my particles. A lot of particle systems allow you to specify a color gradient for particles from one color to a second color, the particle slowly changing color as it ages. I take it a step further by allowing color scales to be specified as arbitrary curves with any number of samples. For speed, I sample the curve into a color table which is stored by each particle emitter. Every particle that comes out of that emitter is subject to the color scale. I combine different emitters for multi-colored effects as needed.

You can play with the appearance of the particle as well. In a "standard" particle system, as I mentioned, the particle is usually an additive blended fuzzy sphere or dot. You can draw a particle in mult-pass with a halo (glow) background of one color, and a star, line or flare pattern of another color on top.

Some example particle appearances:


There really are no hard and fast rules on how to do anything with a particle system. It''s mostly a lot of playing around and experimenting with stuff.

Good luck


Golem
Blender--The Gimp--Python--Lua--SDL
Nethack--Crawl--ADOM--Angband--Dungeondweller
For particle effects dealing with nature you will probably need to step through what an idividual particle actually does. A prime example would be fire. Think of it in simple terms for now ignoring external environmental variables since you can add those later. A single particle is generated at the base arbitrarly from two points specified as its coverage distance. The particle will move up due to fact that hotter air is "lighter" than cooler air just because it covers a wider range. You will notice that flames will most of the time go into a tip so you will have to stipulate a routine in which if a particle is on one side of the tip''s x axis then it will move toward it. All this is done while the particle fades. Now once a particle meets the end of its specified y-max boundry it is sent back or if it fades away to where the user no longer sees it.

The following code is of my simple 2D fire generator:
void xia_GenFire(float limit_x1, float limit_x2, float max_y, float min_y, float z, float speed_fade,                  float speed_point, float r, float g, float b, float a, float speed_mod){    static float distance, xTarget;    for(int p=0; p < fire_objects; p++)    {        glPushMatrix();//Assign all particles to the bottom since that is where a fire typically starts at and randomize        if(!fire[p].start_assign)        {            fire[p].y_pos=min_y;            fire[p].start_assign=TRUE;        //Find the distance between two specified points -> (x1,y1) and (x2,y2)            if(limit_x1 <= 0 && limit_x2 <= 0)            {               distance=limit_x1-limit_x2; //If both are negative            }            else //If both are positive            {               if(limit_x1 < limit_x2) //If first x is greater than the second x               {                   distance=limit_x2-limit_x1;               }               else //If first x is smaller than the second x               {                   distance=limit_x1-limit_x2;               }            }        //If distance is negative then change it            if(distance < 0)            {               distance=-distance;            }        //Randomize particle location in an area between x1 and x2 to achieve that spread out effect            float range;            range=rand()%100*distance*.01;            if(limit_x1 < limit_x2) //If the first x is smaller            {               fire[p].x_pos=range+limit_x1;            }            else //If the second is smaller            {               fire[p].x_pos=range+limit_x2;            }        //Store original value of x so the flame will be uniform at the base again.            fire[p].x_store=fire[p].x_pos;        }//Color        if(r <= 1 && g <= 1 && b <= 1)        {            glColor4f(r,g,b,fire[p].alpha);        }        else        {            glColor4f(fire[p].red, fire[p].green,fire[p].blue,fire[p].alpha);        }//If a particle goes over its y max value or fades out then send it to the bottom        if((fire[p].y_pos > max_y-fire[p].size/2) || (fire[p].alpha < 0.0))        {            fire[p].y_pos=min_y;            fire[p].alpha=1.0;            fire[p].x_pos=fire[p].x_store;        }//Begin Positioning and Drawing        glTranslatef(fire[p].x_pos,fire[p].y_pos,fire[p].z_pos);        xia_PersonalDrawParticle(fire[p].size,fire[p].size);//Increment Particle Position and Fade        fire[p].y_pos+=fire[p].speed_move*speed_mod;        fire[p].alpha-=fire[p].speed_move*speed_fade;//Attraction towards a point        if(limit_x1 < limit_x2)        {            xTarget=distance/2+limit_x1;        }        else        {            xTarget=distance/2+limit_x2;        }        if(xTarget < fire[p].x_pos)        {            fire[p].x_pos-=fire[p].speed_move*speed_point;        }        else if(xTarget > fire[p].x_pos)        {            fire[p].x_pos+=fire[p].speed_move*speed_point;        }        glPopMatrix();    }}

Note the math involved to actually stipulate distances and move the particle - easy algerbra. It''s really easy to understand, and you will be hiting your head against the wall for not actually attempting it a long time ago.

Screen shot of the resulting fire:


Ask more questions if you are still unsure of what is happening.
how can i get partially transparent sprites for particles using allegro?

This topic is closed to new replies.

Advertisement