Agence Elysium - Montreal Web Marketing/Development/Design | Advertising | Communication

Font Size

Profile

Menu Style

Cpanel
 

This is a community page for media and technologies developers. Learn ActionScript3.0, Animation, Flex, Air, Effects, XML, Web Design, Design Theory, Typography, Workflow and more.

FacebookTwitterFeedLinkedinYoutube

Tutorials

Flash Actionscript - Squeezing More Juice Out of the Flash Player

User Rating:  / 1
PoorBest 
preview.jpgAutor: Mark Dunne
Source: Activetuts+
Tutorial Details:
  • Difficulty: Intermediate
  • Platform: Flash (Flash Player 10.1)
  • Language: AS3
  • Software used: Flash Professional CS4+, FlashDevelop
  • Estimated Completion Time: 60 mins

In this tutorial you’ll build an extreme particle system whilst learning how to squeeze more efficient goodness out of the Flash Player than you ever thought possible!


Final Result

Here are a couple of examples of what we’ll be working towards:

{notr_thumbs}

demo_link_particles

EPILEPSY WARNING:
Please don’t view this demo if you are likely to suffer from epileptic attacks or loss of consciousness, particularly when looking at some types of strong flashing lights, rapid succession of images, simple geometric shapes, flashes or explosions.


Introduction: What Will You Learn?

Many users, from beginners to advanced, can still be seen using less than efficient Actionscript 3.0. In all likelyhood, this is because the efficient ways are made to sound a bit more difficult and aimed at highly advanced users. This tutorial will show you that these methods can be used by everyone, and they’re more useful than you may think!

The aim of this tutorial is to let you tackle those tasks that require working with a lot of data, very fast, with ease.

Helpful Hint: This tutorial will feature a lot of coding so I recommend using a more user friendly coding interface. I recommend FlashDevelop, it has some of the best code hinting around and best of all, it’s completely free! But I’m sure most of you will just copy and paste if do anything at all :)


Step 1 Check out That FPS!

At the heart of making all things efficient in the Flash Player is the FPS (Frames Per Second) of your SWF. The proffered target of your SWF can be set in the Flash Professional interface or, a new feature in Actionscript 3.0, you can change the FPS of the stage at runtime.


	stage.frameRate = 30;

	trace(stage.frameRate); //30

	

However, this will only ever get and set the target FPS (what the Flash Player will attempt to play at), which makes it pretty useless for preformance testing. You might think that the lovely folks over at Adobe would made a nice neat way to find you real FPS but, nope. You’ve got to do the math for yourself. Let’s take a look.

FPS can be defined as the time difference between the current frame and the last.
So all we need is some way to track the time difference from this frame to the previous frame of the SWF. This uses the document class feature. If you are unsure how to use this, check out this Quick Tip on how to use a document class.


	package  {

	 

	    //imports

	 

	    import flash.events.Event;

	    import flash.utils.getTimer;

	    import flash.display.MovieClip;

	 

	    public class FPSCalculator extends MovieClip {

	 

	        //variable to hold the current time

	        private var currentTime:int = 0;

	 

	        public function FPSCalculator() {

	 

	            //add the enter frame listener, this is fired when the SWF updates to a new frame

	            stage.addEventListener(Event.ENTER_FRAME, onFrameLoop);

	        }

	 

	        private function onFrameLoop (evt:Event):void{

	 

	            //for the sanity of the fellow developers, try to put each task into a seperate function.

	            //this makes it infinitely easier to read for them and yourself on a large project or when you come back to and old one

	            //since the getTimer() function returns the played time in milliseconds and we want FPSecond, we divide it into 1000

	            var fps:Number = (1000 / timeDifference);

	 

	            trace(fps);

	        }

	        //this is a get function so it can be referenced just like a variable, without the brackets on the end like a normal function

	        private function get timeDifference ():int{

	 

	            //the getTimer() function returns the total played time of the SWF in milliseconds

	            var totalPlayedTime:int = getTimer();

	 

	            //The difference in time from the previous frame to this frame will to calculated here

	            var timeDifference:int = (totalPlayedTime - currentTime);

	 

	            //The currentTime is set to the total played time so it is ready for the next frame

	            currentTime = getTimer();  

	 

	            //return the difference in time

	            return timeDifference

	        }

	    }

	}

	

We will use this function as the benchmark for comparing the efficiency of different methods from here on. As you might have noticed, we use the function to calculate the time difference, not the FPS. This is because tracing the time difference is actually much more useful and easier to read when we get to the speed tests. Calculating the FPS only becomes useful when we are putting everything together at the end.


Tip #1 Arrays vs. Vectors

Have you ever needed to store a whole bunch of numbers, strings or objects in a list? Of course you have! But the question is – have you been doing it right?

If the first thing you think think of when making a list in AS3 is an Array, then this tip is for you. A Vector is exactly the same as an ordinary Array except for one fact, it is a typed Array. This means that you can only populate it with one type of item. For instance, you can put a number and a string in the same Array, but not in a Vector.

