Skip to any available Digital Media Help Center chapter of Learning ActionScript 3.0:
Chapter 1 | Chapter 4 | Chapter 7

Chapter 7: Motion

From your very first experiment to the umpteenth time you've performed a familiar task, moving assets with code can be a gratifying experience. In addition to creating more dynamic work by freeing yourself from the permanency of the timeline, there is something very immediate and pleasing about controlling the motion of a symbol instance purely with ActionScript.

Because programming motion can cover a large number of concepts, we've chosen a few as the main focus areas for this subject. In each area, we offer what we call simplified simulations—that is, we do not maintain that our examples accurately reflect real-world scenarios. We won't be accounting for every possible force that can act on an object in each sample file. On the contrary, we try to present approaches to each topic that are simple enough to integrate into your projects with ease.

In addition to simplifying some topics, we also hope to show that math can be your friend. To some of you, this is a given, but to others, having to deal with numbers is an uphill journey. If you find yourself in the latter category, we hope to smooth over some of the smaller bumps that might be caused by a knee-jerk reaction to the need for math. Understanding just a few small applications of mathematical or scientific principles can really go a long way. You may even find yourself becoming comfortable with these principles and applying them even when there are other ways to accomplish a goal.

In this chapter, we'll look at the following topics:


This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

Section 7.1: Basic Movement

When discussing scripted motion, a good place to begin is simple incrementing (increasing by a certain amount) or decrementing (decreasing by a certain amount) of x and y coordinates. Whether you realize it or not, you are probably used to working in a Cartesian coordinate system, where unique points are expressed on a single plane of two numbers, typically x and y. However, you are probably used to thinking about positive x values moving to the right and positive y values moving up, the way simple graphs are usually expressed.

The Flash coordinate system differs a bit from the typical coordinate system, in that the origin, or point (0, 0), is the upper-left corner of the stage, and y values increase when moving down. We will mention this again when it is directly applicable to an example, such as in when you control sound volume with your mouse. However, if you try to remember this difference, you will probably have fewer surprises.

To increment or decrement a value, you simply add or subtract a unit from that value. Here are two example ways of doing this:

mc.x++;
mc.y−−;

mc2.x += 10;
mc2.y −= 10;

The first example uses double plus signs to increment a movie clip's x coordinate by 1 and double minus signs to decrement its y coordinate by 1. If you need to add or subtract more than one unit, you can use a plus or minus sign followed by an equal sign to add the amount shown on the right side of the equation to the entity on the left side. The second example cited adds 10 pixels to the x coordinate and subtracts 10 pixels from the y coordinate. In both cases, because the amount added and subtracted is the same, these hypothetical movie clips will move up (subtracting y coordinate values moves a movie clip up) and to the right by 1 pixel in the first example, and move up and to the right 10 pixels in the second example.

As we start discussing speed, velocity, and acceleration, it might help to have a little background that you can relate to the code. Speed, or how fast an object is moving, is a scalar quantity. That means it is a value that can be expressed with a magnitude alone, such as 80 miles per hour. Velocity is the rate of change in movement, and is a vector quantity. It must be expressed with both a magnitude and a direction. In contrast to the definition of speed, velocity can be described as how fast in a particular direction, such as 80 miles per hour, South-South-East. Acceleration is also a vector quantity and is the rate of change in velocity.

These distinctions are subtle but helpful when it comes to getting from point a to point b. An easy way to remember each property is to think of your own movement. You can move very quickly, alternating one step forward and one step backward. This will give you speed but (from a simple way of looking at things) an overall velocity of 0. If you switch to always moving one step forward, you may move at the same speed but now have a constant velocity. If you increase your speed over time, while continuing to move forward, your velocity increases, giving you acceleration.

This is not terribly important if you just want to create a basic animation. However, as you begin to build more complex systems, it may help to understand what is required to meet your goals, and it may help you create more realistic simulations.

Velocity

Example
Figure 7-1: Simulated movement of a movie clip, at a constant velocity, down and to the right

Later on, we'll show you how to express a direction using an angle. For now, however, the direction of movement will be dictated by whether you increase or decrease x and y coordinates—that is, velocity is often implied in, or can be extrapolated from, simple code. For example, if you remember that positive x values move an object to the right, you can specify a velocity merely by incrementing an x coordinate.

Breaking out this change into a variable can make this clearer and easier to work with. For instance, if you think of always adding a velocity to a movie clip's position, you not only simplify your operator use, but you also need to add only a positive value to move in a positive direction, or add a negative value to move in a negative direction.

This code creates a ball from a library movie clip included in this lesson with the linkage identifier class Ball. It then adds 4 pixels to the ball's x and y coordinates each time the enter frame event occurs. This means the ball moves down and to the right, as depicted in multiple frames in Figure 7-1.

1 var   ball:MovieClip = new Ball();
2  ball.x = ball.y = 100;
3  addChild(ball);
4
5  var xVel:Number = 4;
6  var yVel:Number = 4;
7
8  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
9  function onLoop(evt:Event):void {
10      ball.x += xVel;
11      ball.y += yVel;
12  }

Because the updated values are always 4 pixels, the ball is said to have a constant velocity. If you think of the onLoop() function executing once per second, the velocity would be 4 pixels per second, South-South-East. However, the function is executed every time the playhead enters the frame, so it's tied to the temp (frame rate). A frame rate of 20 frames per second (fps), therefore, would yield a velocity of 80 pixels (approximately one inch on a 72-pixel-per-inch monitor) per second. Let's see what happens if we vary the velocity over time.

Acceleration

Changing the velocity over time adds acceleration to an object. Consider the previous example of a constant velocity of 4 pixels down and to right. At 20 frames per second, this constant velocity (equivalent to 4 + 4 + 4 + 4, and so on) would take 3 seconds to move 240 pixels. However, if we accelerate the object 1 pixel per function execution, the changing velocity would look like 4 + 5 + 6 + 7 + 8, and so on. At that rate, using our 20 fps frame rate, the velocity would reach 23 pixels per iteration, and the ball would travel 270 pixels, in only one second. Acceleration is the compound interest of movement!

To realize this change, all you have to do is increment the velocity by the acceleration (rate of change of velocity) every time the function executes. Start with typed variables with initial values in lines 7 and 8, and then use them to increment the velocity in lines 15 and 16. This yields the effect of moving 4 pixels the first time, adding 1 to the velocity, moving 5 pixels the second time, adding 1, and so on.

