Motion: Chapter 7 - Learning ActionScript 3.0
Pages: 1, 2, 3, 4

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;
4
5  var speed:Number = 12;
6  var angle:Number = 45;
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.

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.

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;

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 {
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

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;
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);
13 }
14
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;
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 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.

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;
4
5  var speed:Number = 12;
6  var angle:Number = 45;
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.

 Pages: 1, 2, 3, 4