Let’s take a look at the only difference between an Array and a Vector, declaring the Vector.


	private function Arrayvs.VectorDifferences ():void{

	 

	    //delare the array and the vector, this is the only difference between the two

	    var myArray:Array = new Array();

	    var myVector:Vector.<String> = new Vector.<String>;

	 

	    //populate them in the same way

	    myArray.push("this", "is", "an", "Array");

	    myVector.push("this", "is", "a", "Vector");

	 

	    //call elements and length in the same way

	    trace( myArray[myArray.length - 1] );   //Array

	    trace( myVector[myVector.length - 1] ); //Vector

	 

	    //The following creates an error - "Access of possibly undefined property x through a reference with static type String."

	    trace( myVector[myVector.length - 1].x );

	    //The Flash Player casts the movieclip as a string and a string does not have an 'x' value.

	 

	}

	

As seen above, declaring the vector is the only difference between the two types. To have the Vector hold another object, just replace String with your object. For holding MovieClips for example:


	var myVector.<MovieClip> = new Vector.<MovieClip>;

	

How does this make it more useful? If all the elements in a Vector are the same type then the Flash Player can zip through them much much quicker because it knows what’s coming up and doesn’t have to test the next element every time (this is true even when declaring a variable type, so never leave it out!).

How big is the difference? Surprisingly big actually! Let’s take a look at our first speed test.

Speed Test #1 Array vs. Vector


	package  {

	 

	    //imports

	 

	    import flash.utils.getTimer;

	    import flash.display.MovieClip;

	 

	    public class Arrayvs.Vector extends MovieClip {

	 

	        private var currentTime:int = 0;

	 

	        //we want to read and write into the array and vector 10,000,000 times.

	        //this will provide a good indication of the speed difference

	        //dont worry, it wont crash you computer but the FLash Player will pause for about 3 to 5 seconds

	        private var n:int = 10000000

	 

	        //Declare the Array and Vector

	        private var myArray:Array = new Array();

	        private var myVector:Vector.<int> = new Vector.<int>;

	 

	        public function Arrayvs.Vector() {

	 

	            //time test for writing to the array and vector

	            write();

	 

	            trace("----");

	 

	            //time test for reading from the array and vector

	            read();

	        }

	        private function write ():void{

	 

	            trace("Writing Times");

	            timeDifference

	 

	            //for n times, push i into the array

	            for(var i:int = 0; i < n; i++){

	                myArray.push(i);

	            }

	 

	            //trace the time taken

	            trace("Array: " + timeDifference + "ms");

	 

	            //for n times push j into the vector

	            for(var j:int = 0; j < n; j++){

	                myVector.push(j);

	            }

	 

	            //trace the time taken

	            trace("Vector: " + timeDifference + "ms");

	        }

	        private function read():void{

	 

	            var num:int = 0;

	            trace("Reading Times");

	            timeDifference

	 

	            //for n times, set num to the corresponding array value

	            for(var i:int = 0; i < n; i++){

	                num = myArray[i];

	            }

	            //trace the time taken

	            trace("Array: "+ timeDifference + "ms");

	 

	            //for n times, set num to the corresponding vector value

	            for(var j:int = 0; j < n; j++){

	                num = myVector[j];

	            }

	            //trace the time taken

	            trace("Vector: " + timeDifference + "ms");

	        }

	        private function get timeDifference ():int{

	            var totalPlayedTime:int = getTimer();

	            var timeDifference:int = (totalPlayedTime - currentTime);

	            currentTime = getTimer();

	            return timeDifference

	        }

	        private function Arrayvs.VectorDifferences ():void{

	 

	            //delare the array and the vector, this is the only difference between the two

	            var myArray:Array = new Array();

	            var myVector:Vector.<String> = new Vector.<String>;

	 

	            //populate them in the same way

	            myArray.push("this", "is", "an", "Array");

	            myVector.push("this", "is", "a", "Vector");

	 

	            //call elements and length in the same way

	            trace( myArray[myArray.length - 1] );   //Array

	            trace( myVector[myVector.length - 1] ); //Vector

	 

	            //pushing in a non-string value

	            myVector.push(new MovieClip());

	 

	            //The following creates an error - "Access of possibly undefined property x through a reference with static type String."

	            //trace( myVector[myVector.length - 1].x );

	            //The Flash Player casts the movieclip as a string and a string does not have an 'x' value.

	        }

	    }

	}

	

This outputs the following:


	Array: 2073ms

	Vector: 1476ms

	----

	Reading Times

	Array: 190ms

	Vector: 134ms

	

You shouldn’t get exactly the same results as what I have here of course, you will almost never get the same set of values yourself even as this depends on how much effort your CPU can push into this at runtime.

The first pair of values comes from writing into the array and vector respectively. We can see that the Vector shaved off almost 0.6 seconds, quiet a substancial amount if we need to do something similar 24 (standard FPS for flash movies) times a second. After all, 1/24th of a second is just over 0.04 seconds.

An even bigger percentage difference can be found when you are reading from an Array vs. from a Vector, and this is luckily what you will need to do most of the time every frame.

Hopefully, after reading this section you should be comfortable with using Vectors in your projects for that extra kick of efficiency.


Tip #2 Event Listeners

Like all things in Flash, there’s more than one way to solve a problem, Event Listeners are no exception. In this tutorial we will look two methods of attaching the Event.ENTER_FRAME listener to your particles.

Method #1 One Listener per Particle

The idea behind this approach is that you attach a listener to each of your particles and direct them to a set function. This isn’t ideal for what we have in mind but remember a particle, in terms of programming, doesn’t have to be a single dot. For instance, this method might be preferred when going between Flash based webpages or objects that are treated differently in the listener function. Let’s take a look.


	private function createPages():void{

	    //create 10 webpages

	    for(var i:int = 0; i < 10; i++){

	 

	        //create a new webpage as a movieclip

	        var webpage:MovieClip = new MovieClip();

	 

	        //add the listener

	        webpage.addEventListener(Event.ENTER_FRAME, onWebpageLoop);

	    }

	}

	private function onWebpageLoop (evt:Event):void{

	 

	    //all webpages call this function every frame

	 

	    //evt.target is the webpage

	}

	