1 var ball:MovieClip = new Ball();
2   ball.x = ball.y = 100;
3   addChild(ball);
4
5 var xVel:Number = 4;
6 var yVel:Number = 4;
7 var xAcc:Number = 1;
8 var yAcc:Number = 1;
9
10 addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
11 function onLoop(evt:Event):void{
12     ball.x += xVel;
13     ball.y += yVel;
14
15     xVel += xAcc;
16     yVel += yAcc;
17 }
Example
Figure 7-2: Acceleration increasing the velocity over time, simulated by increased movement in each frame

The effect is a rapid acceleration of the ball along its set direction. Figure 7-2 illustrates this effect by depicting the increasing distance between ball positions.

The opposite of acceleration, deceleration can be simulated by decreasing the velocity. Later on, we'll use this technique, in part, to illustrate gravity, and we'll also look at a more sophisticated deceleration technique to simulate friction.

Section 7.2: Geometry and Trigonometry

While many people find geometry and trigonometry intimidating, the small investment required to understand a few basic principles in these disciplines can pay large dividends. For example, what if you needed to find the distance between two points, or rotate one object around another? These small tasks are needed more often than you may think, and are easier to accomplish than you may realize.


This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

Distance

Example
Figure 7-3: Calculating the distance between two points using geometry

Let's say you are programming a game in which a character must be pursued by an enemy and must exit through one of two doors to safety. However, the enemy is close enough that the character must choose the nearest exit to survive. The player controls the character, but you must make the game challenging enough for the enemy to catch the character if the player makes the wrong decision. To do that, the enemy must know which exit is closest.

To determine which of two objects (the doors) is closest to a given point (the enemy), you need only one formula called the Pythagorean theorem. Simplified, the theorem says that the length of the longest side of a right triangle is equal to the square root of the sum of the squares of the horizontal and vertical sides. For our needs, this can be determined by finding the differences between the two x values and two y values, and then checking the square root of the sum of those two squares. Figure 7-3 illustrates both descriptions.

To determine the distance between two points in ActionScript, you must calculate the difference between the x values of both points and multiply that difference by itself (squaring the value). Then do the same thing with the y values (squaring that difference, as well). Finally, following the Pythagorean theorem, use the Math object to return the square root of that sum.

function getDistance(x1:Number, y1:Number, x2:Number, y2:Number):
    Number {
    var dx:Number = x1-x2;
    var dy:Number = y1-y2;
    return Math.sqrt(dx * dx + dy * dy);
}

Here is an example usage of our getDistance() function, seen in the accompanying distance2.fla source file. It compares the distance between ball0 and ball1 to the distance between ball0 and ball2:

var dist1 = getDistance(ball0.x, ball0.y, ball1.x, ball1.y);
var dist2 = getDistance(ball0.x, ball0.y, ball2.x, ball2.y);
if (dist1 < dist2) {
    trace("ball1 is closest to ball0");
} else {
    trace("ball2 is closest to ball0");
}

Movement Along an Angle

Earlier we discussed velocity as a vector quantity because it combined magnitude and direction. However, the direction in the previous example was determined by changing x and y coordinates. Unfortunately, such a direction is easily identifiable only when moving along simple paths, such as along the x or y axis. A much better way to indicate a direction is to specify an angle to follow.

Before we discuss angles and their different units of measure, it will help to understand how angles are indicated in the Flash coordinate system. As you might expect, angles are commonly referenced using degrees, but it's important to note that 0 degrees is along the x axis pointing to the right. The 360-degree circle then unfolds clockwise around the coordinate system. This means 90 degrees points down along the y axis, 180 degrees points left along the x axis, and so on. This is depicted in Figure 7-4.

Example
Figure 7-4: How Flash angles (a) and radians (b) are calculated

Now that you have a correct point of reference, the next important concept to understand is that most of ActionScript, like most computer languages and mathematicians, does not use degrees as its preferred unit of measurement for angles. This is true for just about all common uses of angles, except for the rotation property of display objects and one or two somewhat more obscure items (such as a method of the MatrixTransformer class also used to rotate display objects). These entities use degrees as measure angles and to remain comfortable and familiar to users. The remainder of ActionScript uses radians: A radian is the angle of a circle defined by moving along the outside of the circle only for a distance as long as its radius, as seen in 7-4. One radian is 180/PI degrees, which is approximately 57 degrees.

While some of that may prove helpful, or even interesting, for our purposes we don't need to memorize this information. Instead, all we need to do is remember that there is a formula handy for our conversion needs. Converting degrees to radians is accomplished by multiplying the original angle in degrees by (Math.PI/180). Conversely, radians can be converted to degrees by multiplying the original angle in radians by (180/Math.PI). In the upcoming example, we'll write a utility function for this purpose that we can use throughout the rest of our examples.

Now we're prepared to address the task at hand. We must send a movie clip off in a direction specified by an angle (direction) at a specific speed (magnitude). This will be the resulting velocity. This script starts by creating a movie clip and positioning it on stage at point (100, 100). It then specifies the speed and angle at which the movie clip will travel, and converts commonly used degrees to ActionScript-preferred radians using the utility function at the end of the script.

1  var ball:MovieClip = new Ball();
2  ball.x = ball.y = 100;
3  addChild(ball);
4
5  var speed:Number = 12;
6  var angle:Number = 45;
7  var radians:Number = deg2rad(angle);
Example
Figure 7-5: A point on a circle can be determined by using the cosine and sine of an angle, multiplied by a desired radius, to calculate the x and y values, repectively.

With both a direction (angle) and magnitude (speed), the required velocities relative to the x and y axes can be determined. We accomplish this by using the sine() and cosine() methods of the Math class. Think of a triangle with one point at the origin of the x/y axes, as seen in Figure 7-5.

The sine of an angle is the length of the opposite side of the triangle divided by the length of the triangle's hypotenuse (which is the side opposite the triangle's right angle). The cosine of an angle is the length of the adjacent side of the triangle divided by the length of the triangle's hypotenuse. Therefore, the x component of the direction is determined by calculating the cosine of a specified angle, and the y component of the direction is determined by calculating the sine of the same angle. Multiply each value by the speed of the movement and you have a velocity vector.

8  var xVel:Number = Math.cos(radians) * speed;
9  var yVel:Number = Math.sin(radians) * speed;

All that remains is to add that change in velocity to the x and y coordinates of the ball and it's on the move.

