ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Advanced Actionscript 2 Computer Graphics Particle Systems and Object Oriented Class Design
http://www.actionscript.org/resources/articles/171/1/Advanced-Actionscript-2-Computer-Graphics-Particle-Systems-and-Object-Oriented-Class-Design/Page1.html
Christopher Skyi






Words Words SEO Words is a New York City Brooklyn-based Personalized SEO, Copywriting & Inbound Marketing Boutique designed to address the needs of large and medium size business who need more traffic to, and better lead generation from, their web site.

Words Words SEO Words 1) finds high value, specific ("Long Tail) keywords that brings traffic from visitors who want to buy the types of products and services you have to offer; 2) converts more of your traffic into leads by using great copywriting, google analystics, and A/B landing page testing; 3) creates inbound links to your business website; and 4) sets up and manages your social media profiles and maximizes your ranking in local search (e.g., "Google Places).

Visit Christopher's blog @ Hang Him With His SEO Pen CopyWriter
 
By Christopher Skyi
Published on September 9, 2005
 
Written by: Brooklyn Sky Design, [email:christopher@bro[email protected]]
Difficulty Level: Intermediate to advanced Actionscript.
Requirements: Flash MX 2004
Topics Covered: 1) The basic idea behind "Particle Systems" as the basis for complex animations, and 2) an object-oriented design approach to building this type of animation.
Assumed Knowledge: Actionscript 2.0 and introductory knowledge of class-based algorithms.
Download FLA

Page 1 of 4
Written by: Brooklyn Sky Design, [email:[email protected]]
Difficulty Level: Intermediate to advanced Actionscript.
Requirements: Flash MX 2004
Topics Covered: 1) The basic idea behind "Particle Systems" as the basis for complex animations, and 2) an object-oriented design approach to building this type of animation.
Assumed Knowledge: Actionscript 2.0 and introductory knowledge of class-based algorithms.
Download FLA

INTRODUCTION TO PARTICLE SYSTEMS

A 'particle system' is a method for modeling fuzzy objects, such as fire & smoke. They have been used, both as a method for modeling complex phenomena and a technique for building complex animations, since the early 80's (See Reeves, Particle Systems--A technique for modeling a class of fuzzy objects , AMC Transactions on Graphics, April 1983, vol. 2, no. 2, 359-376).

The now classic example of a 'particle system' is the transformation of the planet from the film "Star Trek II: The Wrath of Khan," i.e., the "Genesis Effect." Essentially, that special effect is just a collection of many minute particles that together represent a "fuzzy" object. Over time, the system generates particles that move, change, and die within the system.

Such a system is naturally implemented with an object-oriented language such as Java or C++. With the advent of Macromedia's Actionscript 2.0, good object oriented software design principles and techniques can now be the basis of sophisticated Flash MX-based animations. Welcome to the 21st century!

INTRODUCTION TO THE TUTORIAL

In this tutorial, I will be showing you how to build an example of simple 'particle system', a system of dynamically evolving rings (see below), using an object oriented design principle called ' composition .' Designing by composition is one of the main organizing design principles in object-based algorithms. Composition is a mechanism by which one object delegates its responsibilities to other objects. While the class design in this tutorial is simple, my hope is that it will serve as the basis for building more complex 'particle system' based animations. Working through this tutorial should also help you get a grip on the concepts of "classes," instances of those "classes," movie clips in the library, and "instances" of movie clips on the stage.

While the implementation of the 'particle system' is relatively simple, the 'life' of the system, the animation itself, is rather complex:

What you see above is a system of "ring" objects that grow (or shink) and fade and die over time. I have based the above animation on two movie clips:

1. A "Ring clip" that holds graphic of a yellow ring, and
2. A "Container clip" that will hold instances of the "Ring clip."

The above animation is just a collection of "Ring clip" instances: each instance is dynamic in that it is created, it grows (or shrinks), and finally, fades and dies. Each instance is independent of all the other instances.

Only two classes are essential for creating and controlling the animation: a "Ring" class that extends the functionality of the "Ring clip" movie clip, and a "Container" class that extends the functionality of the "Container clip" movie clip. The Container class is responsible for creating, initializing and launching new instances of the "Ring clip." A third class, MathToolKit, helps support some of the tasks of both the Ring & Container class. The "Container" & "Ring" classes contain (or are composed of) this third class, "MathToolKit".

At this point, you should download all supporting Flash files from here and refer to those files as you work through this tutorial.


Page 2 of 4

THE DETAILS

Step one .
Create the "Ring clip" movie clip:

If you examine the time line of the "Ring clip" in the library, you will see that I created the yellow ring by first drawing a yellow disk as saved it as a graphic symbol called "disk:"


I then created a black ring and saved it as graphic symbol called "mask:"

I then created an empty movie clip called "Ring clip." I then dragged the yellow disk onto a layer called "disk," and I dragged the black ring onto a layer called "mask." I then converted the layer labeled "mask" into a mask layer, and Voilà!: a yellow ring:


Step two . Make a list of "Ring clip" properties you want to be dynamic. Here's my list:
  • I want the ring to change size (i.e., I want to manipulate the _xscale & _yscale properties of a "Ring clip" movie clip instance).
  • I want the ring to "fade out" over time (i.e., I want to manipulate the _alpha property of a "Ring clip" movie clip instance). Once the ring completely fades out, it is dead.
  • I want to vary the rate of the change of these two properties, i.e., each "Ring clip" instance will grow or shrink and fade at its own rate.

Step three . Create an empty movie clip in the library called "Container clip."

I needed some way to manage the creation and initialization of 'instances' of the "Ring clip" movie clip, and I also needed a way to specify where the rings will be located on the stage. One way to do this is to use another movie clip that will serve to hold (or contain) "Ring clip" instances -- I used the "Container clip" movie clip for this purpose.

I then exported the "Container clip" for Actionscipt, made the 'Identifier' name be: "container" and the 'AS 2.0 Class' name be: "Container." (I always make the 'Identifier' and 'Class' names the same, but I start the class name with an upper case letter):

I then dragged the "Container clip" directly onto the stage. This automatically created an 'instance' of the "Container clip" on the stage. As you can see in the flash movie, the rings emanate from this point. Remember, the "Container clip" is empty ? the 'instance' of this movie clip on the stage will do nothing more than "hold" or "contain" instances of the "Ring clip" movie clip.

(Quick aside: note that I did not name the instance in the Properties dialog box ? I could have, but I don't need to: flash automatically generates a default instance name anyway if you don't supply one in the 'Properties' dialog box. Note that I could have also used the 'Identifier' name to attach an instance of the "Container clip" to the main time line, e.g., attachmovie ("container","myInstanceName", some leve), and then explicitly set the position of the instance on the stage, i.e., myInstanceName._x = 487; myInstanceName._y = 78).

Page 3 of 4

Step four . Building the classes and getting a grip on the differences between 1) classes & "instances" of those classes, and 2) movie clips in the library & "instances" of those movie clips on the stage:

We now have an instance of the "Container clip" movie clip on the stage. How do we create, initialize, and attach instances of the "Ring clip" to this instance of the "Container clip"? This is where the "Container" class comes in. The "Container" class extends the functionality of all instances of the "Container clip" that are on the stage ? in this case, we have only one.

Let's look at what this class does:

Recall that the "Container" class performs two tasks: 1) creates and sets initial animation parameters for each "Ring clip" instance, and 2) attaches to itself instances of a the "Ring clip" movie clip.

The first part of the "Container" class contains private class variables that define initial animation parameters:

[as]class Container extends MovieClip {
 /*
 Define initial animation parameters of each "Ring clip" instance:
 1. Initial "Ring clip" instance size (will think of this variable as a radius of the ring).
 2. Final "Ring clip" instance size.
 3. Size rate of change: how fast the size of the movieClip (or radius of the ring) changes.
 4. Direction of size rate of change, i.e., increase or decrease.
 5. Number of rings.
 6. Initial "Ring clip" instance position.
 7. Direction of ring scaling: increase or decrease
 8. Average (& std. dev.) of frames to wait before rendering a ring after it's been reset.
 */
 private var startRadius:Number; // initial ring radius: NOTE: movieClip Ring clip is 100px by 100px
 private var endRadius:Number; // final "Ring clip" instance size
 private var radiusStepSize:Number; // "Ring clip" instance size rate of change
 private var numRings:Number; // number of "Ring clip" instances
 private var xPos0:Number; // initial x-position of "Ring clip" instance
 .
 .
 .
 
[/as]

The "Container" class is explicitly composed of one other class ? MathToolKit. MathToolKit is simply a helper class. The constructor of the "Container" class first creates an 'instance' of the MathToolKit class (this is called 'instantiation') and it calls a private function run():

[as]function Container(){
        mathToolKit = new MathToolKit(); // create an instance of the MathToolKit class
        run();
}
[/as]

(By "native," I mean methods are properties that part of the MovieClip object by default; extra "non-native" methods & properties can be defined within a class that "extends" the MovieClip object ? run() is an example of a "non-native" method of a MovieClip object because the "Container" class is just an "extention" of an instanciated MovieClip object).

Here's the run() method in pseudo-code:

[as]function run(){
        
        //Assign the number of "Ring clip" instances in the animation to the variable: numberOfRings.
        
        //  create, initialize, and attach N ring objects within a for loop
        for ( i = 1 to numberOfRings) {
                
                //Assign staring values to all animation parameters.
                
                //Use the native MovieCip method attachmovie() to create a "Ring clip" instance and
                //send the animation parameters to an 'initialization? function defined in the "Ring" class.
                
        } // end of the for loop
}
[/as]

Here's the full code of the run() method taken from the "Container" class:

[as]private function run(){
 ///////////////////////////////////////////////////////////////
 // Set animation parameters and start firing off rings
 ///////////////////////////////////////////////////////////////
 numRings = 6; // total number of "Ring clip" instances to create
 for ( var i = 0; i <= numRings; i++ ) {
 
 // Does the ring increase or decrease?
 // We'll give this ring a 20% probably of decreasing
 if ( Math.random() < .2)
 dirDecrease = true; // this ring will decrease
 else
 dirDecrease = false; // this ring will decrease
 
 // Set animation parameters "startRadius" & "endRadius"
 //
 // Starting and ending (max) radius:
 //
 // Starting radius will be in the bounds: 0 to 20% of header width
 // Max radius:
 // lower bound = startRadius (the starting radius) + minOffset,
 // upper bound = absMaxRadius
 //
 // Note: "headerWidth" holds the width of the background graphic.
 startRadius = Math.round(Math.random()*0.2*headerWidth); // headerWidth = 700;
 endRadius = (startRadius+minOffset) + Math.round(Math.random()*
 (absMaxRadius - (startRadius+minOffset)));
 if ( dirDecrease ) { // swap min and max radius values
 var temp = startRadius;
 startRadius = endRadius;
 endRadius = temp;
 }
 
 // Set the "Ring clip" instance location on the background graphic image
 xPos0 = 0; yPos0 = 0; // keep fixed
 
 // Set the rate of ring increase or decrease: "radiusStepSize"
 radiusStepSize = Math.round(mathToolKit.getNormalDistrubuted(7,4));
 if ( radiusStepSize <= 0 )
 radiusStepSize = 3; // put a floor under the min rate of change
 
 // Call attachmovie() with the following arguments:
 // 1st) class name,
 // 2nd) instance name,
 // 3rd) movie clip depth
 // 4th) Object containing movie clip property values to be
 // passed into the movieClip constructor:
 // (_xscale,_yscale) -- initial movieClip size
 // (_x,_y) -- initial movieClip position
 //
 ringConstructorParameters = {_xscale:startRadius,
 _yscale:startRadius,
 _x:xPos0,
 _y:yPos0};
 //
 // Create an instance of the Ring class inside an instance of this ("Container) class
 // and set the animation parameters:
 attachmovie("ring", // Identifier name in linkage field of movie clip Properties dialog box
 "Ring"+i,  // instance name
 i,   // movie clip depth
 ringConstructorParameters // values to be passed to the constructor
 ).init("Ring"+i; // name of this instance
 dirDecrease, // Boolean: if true, decrease ring size
 radiusStepSize,  // rate of ring size change
 meanFrames, // average wait time (in frames)
 stdDevFrame,
 headerWidth, // headerWidth, minOffset, & absMaxRadius used to reset ring size
 minOffset, // once the ring increases/decreases to it's final size
 absMaxRadius,
 endRadius); // final ring size
 
 }
}
[/as]


Page 4 of 4

Some Comments on the Container class

1. If you're not familiar with the native MovieClip class method attachmovie(), you should take a few minutes now to read through the Flash documentation.

2. I'm using a somewhat complicated call to attachmovie() to give you an example of it's many features, such as:

1st) Passing values to the constructor of a "Ring" class instance: The Ring class (as you'll see in a moment) has a constructor that expects values for 4 of its native MovieClip properties, i.e., its size: (_xscale, _yscale), and its position within the "Container clip" on the stage: (_x, & _y). I've created an object, "ringConstructorParameters" to hold these values. It is passed to a "Ring" class instance's constructor by making it the 4th argument in the call to attachmovie(). (Note: that this forth argument is optional).
This way of setting (initial) "Ring clip" properties looks a bit weird: you need to specify both the name of the native MovieClip property (e.g., _xscale) and its value.

2nd) Three VERY important things happen during the run-time execution of the call to attachmovie():