Method #2 One Listener to Rule Them All…

The second method is the one that we will be using and the preferred method when dealing with a lot of similar objects. The idea here is to attach a single listener to only one object – usually the stage – which then loops through each of the particles and tells each one what to do on each frame. This method is slightly more complex as we need some way to reference the particles, so we put them into a Vector. Let’s take a look.


	private function createPages2():void{

	 

	    //create 10 webpages

	    for(var i:int = 0; i < 10; i++){

	 

	        //add a movieclip to the webpagesHolder

	        //reducing the number of variables and steps used increases the speed

	        webpagesHolder.push(new MovieClip());

	    }

	    //add the listener

	    stage.addEventListener(Event.ENTER_FRAME, onStageLoop);

	}

	private function onStageLoop (evt:Event):void{

	 

	    //called only once when the stage changes frame

	 

	    for(var i:int = 0; i < 10; i++){

	 

	        //webpageHolder[i] is the webpage

	    }

	}

	

Speed Test #2 Methods of Using Event Listeners

Unfortunately there is no hard and fast way to accurately check the difference in speed between these two methods because of the inconsistencies of the calling process while using method one (at least not an accurate method that won’t require its own complete tutorial!)

Take my word for it, using method two is far better for use in particle systems because of two major factors:

  • Huge speed increase
  • Much easier to reference other particles from particles

After reading this section you should now know how to comfortably keep track of and reference many particles. Remember, method one for objects that should treat their listener function differently and method two for many objects that all should be treated in exactly the same or a very similar way in their listener function.


Tip #3 Building Your Particle

The next step towards our complete particle system is building the right particle for ourselves. It might so happen that you’ll need a full MovieClip for each particle, to make use of frames and such, but for us, a MovieClip is a huge overkill. All our particle is is a placeholder for a bunch of values that need to be kept together and relate to each other. This allows us to drastically reduce the size of the class used.

The following is the basic class we can use for our particle.


	package  {

	 

	    //imports

	    import flash.geom.Vector3D;

	 

	    //notice that the class extends nothing because there is no need

	    public class Particle {

	 

	        //define the Vector3D objects to hold the position and velocity values

	        private var pos:Vector3D = new Vector3D(0, 0, 0);

	        private var vel:Vector3D = new Vector3D(0, 0, 0);

	 

	        public function Particle(stageRect:Rectangle) {

	 

	        }

	        public function update ():void{

	 

	            //update the position according to the velocity in that direction

	            pos.x += vel.x;

	            pos.y += vel.y;

	        }

	 

	        //the getter methods that will be used to read the position of the particle

	        public function get x ():Number{ return pos.x }

	        public function get y ():Number{ return pos.y }

	    }

	}

	

Notice that it does not have any base class (i.e. it does not extend anything) and therefore it is ‘born’ without properties that you might use regularly, for instance the ‘x’ and ‘y’ values. To correct this, we use build our own getter methods to read these values. These values are then passed into a Vector3D object. A Vector3D object is basically a Vector which holds three variables and an optional fourth variable. The difference is that you can reference these values as ‘x’, ‘y’, ‘z’ and ‘w’ respectively, The ‘w’, which is optional, could be used to old a rotation value for example. This makes this type of holder perfect for what we need.

(We could even create these properties as public Number variables within the class directly, without using any Vector3D objects at all… but let’s stick with what we’ve got.)

But how exactly does creating our own class help? Let’s make a quick memory test to find out! Remember to save this, your document class and the Particle class in the same destination.


	package  {

	 

	    //imports

	    import flash.sampler.getSize;

	    import flash.display.MovieClip;

	 

	    public class BuildingYourParticle extends MovieClip {

	 

	        public function BuildingYourParticle() {

	 

	            //simulate 100,000 of the respective object

	            var n:int = 100000

	 

	            //define the object

	            var p:Particle = new Particle();

	            var m:MovieClip = new MovieClip();

	 

	            //use the getSize() method the find the memory used for n of m and p. Convert them to megabyte format

	            var pSizeTotal:Number = (getSize(p) * n) / (1024 * 1024);

	            var mSizeTotal:Number = (getSize(m) * n) / (1024 * 1024);

	 

	            //trace the respective sizes to two decimal places

	            trace("Particle Memory: " + pSizeTotal.toFixed(2) + "mb");

	            trace("MovieClip Memory: " + mSizeTotal.toFixed(2) + "mb");

	        }

	    }

	}

	

This Outputs something like the following:


	Particle Memory: 1.53mb

	MovieClip Memory: 40.05mb

	

Speed Test #3 Our Particle vs. Movieclips