10 addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
11 function onLoop(evt:Event):void {
12     ball.x += xVel;
13     ball.y += yVel;
14 }
15
16 function deg2rad(deg:Number):Number {
17     return deg * (Math.PI/180);
18 }

Circular Movement

Now that you know how to determine x and y coordinates from an angle, circular movement is a snap. For example, it will now be relatively trivial for you to move an object in a circle, the way a moon revolves around a planet. With circular movement, we are not interested in the velocity derived from direction and magnitude, because the ball in this example will not be traveling along that vector. Instead, we want to calculate the x and y coordinates of many consecutive angles. By plotting the sine and cosine of many angles, you can move the ball in a circle.

If you think of the sine and cosine values of various angles, this technique is easy to understand. (For simplicity, all angles will be discussed in degrees, but assume the calculations are performed with radians.) The values of both cosine and sine are always between −1 and 1. The x component, or cosine, of angle 0 is 1, and the y component, or sine, of angle 0 is 0. That describes point (1, 0), or straight out to the right. The cosine of 90 degrees is 0 and the sine of 90 is 1. That describes point (0, 1), or straight down.

This continues around the axes in a recognizable pattern. Remembering that we're discussing degrees but calculating in radians, the cosine and sine of 180 degrees are −1 and 0, respectively (point(−1, 0), straight to the left), and the cosine and sine of 270 degrees are 0 and 1, respectively (point(0, 1), straight up).

You have only two things you must still do to plot your movie clip along a circular path. Because all the values you're getting from your math functions are between −1 and 1, you must multiply these values by the desired radius of your circle. A calculated value of 1 times a radius of 150 equals 150, and multiplying −1 times 150 gives you −150. This describes a circle around the origin point of the axes, which spans from −150 to 150 in both horizontal and vertical directions.


This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

Figure 7-6 illustrates these concepts in one graphic. Each color represents a different angle shown in degrees, with the x and y values expressed in both standard cosine and sine units (−1 to 1) and the result of multiplying that value by a desired radius of 150.

Example
Figure 7-6: Four angles rotating around a circle, expressed both in degrees and as x and y points on a circle with a radius of 150 pixels

Finally, you must position the circle wherever you want it on the stage. If you take no action, the object will rotate around the upper-left corner of the stage, or point(0, 0). This script centers the circle.

The first nine lines of the script initialize the important variables. Specified are a starting angle of 0, a circle radius of 150, an angle increment of 10, and a circle center that matches the center of the stage (its width and height divided by 2, respectively). Also created is the satellite that will be orbiting the center of the stage, derived from the Asteroid class. This uses the same technique you used to create the balls in the previous files, making a new display object from the linkage class of a library symbol, but you might need a little spice now, so an asteroid it is. To prevent a quick blink of the satellite at point(0,0), it is initially placed offstage in line 8 before becoming a part of the display list in line 9.

1  var angle:Number = 0;
2  var radius:Number = 150;
3  var angleChange:Number = 10;
4  var centerX:Number = stage.stageWidth/2;
5  var centerY:Number = stage.stageHeight/2;
6
7  var satellite:MovieClip = new Asteroid();
8  satellite.x = satellite.y = −200;
9  addChild(satellite);

The last part of the script is the frame loop and handy degree-to-radian conversion utility discussed earlier. The function begins by translating the specified angle from degrees to radians and determining the x (cosine) and y (sine) values that correspond to each angle. The function then multiplies each value by the desired radius and adds it to the origin point of the circle (in this case, center stage). After each plot, the angle is incremented (line 15) and then reset to an equivalent near-zero angle once it reaches or exceeds 360.

The angle value reset (line 16) is accomplished using the modulus operator, which simply determines the remainder after a division. For example, using the 10-degree increment in this example, 360 goes into 350 zero times, leaving a remainder of 340. However, 360 goes into 360 one time, leaving a remainder of 0. As a result, the angle is reset to 0, and you don't have to deal with angles like 370 or 380 degrees.

10  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
11  function onLoop(evt:Event):void {
12      var radian:Number = deg2rad(angle);
13      satellite.x = centerX + radius * Math.cos(radian);
14      satellite.y = centerY + radius * Math.sin(radian);
15      angle += angleChange;
16      angle %= 360;
17  }
18
19  function deg2rad(deg:Number):Number {
20      return deg * (Math.PI/180);
21 }

Note: The last step of the circular rotation script, resetting the angle on line 16, isn't wholly necessary because Flash will adjust angle values automatically. However, it's not a bad idea to understand what's going on in your scripts, in case you have to use a value for another purpose. Obviously, the fewer surprises you must face, the better.

Rotation Toward an Object

Example
Figure 7-7: Using atan2(), you can continuously point a movie clip at the mouse no matter where it is on the stage

Determining points on a circle when you start with an angle requires sine and cosine, as seen in the previous example. However, the opposite of that task requires a different trigonometric method. Determining an angle when starting with point data requires atan2(). This method of the Math class determines the angle between two points, based on two assumptions. The first is that the zero angle is on the right half of the x axis, and the second is that the angles increase moving counterclockwise from this zero point.

The atan2() method is especially useful when you want to use rotation to point something at another point. For instance, the next code example uses a frame event to continuously point a movie clip at the mouse location, no matter where the mouse is on the stage, as simulated in Figure 7-7.

There are two important issues to be aware of when using atan2(). The method always takes y point data as its first parameter (instead of x, which is more commonly placed in the first position), and the method returns its angle in radians, not degrees.

With that in mind, let's take a look at the script. It begins by creating a new instance of the Hand movie clip class from the library, placing it in the center of the stage, and adding it to the display list. The listener that follows in lines 6 through 9 then sets the rotation property of the resulting hand movie clip every time the playhead enters the frame. The angle is computed by the getAngle() function, after passing the location of the hand and mouse to the function.

1  var hand:MovieClip = new Hand();
2  hand.x = stage.stageWidth/2;
3  hand.y = stage.stageHeight/2;
4  addChild(hand);
5
6  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
7  function onLoop(evt:Event):void {
8      hand.rotation = getAngle(hand.x, hand.y, mouseX, mouseY);
9  }

The atan2() method in line 11 then subtracts the second location (in this case, think of the mouse as a point on a circle) from the first location (the hand movie clip, which serves as the center of the circle), to get its angle. However, remember that atan2() returns its angle value in radians, and you want to set the rotation of a movie clip, so degrees are required. Therefore, you must convert from radians to degrees using the rad2deg() function.