1) A "Ring clip" instance is created within the "Container clip" that is on the stage.

2) When a "Ring clip" instance is created, an 'instance' of the "Ring" class is also created
(this is known as 'class instantiation'), i.e., the "Ring" class constructor is called and
accepts the property:value list in the generic object specified as 4th argument in the call to
to attachmovie().

3) When the "Ring clip" instance is created, attachmovie() returns a reference to the
"Ring" class instance. That reference is used to call an init() method of the "Ring"
class instance. This call to init() simply passes values for other animation parameters
in the "Ring" class

So let's sum up: An 'instance' of the "Container clip" library object is on the stage. When the movie starts, an 'instance' of its class ("Container") is automatically created. The constructor of the "Container" class automatically runs and it calls a run() method that starts the entire system. The "Container clip" instance does this by creating and then attaching instances of the "Ring clip" movie clip to itself. Each time the run() method creates a "Ring clip" instance, a corresponding instance of the "Ring" class is created that is used to extend the functionality of that "Ring clip" instance.

Now let's take a look at the constructor for the "Ring" class:

[as]//  Method Desciption: MovieClip properties set through this constructor
// (see calling instantiation code in first frame of main movie).
//  Arguments:
//  (_xscale,_yscale) - initial scaling factors
//  (_x,_y) - initial movie clip position on main stage
//
//   NOTE: the constructor seems to only accept movieClip variables,
//   BUT only implicitly, i.e, there's no argument signature
//    in the function parameter list . . . ? Attempts to
//    insert a parameter list screws everything up.
public function Ring(){
 startRadius = _xscale;
 xPosition0 = _x;
 yPosition0 = _y;
 mathToolKit = new MathToolKit(); // objects must be instanciated in constructors
}
[/as]

The only thing to note here is how the constructor accepts the MovieClip property:value list from the "ringConstructorParameters"object defined in the "Container" class.

So how does the "Ring" class instance animate the properties of its associated "Ring clip" instance? A "Ring clip" instance will increase or decrease to a final size before it completely (or almost completely) fades out. Once the "Ring clip" instances reaches its final size, it "dies" (i.e., the MovieClip instance property _visible is set to 'false'). The "Ring" class will change the _xscale, _yscale, & _alpha properties of the "Ring clip" instance in discrete steps. This is controlled by the native MovieClip method onEnterFrame():

[as]// Method Description: This method overwrites the native MovieClip method. It is
// called at the frame rate of the main movie timeline. Will each call,
// it will change the properties of an instance of the Ring class.
// Arguments: none.
//
private function onEnterFrame():Void {
        if ( render ) {
                setSize(); // step increase or decrease
                setAlpha();
        }
        else { // the ring is dead, so just wait the required number of
                // frames before bringing it back to life
                cntFrames++;
                if ( cntFrames == waitFrames ){ render = true;
                        // start rendering cntFrames = 0;
                        // reset counter _visible = true;
                        // make ring visible
                }
        }
}
[/as]

At this point, I'll simply describe the functions of the "Ring" class ? for details, please consult the source code.

At the frame rate of the main movie, onEnterFrame() calls setSize() to change the size of the ring. The onEnterFrame() method then calls setAlpha() to increase the level of transparency of the ring. The level of transparency at any point during the life of the ring is a function of the percentage difference between the final ring size and whatever the size the ring happens to have at that point.

The method setSize() detects when the ring has reached it's final size. It then calls a method, resetParameters(), that randomly assigns a new set of starting animation values for the "Ring clip" instance. It also assigns a random number of frames to the class variable "waitFrames," where "waitFrames" is the number of frames to wait before bringing the ring back to life (with it's new starting animation values).

In sum then, an instance of the "Ring" class makes dynamic the scale and alpha properties of its associated "Ring clip" instance. It does this by calling the (over-ridden) native MovieClip method onEnterFrame(). When the "Ring clip" instance reaches its final size, resetParameters() makes it invisible (i.e., _visible = false) and assigns new starting values to its animation parameters, i.e., _xscale, _yscale, & _alpha. The "Ring" class then waits a random number of frames before making the "Ring clip" instance visible.

One final note about "stochastic behavior" & the MathToolKit class: introducing random variables into models and animations is an advanced topic, so I won't go into any details here. However, if you build your animations with a certain amount of stochastic (i.e., random) behavior, you can make the animation more 'nuanced' by drawing your random variables from other known probability distributions rather than the usual, boring 'uniformed' distribution that Math.random() gives you. In this application, I generated the step sizes and wait times from simple normal distributions that differed only in terms of their means and standard deviations.