As you can see, there is a massive difference between the size of our class and the movieclip. Freeing up all this space allows more space on your RAM and so it allows the whole Flash Player to run quiet a bit faster. How much faster? Let’s take a look at that too! Much of this code is the same as our Array vs. Vector Speed test.


	package  {

	 

	    //imports

	    import flash.utils.getTimer;

	    import flash.display.MovieClip;

	 

	    public class ParticleSpeedTest extends MovieClip {

	 

	        private var n:int = 100000

	        private var currentTime:int = 0;

	 

	        //Declare the respective holders

	        private var particleHolder:Vector.<Particle> = new Vector.<Particle>;

	        private var movieclipHolder:Vector.<MovieClip> = new Vector.<MovieClip>;

	 

	        public function ParticleSpeedTest():void {

	 

	            //time test for writing a new particle or movieclip

	            write();

	 

	            trace("----");

	 

	            //time test for reading from a particle or movieclip

	            read();

	        }

	        private function write ():void{

	 

	            trace("Writing Times");

	            timeDifference

	 

	            //for n times, push a new particle into the vector

	            for(var i:int = 0; i < n; i++){

	                particleHolder.push(new Particle());

	            }

	 

	            //trace the time taken

	            trace("Particle: " + timeDifference + "ms");

	 

	            //for n times push a new movieclip into the vector

	            for(var j:int = 0; j < n; j++){

	                movieclipHolder.push(new MovieClip());

	            }

	 

	            //trace the time taken

	            trace("MovieClip: " + timeDifference + "ms");

	        }

	        private function read():void{

	 

	            var num:int = 0;

	            trace("Reading Times");

	            timeDifference

	 

	            //for n times, set num to the corresponding particle's 'x' value

	            for(var i:int = 0; i < n; i++){

	                num = particleHolder[i].x;

	            }

	            //trace the time taken

	            trace("Particle: "+ timeDifference + "ms");

	 

	            //for n times, set num to the corresponding movieclip's 'x' value

	            for(var j:int = 0; j < n; j++){

	                num = movieclipHolder[j].x;

	            }

	            //trace the time taken

	            trace("MovieClip: " + timeDifference + "ms");

	        }

	        private function get timeDifference ():int{

	            var totalPlayedTime:int = getTimer();

	            var timeDifference:int = (totalPlayedTime - currentTime);

	            currentTime = getTimer();

	            return timeDifference

	        }

	    }

	}

	

This should output something similar to the following:


	Writing Times

	Particle: 334ms

	MovieClip: 2096ms

	----

	Reading Times

	Particle: 16ms

	MovieClip: 22ms

	

As you can see from these results the real difference is in the writing times; this is because the Flash Player has to draw on more resources (the base classes of the MovieClip and each subsequent object) to complete this. In our main project this isn’t any real issue since we only need to create new Particles at the beginning but we will see an example later on when pushing new Particles in all the time becomes necessary. The difference in reading times is almost neglectable since both classes use their getters for the ‘x’ property in the same way.

After reading this section you should be comfortable with building your base class for your particle’s needs.


Tip #4 Introduction to Bitmaps

Bitmaps, and their partner in crime BitmapData, are usually some of the two most confusing steps for a beginner, mainly because what they are generally used for is higher level stuff. Here I will give a short introduction on some of the basic and most used methods regarding the Bitmap and BitmapData classes.

What are They?

In the quickest explanation possible:

  • The Bitmap class represents display objects that represent bitmap images
  • The BitmapData class Let’s you work with the data (pixels) of a Bitmap object

Basically, the Bitmap class displays what the BitmapData class tells it to. They go hand in hand practically always.

Drawing with Bitmaps

The BitmapData class does not have a graphics property of its own, yet it remains one of the most important classes for Flash graphics! How? It draws the shapes of other classes. Let’s take a look at how to draw a simple circle using Bitmaps and BitmapData.


	public function SimpleCircle():void {

	 

	    //define the radius of the circle

	    var radius:int = 30;

	 

	    //draw a circle the normal way. Notice that you do not add the shape that is to be drawn to the display list

	    var circleShape:MovieClip = new MovieClip();

	    circleShape.graphics.beginFill(0x555555, 1);

	    circleShape.graphics.drawCircle(radius, radius, radius);

	    circleShape.graphics.endFill();

	 

	    //create the bitmap and add it to the display list

	    var bmd:BitmapData = new BitmapData(radius * 2, radius * 2, true);

	    var bm:Bitmap = new Bitmap(bmd);

	    stage.addChild(bm);

	 

	    //draw the shape

	    bmd.draw(circleShape);

	}

	

This results in a simple grey circle touching the top left corners of the stage when run. The draw() method is simply a snapshot of the movieclip at that time so in theory we could move the circleShape object around an continue to draw it to give the effect of many circles. This is how a lot of drawing within bitmaps is done.

The setPixel() Method

Because at the base of all graphics are raw pixels, this method becomes very important for creating effects using the Bitmap and BitmapData classes. It allows you to change to color of a pixel inside the BitmapData area. Heres how that’s done.


	package {

	 

	    //imports

	    import flash.events.Event;

	    import flash.display.Bitmap;

	    import flash.display.BitmapData;

	    import flash.display.MovieClip;

	 

	    public class SetPixelMethod extends MovieClip {

	 

	        //define variables

	        private var n:int = 50;

	        private var w:int;

	        private var h:int;

	        private var bm:Bitmap

	        private var bmd:BitmapData;

	 

	        public function SetPixelMethod ():void {

	 

	            w = stage.stageWidth;

	            h = stage.stageHeight;

	 

	            //create the bitmap data the width and height of the stage that is not transparent and grey in color

	            bmd = new BitmapData(w, h, false, 0x222222);

	            bm = new Bitmap(bmd);

	 

	            addChild(bm);

	            addEventListener(Event.ENTER_FRAME, onFrameLoop);

	        }

	        private function onFrameLoop (evt:Event):void {

	            for (var i:int = 0; i < n; i++ ) {

	 

	                //randomly pick the x,y coordinates to set the new pixel color

	                var px:int = Math.random() * bmd.width;

	                var py:int = Math.random() * bmd.height;

	 

	                //give the pixel a random color

	                var pc:uint = Math.random() * 0xffffff;

	 

	                //set the pixel at (px,py) to that color

	                bmd.setPixel(px, py, pc);

	            }

	        }

	    }

	}

	