10  function getAngle(x1:Number, y1:Number, x2:Number, y2:Number):
    Number {
11     var radians:Number = Math.atan2(y1-y2, x1-x2);
12     return rad2deg(radians);
13 }
14
15 function rad2deg(rad:Number):Number
16     return rad * (180 / Math.PI);
17 }

This example points one movie clip at the mouse, but the effect can be adapted in many ways. One obvious variant is to point a movie clip at another movie clip. Another visually interesting adjustment is to point many instances of a movie clip at the same object. A grid of such pointers, for example, looks interesting because each pointer rotates independently based on its location. Finally, the ultimate effect need not be visual. You can use this technique simply to track things, such as planning the trajectory of a projectile toward a target.

Section 7.3: Physics

Adding physics to animations, games, and similar projects can really elevate them to another level of user enjoyment. The visual appearance and, in interactive scenarios, even the user experience, of a project are sometimes dramatically enhanced by surprisingly small code additions.

Although we're going to be discussing some basic physics principles in this section, it is initially more important to understand their effects than to focus minutely on the math and science behind them. For the scientifically minded, this should be viewed more as a matter of practicality than heresy. The formulas offered here are sometimes simplified, or even adapted, from their real-world counterparts for practical use or familiarity. Once you are comfortable with their uses, you can then refine their formulas, account for additional variables, and so on, to improve their realism. In short, it is often helpful to first simulate the simple orbit of a planet before considering the orbit's decay, the gravitational attraction of other bodies, and so on.

Gravity

Let's start off with a simple implementation of pseudo-physics that is based on an example used previously in this chapter. If you think about it, a basic Flash simulation of gravity requires little more than acceleration in the y direction, and requires only two small changes to the previous acceleration code. What do you think would happen if you added only 1 to the y velocity (meaning no change to the x velocity), and started out with a negative y value, as seen in the following code?

1  var ball:MovieClip = new Ball();
2  ball.x = ball.y = 100;
3  addChild(ball);
4
5  var xVel:Number = 4;
6  var yVel:Number = −10;
7  var yAcc:Number = 1;
8
9  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
10  function onLoop(evt:Event):void {
11      ball.x += xVel;
12      ball.y += yVel;
13
14      yVel += yAcc;
15   }

This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

This code would effectively simulate tossing a ball into the air. The ball would appear to rise initially because the y velocity is negative (remember that negative y is up in the Flash coordinate system). However, by adding a positive 1 in line 14, each time the function executes, the velocity decreases from −10 to −9 to −8, and so on, slowing the ball's ascent, just as if gravity were counteracting the upward force of the toss. Eventually, the y velocity reaches zero at the height of the toss, where the upward force and gravity reach equilibrium. Then the velocity changes to positive, continuing to increase by 1 each time. The value becomes 1, then 2, then 3, and so on, as the ball begins to accelerate downward due to the effect of gravity. shows the effect of the simulated gravity by depicting several frames of the animation at once. When a ball is tossed in the air, gravity slows its rate of ascent and then increases the rate at which it falls.

Example
Figure 7-8: The effect of gravity on acceleration

To continue your exploration of gravity, velocity, and acceleration, visit the book's companion web site. A file called wall_bounce.fla demonstrates all these concepts and adds several additional features. Included are conditionals to change the ball's direction when hitting a stage boundary, bounce behavior, and even a texture to simulate rotation during bouncing.

Note: The role of the y acceleration in this discussion is important for future topics because it behaves as a coefficient of gravity. A coefficient is a modifier that alters a property of a system. It is often a multiplier, which we'll see in a little while, multiplying by a value less than 1 to reduce an effect, or by a value grater than 1 to exaggerate an effect. However, it can also add, subtract, or divide a value, as needed. If you altered the y acceleration value in the previous example to 2 or .5, it would be doubling or halving the amount of gravity applied, respectively.

Friction

All other things being equal, if you slide a hockey puck along three surfaces—a street, a marble floor, and an ice rink—the puck will travel three different distances due to friction. Friction will be highest on the street, building up resistance to motion between the puck and the street surface, limiting the progress of the puck. Friction will be reduced on the marble surface, and lowest on the ice, allowing the puck to travel the farthest.

A simple way to add friction to an animation is to create a friction coefficient that gradually reduces velocity over time. To demonstrate this, we'll start with the example from the section. You'll only need to do two things to add friction. First, create the coefficient variable, as seen in line 10, and then multiply the x and y velocities by this coefficient in lines 14 and 15. Remember that friction hinders movement, so you want to choose a friction value between 0 and 1. Depending on the application, you can vary the number. Perhaps you might use .97 for ice, .90 for marble, and .60 for asphalt.

1  var ball:MovieClip = new Ball();
2  ball.x = ball.y = 100;
3  addChild(ball);
4
5  var speed:Number = 12;
6  var angle:Number = 45;
7  var radians:Number = deg2rad(angle);
8  var xVel:Number = Math.cos(radians) * speed;
9 var yVel:Number = Math.sin(radians) * speed;
10 var frCooef:Number = .95;
11
12  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
13  function onLoop(evt:Event):void {
14     xVel *= frCoeff;
15     yVel *= frCoeff;
16       ball.x += xVel;
17       ball.y += yVel;
18  }
19
20  function deg2rad(deg:Number):Number {
21      return deg * (Math.PI / 180);
22  }

Note: The effect of friction may be familiar to you as one kind of easing. Easing is so named because when used, an object appears to "ease in" to an animation, accelerating as the animation progresses, or "ease out" of an animation, decelerating as the animation finishes. We'll discuss several more elaborate easing equations already built in to ActionScript later in this chapter.

Zeno's Paradox

Another way to add friction to object movement is to use Zeno's paradox, which says that, when moving from one point to another, you never really reach your ultimate destination because you are dividing the remaining distance with every movement. If you divide the distance between point a and point b in half with every step, theoretically, you could never reach point b. Philosophy aside, this idea can be used to slow down an object as it approaches its destination, as illustrated in Figure 7-9.

Example
Figure 7-9: Zeno's paradox, a simple way to depict friction or easing

This is especially handy when you simply want to add basic deceleration between two points and don't need a more elaborate system. The following example starts by creating a ball movie clip from the library, and then calls the onLoop() function every enter frame. onLoop() updates the movie clip's x and y coordinates separately by calling the velFriction() function, passing in the origin location, destination location, and number of times you want to divide the distance traveled.

