Particle system #1 – Basic particles

Andreas | 2010-04-19 | Flash, Tutorial

As I am coming from a background of gameprogramming (and originally from the demo scene in the 90´s) am I very fond of small tips and tricks to create nice special effects. There are few project that I see out there that couldn’t be spiced up with a nice particle system so I decided to go through some basics of particle systems and how they can be used to create a variety of effects. In this first “chapter” I will just go through the basics of a system and how it works and then later on create a more flexible system that can be used in oh so many situations.

Now lets look at what we will create today:

Facewarp

Neat right? Let’s dive into the universe of particles…

For a programmer used to flash (which is a very “visual” language) it’s not especially hard to understand the concept of particles as it is nothing less than Displayobjects being placed on screen and adjusted using it’s normal parameters such as scale, rotation, color and position. In this exampel you see two different particle systems. One with the stars, adjusting their scale, alpha and position to create the illusion of the camera travelling forward in an endless space. The other system is the supernova with a few “particles” being placed in the center, scaled, rotated and then fading out with the alpha. Everything is blended with the awesome ADD-blendmode which must be the most used blendmode in any game/demo with some self respect. Both these systems origins from the center of the scene/stage so the Main class of this small app is probably very basic.

package
{
        import flash.display.Sprite;
        import se.xcom.effects.particle.NovaEmitter;
        import se.xcom.effects.particle.StarEmitter;

        public class LiteTest extends Sprite
        {
                public function LiteTest()
                {
                        super();
                       
                        // create the emitters and adding it to the middle of scene.
                        var nova:NovaEmitter = new NovaEmitter();
                        nova.x = stage.stageWidth*.5;
                        nova.y = stage.stageHeight*.5;
                        nova.scaleX = 2;
                        nova.scaleY = 2;
               
                        var stars:StarEmitter = new StarEmitter()
                        stars.x = stage.stageWidth*.5;
                        stars.y = stage.stageHeight*.5;
                       
                        addChild(nova);
                        addChild(stars);
                }
        }
}

Ok, lets take a look at one of the systems. All particle systems have some kind of “emitter”. You can see it as an object on the scene that is producing particles. It is not visible itself but the particles it “emits” is (most of the time) visible. The emitter is somehow the engine that decides how many particles is being generated, how they behave, rotate, scale etc.. You can see it as a located factory in the screen producing particles. Sometimes it shoots the particles out from the center of itself (just like nova in this example) but it could on the other hand let particles be spawned all around itself just like the stars system.

package se.xcom.effects.particle
{
        import flash.display.BlendMode;
        import flash.display.Sprite;
        import flash.events.TimerEvent;
        import flash.utils.Timer;
       
        import gs.TweenMax;
        import gs.easing.Linear;

        public class NovaEmitter extends Sprite
        {
                private var emitTimer:Timer
               
                // Time until next particle is emitted (ms).
                private static const EMIT_TIME:Number = 1500
               
                public function NovaEmitter()
                {
                        super();
                        emitTimer = new Timer(EMIT_TIME)
                        emitTimer.addEventListener(TimerEvent.TIMER,onTimer)
                        emitTimer.start()
                       
                        // creating a few particles to begin with.
                        for (var i:uint=0;i < 5;i++)
                        {
                                createParticle()
                        }
                }
               
                public function onTimer(e:TimerEvent)
                {
                        createParticle()
                }
               
                private function createParticle(prog:Number = 0)
                {
                        var part:Sprite = new Particle()  // is a simple bitmap in flash library converted as a Sprite.
                       
                        // modifying particle.
                        part.rotation = Math.random()*360
                        part.scaleX = part.scaleY = 0.3+Math.random()*0.3
                        part.blendMode = BlendMode.ADD
                        part.alpha = 0
                        var time:Number = Math.random()*7+5
                        var rot:Number = Math.random()*180-90
                        var scale:Number = Math.random()*1.6+0.6
                        var posX:Number = Math.random()*50-25
                        var posY:Number = Math.random()*50-25
                               
                        addChild(part)
                       
                        TweenMax.to(part,time,{x:posX,y:posY,rotation:String(rot),scaleX:scale,scaleY:scale,ease:Linear.easeOut, onComplete:killParticle,onCompleteParams:[part]})
                        TweenMax.to(part,time/4,{alpha:1,overwrite:false})
                        TweenMax.to(part,time*2/3,{delay:time/3,alpha:0,overwrite:false})
                }
               
                private function killParticle(obj:Sprite)
                {
                        this.removeChild(obj)
                        obj = null
                }
        }
}