You should end up with something that looks like this, with more dots appearing on every frame:

b_400_0_16777215_00_http___d2fhka9tf2vaj2.cloudfront.net_tuts_213_extremeParticleSystem_tutorial_res_setpixelmethod.PNG

This is the graphics style we will use for our system. Don’t worry if it looks kinda terrible right now, at the end you’ll see how we can make these look much better with a whole bunch of effects.

The lock() and unlock() methods

These methods are some of the least used but most helpful regarding bitmap. In fact I would have never known about there existence if it wasn’t for a great little site called WonderFl where members regularly boast massive particle systems, the truth is that these were the inspiration of this tutorial!

You might be hard pushed to find a similar set of methods to use together, this is how they work:


	public function LockUnlockMethods ():void {

	    //Draw a basic circle, same in steps before this

	    var radius:int = 30;

	    var circleShape:MovieClip = new MovieClip();

	    circleShape.graphics.beginFill(0x555555, 1);

	    circleShape.graphics.drawCircle(radius, radius, radius);

	    circleShape.graphics.endFill();

	 

	    //create the bitmap/bitmapdata

	    var bmd:BitmapData = new BitmapData(radius * 2, radius * 2, true);

	    var bm:Bitmap = new Bitmap(bmd);

	    stage.addChild(bm);

	 

	    //lock the bitmap

	    bmd.lock();

	 

	    //draw resources

	    bmd.draw(circleShape);

	 

	    //unlock and update the bitmap

	    bmd.unlock();

	}

	

In essence, you wrap the lock() and unlock() methods around the point in which your code is changing the appearance of a Bitmap. While for the situation shown above they aren’t all that useful, in a large scale system with many thousands of changes to the bitmap they speed up the process by a long shot. This is because the Bitmap and BitmapData classes like to have a lot going on at once, the more changes you can cram into a single step the better they become! These methods are great for that as they put on hold all changes made to the locked bitmap until it is unlocked, and this means it isn’t re-rendered during that time, which speeds up the process.

Clearing Your Bitmap

The BitmapData class doesn’t offer a clear cut method to create wipe its data, so here are two common ways to do so. We can reuse all the the code from learning about the setPixel method.


	package {

	 

	    //imports

	    import flash.geom.Point;

	    import flash.events.Event;

	    import flash.geom.Rectangle;

	    import flash.display.Bitmap;

	    import flash.display.BitmapData;

	    import flash.display.MovieClip;

	    import flash.filters.BlurFilter;

	 

	    public class ClearBitmap extends MovieClip {

	 

	        //define variables

	        private var n:int = 50;

	        private var w:int;

	        private var h:int;

	        private var bm:Bitmap

	        private var bmd:BitmapData;

	        private var clearRect:Rectangle;

	 

	        public function ClearBitmap ():void {

	 

	            w = stage.stageWidth;

	            h = stage.stageHeight;

	            bmd = new BitmapData(w, h, false, 0x222222);

	            bm = new Bitmap(bmd);

	            addChild(bm);

	            addEventListener(Event.ENTER_FRAME, onFrameLoop);

	 

	            //create the bitmap data the width and height of the stage that is not transparent and grey in color

	            clearRect = new Rectangle(0, 0, w, h);

	        }

	        private function onFrameLoop (evt:Event):void {

	 

	            //use one of these methods to clear your bitmap

	            //bmd.fillRect(clearRect, 0x222222);

	            bmd.applyFilter(bmd, bmd.rect, new Point(0, 0), new BlurFilter(1.1, 1.1, 2));

	 

	            for (var i:int = 0; i < n; i++ ) {

	                var px:int = Math.random() * bmd.width;

	                var py:int = Math.random() * bmd.height;

	                var pc:uint = Math.random() * 0xffffff;

	                bmd.setPixel(px, py, pc);

	            }

	        }

	    }

	}

	

If you use the clearRect method, it simply draws a completely new grey rectangle over everything else. This isn’t as careless method as it sounds at first because remember all you are ever doing with a Bitmap is changing the color of a set of pixels; overlapping items makes no difference what so ever.

The second option blurs out the dots by merging them into the background. We will use this later to create more interesting effects. You should note that this method requires a significantly longer compute time and should be avoided if efficiency is your only concern. We will leave this until the last bit of the tutorial where we are not aiming for maximum efficiency.

Speed Test #4 Bitmap Drawing vs. MovieClips Drawing

This is our final speed test and here we will test the speed difference between drawing multiple shapes using the Bitmap classes and the MovieClip class. Here is is:

CAUTION: The regular createMovieClipCircles() method is so inefficient that you should not let it run for more than a few seconds. The Flash Player will continue to slow down until it eventually grinds to a halt.


	package {

	 

	    //imports

	    import flash.events.Event;

	    import flash.display.Bitmap;

	    import flash.utils.getTimer;

	    import flash.display.BitmapData;

	    import flash.display.MovieClip;

	 

	    public class Bitmapvs.MovieClip extends MovieClip {

	 

	        //define variables

	        private var w:int;

	        private var h:int;

	        private var r:int = 5;

	        private var n:int = 500;

	        private var currentTime:int = 0;

	        private var bm:Bitmap;

	        private var bmd:BitmapData;

	        private var bmShape:MovieClip = new MovieClip();

	 

	        public function Bitmapvs.MovieClip ():void {

	 

	            w = stage.stageWidth;

	            h = stage.stageHeight;

	            bmd = new BitmapData(w, h, true, 0);

	            bm = new Bitmap(bmd);

	            addChild(bm);

	            addEventListener(Event.ENTER_FRAME, onFrameLoop);

	        }

	        private function onFrameLoop (evt:Event):void {

	            timeDifference;

	            //use one of these functions at a time

	 

	            //this one is to create points using the movieclip method

	            //createMovieClipCircles();

	 

	            //this one is to create points using the bitmap method

	            createBitmapCircles();

	        }

	        private function createMovieClipCircles ():void {

	 

	            //this will create a new movieclip for each circle

	            for (var i:int = 0; i < n; i ++) {

	                var m:MovieClip = new MovieClip();

	                drawCircle(m);

	                addChild(m);

	            }

	            //trace the fps

	            trace(1000 / timeDifference);

	        }

	        private function createBitmapCircles ():void {

	 

	            //this will draw the same movieclip in different places over and over

	            bmd.lock();

	            for (var j:int = 0; j < n; j ++) {

	                drawCircle(bmShape);

	                bmd.draw(bmShape);

	            }

	            bmd.unlock();

	            //trace the fps

	            trace(1000 / timeDifference);

	        }

	        private function drawCircle (m:MovieClip):void {

	 

	            //create the same function to draw circles for both to keep it fair and organised

	            m.graphics.clear();

	            m.graphics.beginFill(Math.random() * 0xffffff);

	            m.graphics.drawCircle(Math.random() * w, Math.random() * h, r);

	            m.graphics.endFill();

	        }

	        private function get timeDifference ():int{

	            var totalPlayedTime:int = getTimer();

	            var timeDifference:int = (totalPlayedTime - currentTime);

	            currentTime = getTimer();

	            return timeDifference

	        }

	    }

	}

	

What you should find after using both methods is that:

  • The createMovieClipCircles() method grinds to a halt after a few seconds
  • The createBitmapCircles() can keep running all day at 24/24 FPS and not slow down

The createMovieClipCircles method is so inefficient because it needs to add each circle to the display list which leaves the Flash Player struggling to hold the weight of them all. This is why we must use the Bitmap and BitmapData classes in our particle system.

From reading this section you should now be familiar with many of the methods we can use from the Bitmap and BitmapData classes to build our particle system. I have covered much of what we need to know to build such a system so now I think its time we dive in!


Building the System

Our system won’t be pretty, but dang it will be fast! It will be composed of two classes, the Particle class we made earlier and the controller class that keeps check of everything. Our aim is to build a system that will brush off 100k particles, laugh at 150k particles and take on 200k comfortably. Of course this depends on your system, but mine is about six years old and hasn’t blown up yet so I’m sure most of you will be okay.

First we will start with the basic Particle class, much of the code that you will see will be the same as in the steps previously described.


	package  {

	 

	    //imports - the less the better

	    import flash.geom.Vector3D;

	    import flash.geom.Rectangle;

	 

	    public class Particle {

	 

	        //define the position and velocity Vector3D objects

	        private var pos:Vector3D = new Vector3D(0,0);

	        //the velocity of the particle is between +3 and -3 for x and y

	        private var vel:Vector3D = new Vector3D(rand(3), rand(3));

	 

	        //define the bounds and default color of the particle

	        private var bounds:Rectangle;

	        private var color:uint = 0x555555

	 

	        public function Particle(stageRect:Rectangle) {

	 

	            //the bounding area of the stage is passed into the constructor

	            //we do not need to pass the entire instance of the stage as this will require more memory

	            bounds = stageRect;

	 

	            //spawn the particle at a random point within the bounds

	            pos.x = Math.random() * bounds.width;

	            pos.y = Math.random() * bounds.height;

	 

	            //give a handful a different color so that we can see particles moving more easily

	            if(Math.random() < 0.005) color = 0xFFFFFF

	        }

	        private function rand(n:int):Number{

	 

	            //this function returns a random number between -n and n

	            return n - (n * 2 * Math.random())

	        }

	        public function update ():void{

	 

	            //add the respective velocities to the position

	            pos.x += vel.x;

	            pos.y += vel.y;

	 

	            //check if the particle is outside the bounds of the rectangle

	            checkBounds();

	        }

	        private function checkBounds():void{

	 

	            //this function simply checks the x,y position values and if they are

	            //bigger or greater than the bounds reverse the respective velocity (direction) is reversed

	            if(pos.x < 0 || pos.x > bounds.width) vel.x *= -1;

	            if(pos.y < 0 || pos.y > bounds.height) vel.y *= -1;

	        }

	        //the get methods for the color and x,y values of the particle

	        public function get c ():uint{ return color }

	        public function get x ():Number{ return pos.x }

	        public function get y ():Number{ return pos.y }

	    }

	}

	