1  var ball:MovieClip = new Ball();
2  ball.x = ball.y = 100;
3  addChild(ball);
4
5  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
6  function onLoop(evt:Event):void {
7       ball.x += velFriction(ball.x, mouseX, 8);
8       ball.y += velFriction(ball.y, mouseY, 8);
9 }
10
11 function velFriction(orig:Number, dest:Number, coeff:Number):
   Number {
12     return (dest-orig)/coeff;
13 }

The velFriction() function calculates the difference between the origin and destination point values and divides the result by the number of steps used to close the distance. Note that, despite the commonly stated form of Zeno's paradox, you do not always have to cut the distance in half using two steps. In fact, this is how you vary the animation's deceleration. Higher numbers require more time for the object to reach its destination, and lower numbers finish the animation more quickly. This value can be thought of as a friction coefficient.

As is true of many examples in this chapter, you may want to add a tolerance factor to this system in your own projects. For example, you may want to use a conditional that removes the event listener when your movie clip comes close enough to your destination so you eventually do reach your destination. A variant on this optimization technique is used in the section at the end of this chapter.

Elasticity

Another property of physics that can liven up animations is elasticity. Elastic properties can be applied to spring simulations, of course, but can also be used as another easing method.

Elasticity is easily calculated using Hooke's law. Hooke's law says that the force exerted by a spring is linearly proportional to the distance it is stretched or compressed. It is expressed with the formula F = −kx. F is the resulting force, −k is a spring constant, indicating the strength of the spring, and x is the distance to which the spring is deformed. (Although not vital to this discussion, the equation is expressed as a negative because the force expressed by the spring, often called a restorative force, is not in the same direction as the force applied to the spring.)

The following example uses elasticity to settle a movie clip into each new location. The movie clip moves from a starting position to wherever the mouse is moved, bouncing around the destination until settled, as seen in Figure 7-10.

Example
Figure 7-10: A basic depiction of easing using Hooke's law of elasticity

This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

The script starts by creating a movie clip and initializing x and y velocity variables. It then creates an enter frame listener that calls an elasticity function in lines 10 and 11 that determines both the x and y velocity, and increments the x and y positions of the ball over time. The elasticity function is called separately for x and y values, allowing greater flexibility in which property to affect. For example, to calculate the force of a spring in a cylinder, you might want to affect only the y value, rather than both x and y values. Passed to the function in lines 10 and 11 are the movie clip's starting and ending positions, the spring constant, a damping factor (both of which will be explained in a moment), and the current x and y velocities. Finally, the x and y locations of the movie clip are updated with the newly calculated velocities.

1  var ball:MovieClip = new Ball();
2  ball.x = ball.y = 100;
3  addChild(ball);
4
5  var xVel:Number = 0;
6  var yVel:Number = 0;
7
8  addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
9  function onLoop(evt:Event):void {
10      xVel = velElastic(ball.x, mouseX, .14, .85, xVel);
11      yVel = velElastic(ball.y, mouseY, .14, .85, yVel);
12      ball.x += xVel;
13      ball.y += yVel;
14  }

All that remains is the elasticity calculation itself. The velocity with elasticity is calculated first by employing Hooke's law. The elastic force is determined in line 16 by multiplying the spring constant (the strength of the spring) by the distance between the starting point and the mouse location (the distance the fictional spring is stretched). This elasticity is compounded using the += operator so the value can vary with each new position of the movie clip and/or mouse. Because springs don't have infinite energy, the elastic force is dampened every time the function is called, exerting only 85 percent of the last force value on the current elasticity until the springiness is reduced to nothing.

15  function velElastic(orig:Number, dest:Number, springConst:Number,
    damp:Number, elas:Number):Number {
16      elas += −springConst * (orig − dest);
17      return elas *= damp;
18  }

Section 7.4: Programmatic Tweening

When you need a relatively simple animation and don't want to spend time and effort coding it yourself, you may be able to use Flash's built-in Tween class. An example file called as_tween.fla can be found in the accompanying source code. The Tween class allows you to specify the compatible display object and property you wish to tween, the precreated easing function that will affect the property change, the beginning and finishing values of the property, the duration of the tween, and, finally, whether to use seconds or frames when evaluating the tween's duration. Here is a look at the class's constructor.

Tween(obj:Object, prop:String, func:Function, begin:Number,
     finish:Number, duration:Number, useSeconds:Boolean)

The following example creates a ball movie clip, places it at point (100, 100), and then creates the tween. It alters the x coordinate of the movie clip, using an elastic easing. It begins at position 100 and finishes at position 400, and it takes 3 seconds (indicated by the true value of the last parameter, useSeconds) to complete the tween.

1  import fl.transitions.Tween;
2  import fl.transitions.easing.*;
3
4  var ball:MovieClip = new Ball();
5  ball.x = ball.y = 100;
6  addChild(ball);
7
8  var ballXTween:Tween = new Tween(ball, "x", Elastic.easeOut, 100,
   400, 3, true);

Note: In this case, if the last parameter was false, the tween duration would be measured in frames. Considering a tempo of 20 frames per second, a frame equivalent of 3 seconds would be 60.

Because a single tween instance controls a single property, it is possible, and quite common, to create multiple tweens for the same object. They don't even have to have values that keep pace with other related Tween class instances. For example, adding this new emphasized line (line 9) to the previous script will fade in the ball movie clip from a value of 30 percent to 100 percent across the same 3 seconds, but in a linear process with no easing effect.

9 var ballAlphaTween:Tween = new Tween(ball, "alpha", None.easeOut,
                          .3, 1, 3, true);

The Tween class has several additional properties, methods, and events for use by each instance of the class. Notable properties include the Booleans isPlaying and looping (indicating whether the animation is in progress and looping, respectively), and the Number position. The position property indicates the current value of the tweening property, so it refers to the current position of the tween, not the x/y position of the display object on stage—that is, the ballAlphaTween instance seen previously still reports the position variable, even though the alpha value of the movie clip is being tweened.

Available methods include several navigation options, which command the tween to stop, start, and resume playback, jump to the next, previous, first, and last frames of the animation, and play only until a specified point in the tween is reached. Events associated with the tween are fired when the animation is started, stopped, or resumed, when it loops or finishes, and even during the animation each time the screen is updated.

Select the easing class to use via the fl.transitions.easing package. Although specifying an easing class is required, one of the available options is None, so you don't have to apply an easing effect to your tween. The names and descriptions of available easing classes can be found in Table 7-1.