As you can see the Nova-system creates a new particle every 1.5 sec. It sets a lot of random parameters for the sprite and then just tweens it using the badass library TweenMax (which can be found here. and then destroy’s itself neatly afterwards. Straightforward huh? The secret with the fluid animation of the nova is mostly because of the blendmode that I previously mentioned.
ADD does exactly what it says. It adds two colours (their rgb-values) into one. this is great for oh so many purposes. First of all it never gives you any black corners as if it’s black, it just shows the colour behind. Also it is a great way to mask out an effect with a black background without using alphachannels. In fact this is a much faster way for the processor to handle masking than alphachannels are.

Now let’s look at the stars. They are not different at all.

package se.xcom.effects.particle
{
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.utils.Timer;

import gs.TweenMax;
import gs.easing.Quad;

        public class StarEmitter extends Sprite
        {
                private var emitTimer:Timer
                private static const STAR_TIME:Number = 4
                private static const EMIT_TIME:Number = 1
               
                public function StarEmitter()
                {
                        super();
                        emitTimer = new Timer(EMIT_TIME)
                        emitTimer.addEventListener(TimerEvent.TIMER,onTimer)
                        emitTimer.start()
                       
                        // initiating with some stars
                        for (var i:uint=0;i < 40;i++)
                        {
                                createParticle()
                        }
                }
               
                public function onTimer(e:TimerEvent)
                {
                        for (var i:uint=0;i < 3;i++)
                        {
                                createParticle()
                        }
                }
               
                private function createParticle()
                {
                        var part:Sprite = new Star()  // is a simple bitmap in flash library converted as a Sprite.
                       
                        // modifying particle.
                        var z:Number = Math.random();  // z = 1 means it is in the same Z as the camera
                        var posX:Number = (Math.random()*1500-750)
                        var posY:Number = (Math.random()*1500-750)
                       
                        part.blendMode = BlendMode.ADD
                        part.alpha = 0
                        part.scaleX = part.scaleY =0
                        part.x = posX*z
                        part.y = posY*z
                        addChild(part)
                       
                        var t:Number = STAR_TIME*(1-z)
                        var endScale:Number = Math.random()
                       
                        TweenMax.to(part,t,{x:posX,y:posY,scaleX:endScale,scaleY:endScale,ease:Quad.easeIn, onComplete:killParticle,onCompleteParams:[part]})
                        TweenMax.to(part,t/2,{alpha:1,overwrite:false})
                        TweenMax.to(part,t*1/2,{delay:t*1/2,alpha:0,overwrite:false})
                }
               
                private function killParticle(obj:Sprite)
                {
                        this.removeChild(obj)
                        obj = null
                }
        }
}

The only difference in this class is the way the particles are positioned and adjusted in the tweens. To give you the illusion of depth/moving forward, I create a fake Z-axis value with the span of 0-1. 1 would be closest to the viewer and positioning the star at posX on the screen with the full scale of 1. Any other lower z-value brings the star towards the middle of the screen and also lowers the scale giving the illusion of the star moving further away from the viewer. Some fancy adjusting with alpha tweening in and out + a nice easeIn so the stars that is further away moves more slowly adds to the illusion even more.

Now, there is a great basic example how to use a few particles. Now is it a real system yet?? Well, there are a lot you could do to make it more dynamic. First of all we shouldn’t have to create 2 classes for these very similar classes. In the next part I ill give you the basics to create a more dynamic particle system where you can tweak a lot of parameters to be able to create snow, rain, fire, smoke, explosions etc… exciting, huh?
Have fun!

/Andreas


Responses


  1. Great, I never knew this, thanks.



  2. I’ve recently started a blog, the information you provide on this site has helped me tremendously. Thank you for all of your time & work.



  3. Hola, Super post, tienen que marcarlo en Digg

    Truden



  4. Nice dispatch and this post helped me alot in my college assignement. Thanks you seeking your information.



  5. found your site on del.icio.us today and really liked it.. i bookmarked it and will be back to check it out some more later



  6. Pretty nice post. I just stumbled upon your blog and wanted to say that I have really enjoyed browsing your blog posts. In any case I’ll be subscribing to your feed and I hope you write again soon!



  7. Terrific work! This is the type of information that should be shared around the web. Shame on the search engines for not positioning this post higher!



  8. [...] To view this application in a new window as well get the necessary codes please follow this link. [...]



  9. You would get better performance if you did not recreate the particle every iteration — keep a cache of them and redraw them. You should spin out the cache one time – then you can place the objects where you need them by rolling through the cache. Also if you create a large bitmap and draw the sprites onto the bitmap instead, you can get the same effect with much greater efficiency. You can fade the previous image pass each frame and have a nice trailing effect coming off your particles.

    All in all – nice work, though.



  10. One more thing…
    Using a timer to animate is less efficient than using enterFrame. The timer can overrun the frame rate and cause dropped drawing passes and general weirdness. The only caveat is that you must get the new drawing time each pass so the tween is correctly placed. But all these tweaks would just be improvements on your already cool work. Neato stuff!



  11. Andreas

    17/08/2010
    10:43

    Hi Scott.

    You’re of course correct in all suggestions. The code could be optimized in many ways to get a smoother and faster particleengine. In this case I just wanted to leave these things aside as I wanted to show a very basic introduction of how to think when it comes to particles. I’m about to write a more complex tutorial with some “real” systems instead of this basic one.

    When it comes to enterFrame the user may not want to generate a particle every frame_tick. Now in the next exampel I will use enter frame both in the movements and in the emitter (that generates the particles) still I will need to use “delta-timing” to assure that the particles emits and flows in the same speed even though the framerate drops. Stay tuned :)



  12. Hello,

    Awesome effect you got there. Fascinating.

    I’m really curious about the yellow spike that comes out of the yellow gas. How do you achieve the effect? I couldn’t find it in your code.



  13. thanks for this amazing piece of code. I found the result really beautiful with some violet colors of supernova bitmap too. Thanks for sharing it!



  14. It’s really a nice and helpful piece of information. I’m glad that you shared this helpful info with us. Please keep us informed like this. Thanks for sharing.



  15. Hi Scott. You’re of course correct in all suggestions. The code could be optimized in many ways to get a smoother and faster particleengine. In this case I just wanted to leave these things aside as I wanted to show a very basic introduction of how to think when it comes to particles. I’m about to write a more complex tutorial with some “real” systems instead of this basic one. When it comes to enterFrame the user may not want to generate a particle every frame_tick. Now in the next exampel I will use enter frame both in the movements and in the emitter (that generates the particles) still I will need to use “delta-timing” to assure that the particles emits and flows in the same speed even though the framerate drops. Stay tuned :)



LEAVE COMMENT