In keeping with OOP traditions, our particle is as encapsulated (self-contained) as possible. It defines its own x- and y-positions to keep clutter and unneeded variables from the controller class and only needs the update() method to be called for it to be ready for the next frame.

Next is the Controller class. This class is the brains of the operation doing all the looping and drawing on each frame.


	package  {

	 

	    //imports

	    import flash.events.Event;

	    import flash.display.Bitmap;

	    import flash.display.BitmapData;

	    import flash.geom.Rectangle;

	    import flash.utils.getTimer;

	    import flash.display.MovieClip;

	 

	    public class Controller extends MovieClip {

	 

	        //define variables

	        private var w:int;

	        private var h:int;

	        private var bm:Bitmap;

	        private var bmd:BitmapData;

	        private var clearRect:Rectangle;

	        private var currentTime:int = 0;

	        private var holder:Vector.<Particle> = new Vector.<Particle>;

	 

	        //most machines should be fine with 150,000 particles. Even my Nexus One can handle it!

	        private var n:int = 150000;

	 

	        public function Controller():void {

	            w = stage.stageWidth;

	            h = stage.stageHeight;

	 

	            //create the blank rectangle we will use to clear the bitmapdata

	            clearRect = new Rectangle(0, 0, w, h);

	 

	            //create the bitmap and bitmapdata

	            bmd = new BitmapData(w, h, false, 0);

	            bm = new Bitmap(bmd);

	            addChild(bm);

	 

	            //populate the holder with n number of particles

	            for(var i:int = 0; i < n; i++ ){

	                //notice no temporary variable was used to store the particle before pushing it to the Vector

	                //this only uses unneeded memmory

	                holder.push(new Particle(stage.getRect(this)));

	            }

	            //add listener for every frame

	            addEventListener(Event.ENTER_FRAME, onFrameLoop);

	        }

	        private function onFrameLoop (evt:Event):void{

	            var p:Particle;

	 

	            //lock the bitmap and clear it before drawing

	            bmd.lock();

	            bmd.fillRect(clearRect,0);

	 

	            //for n times get the respective particle in the holder and set the

	            //corresponding pixel at p.x and p.y to the particles color

	            for(var i:int = 0; i < n; i++ ){

	 

	                //notice a temporary variable was used here because otherwise the object

	                //would need to be read 3 times from the Vector - much slower

	                p = holder[i];

	                bmd.setPixel(p.x, p.y, p.c);

	 

	                //update the particle's position

	                p.update();

	            }

	 

	            //update the bitmap

	            bmd.unlock();

	 

	            //trace the FPS

	            trace(1000 / timeDifference);

	        }

	        private function get timeDifference ():int {

	            var totalPlayedTime:int = getTimer();

	            var timeDifference:int = (totalPlayedTime - currentTime);

	            currentTime = getTimer();

	            return timeDifference

	        }

	    }

	}

	

Thats it! You have what you saw in the first demo.

{notr_thumbs}

demo_link_particles

EPILEPSY WARNING:
Please don’t view this demo if you are likely to suffer from epileptic attacks or loss of consciousness, particularly when looking at some types of strong flashing lights, rapid succession of images, simple geometric shapes, flashes or explosions.

Unfortunately there is a problem. If you run this code inside the Flash Professional IDE it will never run at 24/24 FPS. This is because when you run it here, the Flash IDE tries to connect with Flash Player to read many different things off it, this is how you get your error reports when something goes funny. However, luckily for you, your clients probably never have to see this side of things and on its own, the Flash Player works beautifully, running easily at 24/24 FPS. Its just slightly more difficult to read the FPS. I recommend making a small dynamic text field and output what we are should be tracing to that, this is outside the scope of this tutorial and is something that is pretty straightforward so I can leave that with you.

So now that you have made your particle system, wouldn’t it be nice to show it off to your friends and clients? Coming from experience all you will get from showing this one is some weird glances with some squeezed praise on the side from all of those but seasoned programmers. Let’s make something pretty.


Over 9000?! Playing it Safe With so Many Particles.

Before moving on any further I recommend tinkering around a bit with the above particle system and see how far you can push it. On my less than average system I can go to about 200,000 at 24/24 FPS and at 250,000 its at about 18/24 FPS, just as a reference. Get a grip for how far you can push your own system and heck, even boast about it in the comments! :)

Let’s look at some of the things you should avoid when playing around with systems such as this.

Trace statements. One of the most useful things in a Flash Developer’s arsenal is actually a big task for Flash Player. Once per frame is okay, but make sure you don’t shove it into one of your particles when you have 200k of them running. This will simply instantly crash the Flash IDE and you’l spend the next couple of minutes pressing all the exit buttons. A good way to test something in a particle is just to drop the number of them to something between one and ten.

Everything matters when you’re doing something 200,000 times 24 times a second so be sure to keep looking through your code and never make any big changes without putting the number of particles down to a single number.


Making Something Pretty – A Basic Waterfall Effect

This will be a very basic example of building something that looks mildly attractive.