Table 7-1: Easing types found in the fl.transitions.easing package
Easing ClassDescription
BackEasing in begins by backing up and then moving toward the target. Easing out overshoots the target and backtracks to approach it.
BounceBounces in with increasing speed, or out with decreasing speed.
ElasticUndulates in an exponentially decaying sine wave, accelerating in and decelerating out.
NoneLinear motion without easing.
RegularNormal easing, like that found in the timeline's simple easing feature, accelerating in and decelerating out.
StrongEmphasized easing, stronger than that found in the timeline's simple easing feature, but without additional effects. Accelerates in and decelerates out.

Each class has a minimum of three methods to cover easing in, easing out, and easing both in and out of the tween. All methods for each class support initial and final values of the property being animated, the duration of the easing, and the current time in the animation. Back also supports a value for the degree of overshoot beyond the target at the start and/or end of the animation, and Elastic adds support for the amplitude and period of the sine wave used to calculate the elasticity.

Section 7.5: Timeline Animation Recreations

While this isn't entirely an ActionScript solution, we'd like to cover Flash CS3's new Motion and Animator classes. These classes, and their supporting players, make it possible to replay animations that have been created previously in the timeline. Scripting purists may be more interested in perfecting their ActionScript skills than relying on the timeline to originate animations. However, this capability may be attractive to many Flash CS3 users, even coding curmudgeons, for two reasons.

First, it doesn't ultimately use the timeline but still makes it possible to reproduce complex animfations created there—including animations that might not be that easy to achieve strictly with ActionScript. Second, it offers a new path to improved designer-programmer collaboration. Designers can create timeline animations, and programmers can integrate that work into other projects without relying on the original timeline structure.

The foundation of this process exists in a feature called "Copy Motion as ActionScript 3.0." After creating a timeline tween, you can select the entire tween and then choose the Copy Motion as ActionScript 3.0 menu option from the EditŪTimeline menu. This copies to the clipboard all necessary information to recreate the tween with code. During the copy process, the feature prompts you for the tweened symbol's instance name with a convenient dialog—prepopulated if the instance name already exists.

Once the copy completes, you can paste the results in the Actions panel. All the ActionScript needed is included, and the motion is represented by XML information in the format required by the Motion class. A simple example, tweening a movie clip across the stage over 20 frames, follows:

1  import fl.motion.Animator;
2  var ball_xml:XML = <Motion duration="20" xmlns="fl.motion.*"
   xmlns:geom="flash.geom.*" xmlns:filters="flash.filters.*">
3      <source>
4           <Source frameRate="12" x="50" y="50" scaleX="1" scaleY="1"
            rotation="0" elementType="movie clip" instanceName="ball"
            symbolName="Ball">
5             <dimensions>
6                  <geom:Rectangle left="−10" top="−10" width="20"
                   height="20"/>
7              </dimensions>
8              <transformationPoint>
9                  <geom:Point x="0.5" y="0.5"/>
10              </transformationPoint>
11          </Source>
12      </source>
13
14      <Keyframe index="0">
15          <tweens>
16              <SimpleEase ease="0"/>
17         </tweens>
18     </Keyframe>
19
20      <Keyframe index="19" x="450"/>
21  </Motion>;
22
23  var ball_animator:Animator = new Animator(ball_xml, ball);
24  ball_animator.play();

Looking over the generated XML, you can pick out properties such as duration, frameRate, x and y coordinates, scale, and rotation. Also included are the display object type (movie clip), its instance name, and information about its dimensions, registration point, and transformation point. Finally, data about each keyframe is cited, including in which frame each resides, what kind of easing is used, and any information that has changed from keyframe to keyframe. In this case, only the x coordinate has been tweened, so the second keyframe itemizes only this property.


This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

Note: The Keyframe property lists frames 1 and 20 as 0 and 19 because it stores the keyframes in an array, and ActionScript arrays are zero-based. For more information about arrays, see Chapter 2.

As you can see, everything you need to reproduce the tween is included in the XML, here stored in the variable ball_xml. The last two lines of this example include the instantiation of the Animator class, passing in the XML and target movie clip instance name. This class is responsible for playing back the animation, which occurs in line 24. To see the feature work, you can remove the tween from the timeline, place a movie clip with the same instance name on the stage, and test your movie.

Note: The properties copied and applied in this process are relative to the existing movie clip, so the x coordinate updates, for example, will start from the existing location of the movie clip.

This workflow is obviously not something you can carry over to runtime. However, you can do better. With the entire tween selected, you can use the Export Motion XML command found in the Commands menu. This saves the Motion XML only, without the ActionScript that accompanies the copy-paste workflow, into an external file. From there, you can build an Animator player all your own.

The following example reproduces a motion guide tween that traces the word "Flash" in script, with a ball movie clip. The original motion guide, which is the path the Animator class will retrace, can be seen in Figure 7-11. The figure shows two copies of the word because you later can use ActionScript to scale the animation, tracing the word at either size. The XML file needed to recreate this tween is quite long, so it cannot be reproduced here. However, the original file, handwriting.fla, is with the book's source code on the companion web site, and the animation has already been exported to handwriting.xml for easy download.

Example
Figure 7-11: The motion guides used to create a tween that is recreated with the Animator class

This example creates a player that controls playback of the original animation in a new file. It requires nothing more than a movie clip to animate, and the Button component, which we'll use to create the controller buttons.

The first 10 lines of the script import the necessary classes, declare the needed variables, and create and position a ball movie clip from the library, but does not yet add it to the display list.

1  import fl.motion.*;
2  import flash.geom.*;
3  import fl.controls.Button;
4
5  var anim:Animator;
6  var isPaused:Boolean;
7  var isScaled:Boolean;
8
9  var ball:Sprite = new Ball();
10  ball.x = ball.y = 80;

The next segment covers the loading, and response thereto, of the external XML file. We'll cover this in greater detail in , but here's the essence of the matter. All URLs are handled consistently, passing through the URLRequest class. This class captures all HTML information, like MIME types, headers, and so on. In this case, we need only the URL file path to pass to the URLLoader class.

The information the URLLoader class loads can be text, raw binary data, or URL-encoded variables. In this case, the XML document is loaded as text. The event listener in line 13 reacts when this information has been completely loaded by calling the xmlLoaded() function.

11  var xml_url:URLRequest = new URLRequest("handwriting.xml");
12  var xml_loader:URLLoader = new URLLoader(xml_url);
13  xml_loader.addEventListener("complete", xmlLoaded, false, 0, true);

