The Classic Model
Generally, a particle system consists of three parts:Particle Collection Collection
This contains a list of emitters to be drawn or updated. Also may have any adding/removing emitter logic. Sometimes this component is non existant, like in a highly object oriented architecture, where each object would control its own emitter. Even so, if an object had multiple emitters, it would have to control a list of them like this.
Particle Emitter
This spawns and controls a list of particles. The emitter has a specified position and time to live. Because it is in charge of spawning particles, it must store the initial state of particles. The emitter must also have the constraints on particle speed, direction, and generation rate.
Particle
This is the final, smallest part of the particle system. It renders itself and also has a time to live. Updating the particle means doing any physics calculations on its position.
The Problems
The problems with this system is that there is a lot of information passing from component to component that is largely the same. Particles themselves have a lot of data associated with them: position, velocity, acceleration, fade boolean, fade rate, gravity boolean, lifespan, image/animation. The emitter must generate all of this from: range of angle to spawn direction, jitter, and most of the elements listed above. What this usually results in is large constructor and function calls with about 10 parameters each. There are solutions to the classic model, such as using a convention over configuration approach or storing particle information in script files. These are perfectly valid solutions, and I've used the CoC solution in other projects.My Solution
What I did was eliminate the emitter component. The particles are stored directly in a list, contain their own initial state, constraints, and data. In addition, they have a maxGeneration number and a unique ID. Every time a particle is copied, the new particle's generation is one more than the particle that spawned it, and its ID is the same as the particle that spawned it. If its generation is maxGeneration, it does not copy itself. Essentially, to start a new emitter, you just add a particle configuration object to the list. The particle collection does the rest using a queue and set. Here is some pseudo code:for each particle in list: add (particle.id,particle.initial_params) to set update particle draw particle for each tuple in set: if(tuple.params.generation < tuple.params.maxGeneration) make new particle(tuple.id,tuple.params,tuple.params.id+1)A particle object is basically a Javascript object that specifies the behavior of the particle. It relies on resonable defaults, so it can be as big or small as you like:
var particle1 = { initialState : {x:134,y:575,v:.2,a:0}, ttl : 25, maxGen : 10, image : "particle_red_img", gravity : 0, copy_per_frame : 15, fade:1, }
Conclusion
I think in a Javascript environment, my scheme eliminates a lot of redundant code, which is good for reducing loading times and keeping with JS coding convention. For a language like C with no built in support for sets and variable sized arrays, it is probably best to stick with the classic model. A negative to my new system is that there is some added logic in copying particles and using the set. Like anything else, it all comes down to what makes you a more efficient programmer and what is the best fit for your project.I would love to hear other solutions or comments about how you handle particle systems.