This is the updated Particle class which is used to create the waterfall:


	package  {

	 

	    import flash.geom.Vector3D;

	    import flash.geom.Rectangle;

	 

	    public class Particle {

	 

	        private var pos:Vector3D = new Vector3D(0,0);

	        private var vel:Vector3D = new Vector3D(rand(3), rand(3));

	        private var bounds:Rectangle;

	        private var color:uint = 0x00FFFF

	 

	        public function Particle(stageRect:Rectangle) {

	 

	            bounds = stageRect;

	 

	            //spawn the particle from the top left corner

	            pos.x = 0;

	            pos.y = 0;

	        }

	        private function rand(n:int):Number{

	 

	            //this function returns a random number between 0 and n

	            return Math.random() * n

	        }

	        public function update ():void{

	 

	            pos.x += vel.x;

	            pos.y += vel.y;

	 

	            //add a small amount to the y velocity to simulate gravity

	            vel.y += 0.1;

	 

	            //check if the particle is outside the bounds of the rectangle

	            checkBounds();

	        }

	        private function checkBounds():void{

	 

	            //this time we need to decrease velocities in both directions to make sure they are all eventually removed

	            if(pos.x < 0 || pos.x > bounds.width){

	                vel.x *= -0.8;

	                vel.y *= 0.8;

	            }

	            if(pos.y < 0 || pos.y > bounds.height){

	                vel.x *= 0.9;

	                vel.y *= -Math.random() * 0.8;

	            }

	        }

	        //the get methods for the color and x,y values of the particle

	        public function get c ():uint{ return color }

	        public function get x ():Number{ return pos.x }

	        public function get y ():Number{ return pos.y }

	 

	        //returns true if the particle does not have a lot of 'energy' left, false otherwise

	        public function get remove ():Boolean {

	            if(Math.abs(vel.y) + Math.abs(vel.x) < 0.1){

	                return true;

	            }

	            return false;

	        }

	    }

	}

	

As you can see there need to be no major changes to the Particle class because it will always stay as a just a placeholder for a group of related numbers and funcions.

Next is the Controller class. Again, there is no need for any major changes. Let’s take a look:


	package  {

	 

	    //imports

	    import flash.geom.Point;

	    import flash.events.Event;

	    import flash.display.Bitmap;

	    import flash.display.BitmapData;

	    import flash.geom.Rectangle;

	    import flash.utils.getTimer;

	    import flash.display.MovieClip;

	    import flash.filters.BlurFilter;

	    import flash.geom.ColorTransform;

	 

	    public class Controller extends MovieClip {

	 

	        //define variables

	        private var w:int;

	        private var h:int;

	        private var bm:Bitmap;

	        private var bmd:BitmapData;

	        private var clearRect:Rectangle;

	        private var currentTime:int = 0;

	        private var holder:Vector.<Particle> = new Vector.<Particle>;

	 

	        //define the blur and color transformers

	        private var blurFade:BlurFilter = new BlurFilter(4, 4, 1);

	        private var colorFade:ColorTransform = new ColorTransform(0.7,0.7,0.999);

	 

	        //the number of particles pushed into the holder every frame

	        private var n:int = 50;

	 

	        public function Controller():void {

	            w = stage.stageWidth;

	            h = stage.stageHeight;

	            bmd = new BitmapData(w, h, false, 0);

	            bm = new Bitmap(bmd);

	            addChild(bm);

	            addEventListener(Event.ENTER_FRAME, onFrameLoop);

	        }

	        private function onFrameLoop (evt:Event):void{

	 

	            //push new particles in n times every frame

	            for(var i:int = 0; i < n; i++ ){

	                holder.push(new Particle(stage.getRect(this)));

	            }

	            bmd.lock()

	 

	            //apply the BlurFilter which blurs the colors together and fades the particles out

	            bmd.applyFilter(bmd, bmd.rect, new Point(0,0), blurFade);

	 

	            //apply the color transformation to give the particle trails a blue tint

	            bmd.colorTransform(bmd.rect, colorFade);

	 

	            for(var j:int = 0; j < holder.length; j++ ){

	                var p:Particle = holder[j];

	 

	                //check if the particle has a resonable amount of energy left through its getter method

	                //if not remove it from the holder vector

	                if(p.remove){

	                    holder.splice(j,1);

	                    j--;

	                    p = null;

	                }

	                else{

	                    bmd.setPixel(p.x, p.y, p.c);

	                    p.update();

	                }

	            }

	            bmd.unlock();

	 

	            //trace the FPS

	            //trace(1000 / timeDifference);

	        }

	        private function get timeDifference ():int {

	            var totalPlayedTime:int = getTimer();

	            var timeDifference:int = (totalPlayedTime - currentTime);

	            currentTime = getTimer();

	            return timeDifference

	        }

	    }

	}

	

You should end up with something like this. If you’ve just found this, you might want to hit refresh in your browser to see the start of the waterfall to better understand how the particles move.

Needless to say that you can run and run with the basic ideas here and great some amazing effects, a lot of which pop up on WonderFl quite often, or even to create this kind of effect. (shameless self plug :) ) A very ‘cool’ thing to try out is to have the particles interacting with the mouse in some way, the effect can be ne beautiful when done correctly. The perlinNoise() method of the BitmapData class might also be worth mentioning here as it sometimes used in particle systems to create a flowing effect, which can also be very beautiful.


Summary

I hope after reading this that you can take away a number of things from how to get the most out of Flash Player in your code to an introduction to building particle effects. Most of all, I hope I ignited a little flame of curiosity somewhere and gave you a new set of boundaries for Flash itself. Hopefully, an ‘extreme’ particle system won’t seem too extreme anymore :)

LATEST CONTESTS

POPULAR POSTS

You are here:Agence Elysium Home More Tutorials Tutorials Flash Actionscript Development Flash Actionscript - Squeezing More Juice Out of the Flash Player