The xmlLoaded() function converts the loaded text to XML and instantiates the Animator class, passing both the XML and ball movie clip instance to the class. From this class, the motion object can provide information about the XML data that has been loaded. Because we know that the original tween includes color as well as position, we add line 17 to query the color in the first keyframe of that motion data, and set the initial color of the ball movie clip to that same color. This prevents the movie clip from appearing in its default color and then abruptly switching to the color of the first frame of the animation once it starts.

When these initializations are complete, it is safe to add the ball movie clip to the display list and, to round out the xmlLoaded() function, create another listener to react to the end of the animation. The listener's function, in lines 22 through 24, simply resets the name of a Play button, which we will discuss in the next code block.

14  function xmlLoaded(evt:Event):void {
15      var anim_xml:XML = XML(xml_loader.data);
16      anim = new Animator(anim_xml, ball);
17     ball.transform.colorTransform = anim.motion.keyframes[0].color;
18     addChild(ball);
19     anim.addEventListener(MotionEvent.MOTION_END, onMotionEnd,
       false, 0, true);
20  }
21
22  function onMotionEnd(evt:MotionEvent):void {
23      Button(getChildByName("Play")).label = "Play";
24  }

The following segment of ActionScript is responsible for creating all the buttons that will control the animation. The createController() function walks through a loop that creates as many buttons as are named in the array that is passed to it. Each time through the loop, an instance of the Button component is created and positioned, its width is adjusted, and its label and name are set to the string in the current index of the function array. Lastly, a mouse click listener is added to the array to trigger the function responsible for navigation, and the button is added to the display list. This process takes place five times, to match the five button names in the array passed to the function.

25  createController(["Play","Pause","Stop","Next Frame","Toggle
    Scale"]);
26
27  function createController(btns:Array):void {
28      for (var i:Number = 0; i<btns.length; i++) {
29          var btn:Button = new Button();
30          btn.x = 35 + i*100;
31          btn.y = 350;
32          btn.width = 80;
33          btn.label = btns[i];
34          btn.name = btns[i];
35          btn.addEventListener(MouseEvent.CLICK, onNav, false, 0,
            true);
36          addChild(btn);
37      }
38  }

The last function in the script handles all the navigation for the animation. When a button is clicked, the listener calls this function, passing information about the event and, by extension, the button itself. Based on the name of the button, one of five blocks in a switch statement executes, invoking methods from the Animator class, as well as other tasks. If you are unfamiliar with the switch statement, please consult Chapter 2 for more information.

The Play button first confirms that the animation is not already playing and, if it is not playing, checks to see whether it's paused. If so, it resumes playback and clears the isPaused flag. If not, it plays the animation from the beginning. The Pause button pauses the animation and sets the isPaused flag. It also switches the label of the Play button to reflect the animation's paused status. The Stop button stops and rewinds the animation, clears the isPaused flag, and sets the label of the Play button back to "Play." The last playback control simply advances the animation to its next frame.

39   function onNav(evt:MouseEvent):void {
40       switch (evt.target.name) {
41          case "Play" :
42              if (!anim.isPlaying) {
43                 if (isPaused) {
44                     anim.resume();
45                     isPaused = false;
46                 } else {
47                     anim.play();
48                 }
49             }
50             break;
51         case "Pause" :
52             anim.pause();
53             isPaused = true;
54             Button(getChildByName("Play")).label = "Resume";
55             break;
56         case "Stop" :
57             anim.stop();
58             anim.rewind();
59             isPaused = false;
60             Button(getChildByName("Play")).label = "Play";
61             break;
62         case "Next Frame" :
63             anim.nextFrame();
64             break;
65         case "Toggle Scale" :
66             var m:Matrix = anim.positionMatrix = new Matrix();
67             var s:Number;
68             if (isScaled) {
69                 s = 1;
70                 isScaled = false;
71             } else {
72                 s = .5;
73                 isScaled = true;
74             };
75             MatrixTransformer.setScaleX(m, s);
76             MatrixTransformer.setScaleY(m, s);
77             break;
78     }
79 }

The final button in the controller just begins to hint at some of the most interesting things you can do to the preexisting animations. The Animator class has a property called the positionMatrix that allows you to alter the animation as a whole. It can be shifted, scaled, rotated, and/or skewed without otherwise distorting its appearance. The final controller button toggles the animation between full- and half-scale, the paths of which are both visible in Figure 7-11. If the animation is already scaled, the Toggle Scale button will toggle the scale value between full- and half-size, and set the isScaled flag accordingly. Finally, the code uses the static MatrixTransformer class—which automatically adjusts a matrix for you to reflect your desired changes—invoking the setScaleX() and setScaleY() methods to scale the entire animation.


This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

With the ability not only to control, but also to easily transform, a potentially complex timeline tween entirely through ActionScript, the Animator class has a lot of potential for both utility and creativity. Many animations can be lovingly crafted and tweaked in the timeline and can be played back anywhere through code, again and again—even swapped and loaded from external sources at runtime. Entire libraries of highly portable animations can be created and stored in efficient XML formats. Even if you entirely focus on ActionScript, the fl.motion package, in which both the Motion and Animator classes reside, may be worth a look.

Section 7.6: Particle Systems

Example
Figure 7-12: A particle system with a gravity setting of 2 and .2

Particle systems are a way of simulating complex objects or materials that are composed of many small particles, such as fluids, fireworks, explosions, fire, smoke, water, snow, and so on. Complex systems are achievable because individual particles are generated, each is given its own characteristics, and each behaves autonomously. Further, the particles themselves are typically easy to adjust, or even replace, making it possible to alter the appearance or functionality of the system relatively easily. These are also characteristics of object-oriented programming, so it's not surprising that particle systems are often written using this approach.

To end this chapter, we'd like to create a very simple particle system—using only two classes—which looks a little bit like a psychedelic water fountain. Color circles shoot up out of the "fountain" and then fall down under the effect of gravity. shows what the system looks like.

The entry point to the particle system is the document class ParticleDemo. After declaring variables to determine the point at which the particles will appear, all the class constructor does is add an enter frame event listener to the stage. Upon every enter frame event, the listener function creates a new instance of the Particle class, which creates a particle and adds it to the display list.

1  package {
2
3      import flash.display.Sprite;
4      import flash.events.Event
5
6      public class ParticleDemo extends Sprite {
7
8         private var emitterX:Number = stage.stageWidth/2;
9         private var emitterY:Number = stage.stageHeight/2;
10
11          public function ParticleDemo() {
12             stage.addEventListener(Event.ENTER_FRAME, onLoop,
            false, 0, true);
13         }
14
15         private function onLoop(evt:Event):void {
16             var p:Particle = new Particle(emitterX,
17                                           emitterY,
18                                         Math.random()*11 − 6,
19                                         Math.random()*−20,
20                                         1,
21                                         Math.random()*0xFFFFFF);
22             addChild(p);
23         }
24     }
25 }

Five parameters pass to each class when its particle is created. The first two are the x and y coordinates of the particle emitter which dictate the origin location for each new particle. The next two parameters are randomly chosen values for the x and y velocities. The first value is for the x velocity, and is between −5 and 5. That's because the range of numbers is 0 to 11, but subtracting 6 offsets the selected value. The y velocity is between 0 and −20, so particles start by moving up. Next are a gravity value of 1 and a random color for each particle in the range of black (0x000000) to white (0xFFFFFF).

To see how the particle generator works, look within the Particle class. The first 13 lines cover the required setup, including importing the display and geom packages, and the Event class from the events package. Also included are the declaration of the position, velocity, and gravity variables that are private to this class.

1 package {
2
3     import flash.display.*;
4     import flash.geom.*
5     import flash.events.Event;
6
7     public class Particle extends Sprite {
8
9         private var _xpos:Number;
10         private var _ypos:Number;
11         private var _xvel:Number;
12         private var _yvel:Number;
13         private var _grav:Number;

First, the class constructor populates the private variables with the parameters passed in from the document class described earlier. Next, it creates a particle from the Ball class in the library and adds it to the display list. The constructor then sets four values: the particle's x and y position, opacity, and x and y scale. The same range-plus-offset technique used for x velocity when creating the particle is also used here for scale. This allows a scale range up to 200 percent, but guarantees a minimum scale of 10 percent.

14          public function Particle(xp:Number, yp:Number, xvel:Number,
            yvel:Number, grav:Number, col:uint) {
15              _xpos = xp;
16              _ypos = yp;
17              _xvel = xvel
18              _yvel = yvel
19              _grav = grav;
20
21              var ball:Sprite = new Ball();
22              addChild(ball);
23
24              x = _xpos;
25              y = _ypos;
26              alpha = .8;
27              scaleX = scaleY = Math.random() * 1.9 + .1;
28
29              var colorInfo:ColorTransform = ball.transform.
                colorTransform;
30              colorInfo.color = uint(col);
31              ball.transform.colorTransform = colorInfo;
32
33              addEventListener(Event.ENTER_FRAME, onRun, false, 0,
                true);
34          }

Lines 29 through 31 assign the particle's color by way of the ColorTransform class. The first step in this process is to store the particle's default colorTransform information (which allows manipulation of the red, blue, green, and alpha channels of the color), retrieved from the particle's transform object. Next, line 30 changes the color property of the colorTransform to the new color passed into the col argument.

However, that property prefers a uint data type (non-negative whole number) and the initial random color selection made when creating the particle converted the color to a Number data type. Therefore, it's a good idea to cast the number back to uint with the uint() data casting method. Finally, once the color has been changed, update the particle's own colorTransform object using the one in the variable you've been manipulating.

The last line of the constructor adds an enter frame listener to control the particle's movement. It triggers the onRun() function, which follows. This function uses the techniques discussed in the velocity and gravity examples of this chapter, but adds one thing. A conditional determines whether the next particle position is off the stage on the left, top, right, or bottom edge. If so, the event listener is removed and the particle is removed from the display list, ready for garbage collection.

35          private function onRun(evt:Event):void {
36              _yvel += _grav;
37              _xpos += _xvel;
38              _ypos += _yvel;
39              x = _xpos;
40              y = _ypos;
41
42              if (xpos < 0 || ypos < 0 || _xpos >
                stage.stageWidth || ypos > stage.stageHeight) {
43                 removeEventListener(Event.ENTER_FRAME, onRun);
44                 parent.removeChild(this);
45              }
46          }
47      }
48  }

Particle systems are a lot of fun and can lead to many fruitful experiments. Run this system several times, modifying the values sent to the Particle class. Change the x and y velocities for a larger spread of particles, decrease the force of gravity to see what particle life is like on the moon, or even set the emitter location to the mouse location to move the system around.

Try to move some of the hard-coded properties, like alpha, scaleX, and scaleY into the argument list so they can be varied, too. As an example, we've created another version of this system for the book's companion web site that includes several new properties, including filter and blend mode settings that you'll learn about in the next chapter.


This excerpt is from Learning ActionScript 3.0. Learning ActionScript 3.0 gives you a solid foundation in the Flash language and demonstrates how you can use it for practical, everyday projects. The book does more than give you a handful of sample scripts, defining how ActionScript and Flash work. It gives you a clear look into essential topics such as logic, event handling, displaying content, migrating legacy projects to ActionScript 3.0, classes, and much more. Written for those new to the language, this book doesn't rely exclusively on prior knowledge of object-oriented programming (OOP). Instead, it helps you expand your skillset by first focusing on clear, concise examples in the timeline, evolving into OOP examples over time-allowing you to choose the programming approach with which you are most comfortable.

buy button

Project Package

The project package for this chapter includes several of the basic formulas covered herein, including the degree-to-radian and radian-to-degree conversion, Zeno's paradox, Hooke's law, and more. These formulas apply to the project but can also be used in your own work.

Section 7.7: What's Next?

While this chapter details a variety of ActionScript animation techniques, it only begins to cover the subject of motion through code. However, the basic building blocks are here, and it's with these concepts (and related skills that grow from the ideas herein) that greater art and industry can be achieved.

Next on the to-do list is the ability to partially free yourself from the constraints of the Flash interface and approach code-only projects with a little more latitude. When working with visual assets, we've so far relied on symbols created within Flash and stored in the library of a SWF. We will continue to do that any time complex art warrants this practice, but we'll also begin to work with vectors and bitmaps created with code. In addition to giving you more freedom, this approach can also reduce file size and make your SWFs load faster.

In the next chapter, we'll discuss:

  • Using the Graphics class to draw vectors to create assets on the fly without contributing to file size
  • Calling methods of the flash.geom package to use rectangles and points in your scripts
  • Using 9-slice scaling to achieve distortion-free symbol instance scaling

Skip to any available Digital Media Help Center chapter of Learning ActionScript 3.0:
Chapter 1 | Chapter 4 | Chapter 7