There are many motion effects that can be created in Actionscript. Many of them are based on the laws of physics and they build upon one another in a logical sequence. We will examine:
These effects are simulations (not real-world examples) so that they can be easily implemented into any project. They don't take into account every factor (i.e., wind resistance, etc.) that can be exerted on an object. For simplicity sake, the direction will be kept in a straight line in these examples. Examples with both x/y will also be given but not explained in detail.
IMPORTANT NOTE: Unlike the traditional Cartesian coordinate system where the y direction is positive when moving UP. In the Flash coordinate system, the y direction is positive when moving DOWN, not up. This is because Flash treat the default starting point of a display object object at x/y coordinates of (0,0). Moving away from this TOP-LEFT edge of the stage means the value of x and y will be positive as you move away from this point.
There are two types of ActionScript animation you can create in Flash.
However, in the real world, animation is time-based. While time-based examples can be created in Flash, we will be creating code in these examples using frames. The time-based variables will be replaced with frame-based variables. As a result, the frame variable will always be 1.
Speed is defined as distance/time (i.e., 85 mph). In technical term, it is the ratio of the distance cover over the time taken to cover that distance.
For example, 60 miles/hr is typically read as "60 miles per hour." In essence, you will have covered a distance of 60 miles in 1 hour. In this example, it is easy to see that the distance is 60 miles. However, in a more complex example, such 90 miles/2 hours would equate to 180 miles (Distance = speed * time). Time traveled would be: Time = distance/speed. In our exampes, we will replace all references of time with frames.
However, because we will be using frame-based animation, our equation will be distance/frame. Speed is expressed as a SCALAR quantity which means that its value is expressed with a magnitude only (75 mph). Velocity which is the rate of change of speed, on the other hand, is expressed as a VECTOR quantity which means that it is expressed with both magnitude and direction (75 mph north).
Speed by itself is not a vector nor is direction. It takes both magnitude (speed) and direction to be a vector. Acceleration and force are also vectors.
While speed is expresed in how fast an object is moving (i.e., 75 mph), velocity is the speed in a given direction (75 mph north). Velocity is easy to create in Flash because we will update the position for every frame--so if an object has a velocity of 5 that means it will move 5 pixels for every frame. To animate an object with a CONSTANT SPEED, all you have to do is INCREMENT or DECREMENT its X or Y value.
All animation with code is done with some type of loop. The ENTER_FRAME event is typically used to create this effect. It is helpful to think on the ENTER_FRAME as a type of endless loop but based on the frame rate (fps) of the movie. There are two ways to increment or decrement a property (in this case, x or y or both):
// Velocity ball ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // These variables will control the amount that the movie clip move on the stage // for every frame iteration for both x and/or y. var xVelocity:Number = 8; // Velocity expressed in speed (8)s and direction (implied). // var yVelocity:Number = 8; addEventListener (Event.ENTER_FRAME, onVelocity); function onVelocity (eventObject:Event):void { Ball0.x += xVelocity; // Position is incrementally updated based on the xVelocity value. // Ball0.y += yVelocity; }
See page 64-65 of flash mx game design.
While a constant velocity can be thought of as turning your car's cruise control on so that it moves at a uniform speed, acceleration can be thought of as turning off the cruise control and pressing the accelerator pedal to INCREASE the speed of the car.
Acceleration is a vector quantity and is defined as the rate of change in velocity over time.
Acceleration is the ratio of difference in velocity over the difference in time over whcih the acceleratin occurred. In equation form, it would look like this:
If you INCREASE the rate of change of an object, you will accelerate the object. For example, if an object is moving at a constant 5 pixels for every frame, then it will travel 50 pixels in a span of 10 frames (10 fps). However, if you add a 1 pixel "acceleration" to each frame the object will travel 95 frames (5+6+7+8+9+10+11+12+13+14) in a ten frame span of 10 fps.
[SHOW PICTURE TO DEPICT THIS]
To create acceleration, all you have to do in increase the velocity by the acceleration rate (in this case, 1) for every frame iteration.
// Acceleration ball ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ var xVelocity2:Number = 8; // var yVelocity2:Number = 8; var xAcceleration:Number = 1; // var yAcceleration:Number = 1; addEventListener (Event.ENTER_FRAME, onAcceleration); function onAcceleration (eventObject:Event):void { Ball.x += xVelocity2; // Ball.y += yVelocity2; xVelocity2 += xAcceleration; // yVelocity2 += yAcceleration; }
See page 68-69 of flash mx game design.
A gravity effect can be create and then attached to an object or several objects. This effect is particually useful when creating games. As mentioned earlier, gravity can be thought of as acceleration in the y direction instead of the x direction.
//Gravity ball ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ var xVelocity:Number = 10; var yVelocity:Number = -10; var xAcceleration:Number = 1; var yAcceleration:Number = 1; var gravity:Number = .5; addEventListener (Event.ENTER_FRAME, onGravity); function onGravity (eventObject:Event):void { xVelocity += xAcceleration; yVelocity += yAcceleration; // NOTE: It is important to place the gravity is in the middle of velocity and position variables. // The yVelocity is changed based on the yAcceleration. Then the Ball.y position is changed based on the yVelocity. yVelocity += gravity; Ball.x += xVelocity; Ball.y += yVelocity; }
In order to create a bounce effect, you have to have something for it to bouce off of. So, a series of "if/then/else" statements
will be created to add borders for the object (i.e., Ball) based on the stage. A rectangle that is the same demension as the stage was created for visual appeal.
package { // Import necessary classes import flash.display.Sprite; import flash.events.Event; import flash.geom.Rectangle; public class BallWithBoundaries extends Sprite { // Horizontal and Vertical velocity (speed & direction) public var velocityX:int = 10; public var velocityY:int = -10; // Constructor function public function BallWithBoundaries () { addEventListener (Event.ENTER_FRAME, onBounce); } // Cause Ball to bounce of boundaries private function onBounce (eventObject:Event):void { // Move ball based on velocity. The word "this" is optional. this.x += velocityX; this.y += velocityY; // Get bounding box rectangle of ball var bounds:Rectangle = getBounds(parent); //trace(bounds) // Reverse horizontal direction if Ball hit left/right side of stage. "-1" could also be used. if (bounds.left < 0 || bounds.right > stage.stageWidth) { //velocityX *= -1; velocityX = - velocityX; } // Reverse vertical direction if Ball hit top/bottom side of stage if (bounds.top < 0 || bounds.bottom > stage.stageHeight) { //velocityY *= -1; velocityY = - velocityY; } } } }
NOTE: Using the steps above, you created the graphics and attached the code for it all from within the Convert to Symbol dialogo box. In the previous example, you used the Library panel, etc. It is important to note that the ActionScript code is "attached" to the ball in the Library (not in the timeline or an external document class).
In the previous example, the "code" for the ball is attached to it in the library, so you have to drag a copy of it from the library to the stage to see the ball bouncing. Howvever, you could delete the ball from the stage and then recreate the bouncing effect in code.
For example, delete the ball from the stage and then add the following code to Layer1/Frame 1 of the timeline and save and test the movie again::
var myBall:BallWithBoundaries = new BallWithBoundaries() myBall.x = stage.stageWidth/2; myBall.y = stage.stageHeight/2; addChild(myBall);
Because the bounce "behavior" is attached to the ball in the library, you could also drag multiple instances of the ball onto the stage and they will "behave" similiarly. You could also change their size, alpha, tint, etc. because these properties can be different for each instance. Because each instance in placed at different location on the stage, it appears that they are behaving differently. However, if you look carefully when the movie first starts, you will notice that they all follow a similar path. See demo below:
The problem with the previous example it that you have to MANUALLY drag and drop each instance on the stage and then change properties (i.e., size, alpha, tint, etc.) for each instance. It would be better to create a class file that could AUTOMATICALLY create all of the instances on stage and randomly set their properties (i.e., size, alpha, etc.):
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.display.MovieClip; public class BallsOnStageMovingMultiple extends MovieClip { private var ball:Ball; private var vx:Number; private var vy:Number; private var bounce:Number = -0.7; private var gravity:Number = .5; private var left:Number; private var right:Number; private var top:Number; private var bottom:Number; public function BallsOnStageMovingMultiple () { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; for (var i:uint=0; i<150; i++) { var ball:Sprite = Sprite(new Ball()); ball.x = Math.random() * 550; ball.y = Math.random() * 400; vx = Math.random() * 10 - 5; // vy = Math.random() * -10 - 5; // ball.scaleX = ball.scaleY = Math.random() * 1 - .7; ball.alpha = Math.random() * 1 - .5; addChild (ball); ball.addEventListener (Event.ENTER_FRAME, onMove); } } function onMove (eventObject:Event):void { //Get a reference to the current object on a EnterFrame event var ball:Sprite = Sprite(eventObject.currentTarget); //vy += gravity; ball.x += vx; ball.y += vy; //Boundaries~~~~~~~~~~~~~~ left = 0; right = stage.stageWidth; top = 0; bottom = stage.stageHeight; if (ball.x + 30 > right) { ball.x = right - 30; //vx *= bounce; vx = - vx; } else if (ball.x - 30 < left) { ball.x = left + 30; //vx *= bounce; vx = - vx; } if (ball.y + 30 > bottom) { ball.y = bottom - 30; //vy *= bounce; vy = - vy; } else if (ball.y - 30 < top) { ball.y = top + 30; //vy *= bounce; vy = - vy; } } } }
Instead of having the ball randomly move on the stage, we will add the ability for the ball to be thrown. Throwing involves dragging an object in a particular direction and then releasing it. The challenge is to track its start and end positions to determine the direction and velocity to use AFTER the object is released.
First, let's create a ball class. Instead of using a ball that is on the stage or retrieved from the library, we will first create a "virtual" ball by creating a custom class for it:
package { import flash.display.Sprite; public class Ball extends Sprite { public var radius:Number; public var color:uint; public function Ball (radius:Number=60, color:uint=0x00ff00) { this.radius = radius; this.color = color; graphics.beginFill (color); graphics.drawCircle (0, 0, radius); graphics.endFill (); } } }
NOTE: This Ball class can be used in any movie that needs a "virtual" ball. The Ball class will be used next in the DragAndThrow Class as a document class inside the actually Flash file (DragAndThrow.fla).
Next, we will create the DragAndThrow class.
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.display.MovieClip; public class DragAndThrow extends Sprite { private var ball:Ball; private var vx:Number; private var vy:Number; private var bounce:Number = -0.7; private var gravity:Number = .5; private var oldX:Number; private var oldY:Number; public function DragAndThrow () { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; ball = new Ball(); ball.x = stage.stageWidth / 2; ball.y = stage.stageHeight / 2; ball.buttonMode = true; vx = Math.random() * 10 - 5; vy = -10; addChild (ball); ball.addEventListener (MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener (Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame (event:Event):void { vy += gravity; ball.x += vx; ball.y += vy; var left:Number = 0; var right:Number = stage.stageWidth; var top:Number = 0; var bottom:Number = stage.stageHeight; if (ball.x + ball.radius > right) { ball.x = right - ball.radius; vx *= bounce; } else if (ball.x - ball.radius < left) { ball.x = left + ball.radius; vx *= bounce; } if (ball.y + ball.radius > bottom) { ball.y = bottom - ball.radius; vy *= bounce; } else if (ball.y - ball.radius < top) { ball.y = top + ball.radius; vy *= bounce; } } private function onMouseDown (event:MouseEvent):void { oldX = ball.x; oldY = ball.y; stage.addEventListener (MouseEvent.MOUSE_UP, onMouseUp); ball.startDrag (); removeEventListener (Event.ENTER_FRAME, onEnterFrame); addEventListener (Event.ENTER_FRAME, trackVelocity); } private function onMouseUp (event:MouseEvent):void { stage.removeEventListener (MouseEvent.MOUSE_UP, onMouseUp); ball.stopDrag (); removeEventListener (Event.ENTER_FRAME, trackVelocity); addEventListener (Event.ENTER_FRAME, onEnterFrame); } private function trackVelocity (event:Event):void { vx = ball.x - oldX; vy = ball.y - oldY; oldX = ball.x; oldY = ball.y; } } }
NOTE: The key to determine the velocity once the object is released is to subtract the new position from the old position. [SEE SCREENSHOT on page 183 of the ActionScript 3.0 Animation book]. The initial x/y position is stored in two variables (oldX and oldY). That way, when the object is dragged to a new position, its new position x/y values can be substracted from the old ones.The enterFrame handler is removed BEFORE the trackVelocity is ADDED using the MouseDown method. When the object is released, the enterFrame is reversed again.
Finally, we will create the main Flash movie:
There are many occasions where you have to create a bouncing effect using multiple objects. While the example below is not perfect, it does show that you can create objects the reacts to bouncing.
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.display.MovieClip; public class PlatForm extends Sprite { private var ball:Ball; private var vx:Number; private var vy:Number; private var bounce:Number = -0.7; private var gravity:Number = .5; private var oldX:Number; private var oldY:Number; public function PlatForm () { stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; ball = new Ball(); ball.x = stage.stageWidth / 2; ball.y = stage.stageHeight / 2; ball.buttonMode = true; vx = Math.random() * 10 - 5; vy = -10; addChild (ball); ball.addEventListener (MouseEvent.MOUSE_DOWN, onMouseDown); addEventListener (Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame (event:Event):void { vy += gravity; ball.x += vx; ball.y += vy; var left:Number = 0; var right:Number = stage.stageWidth; var top:Number = 0; var bottom:Number = stage.stageHeight; if (ball.x + ball.radius > right) { ball.x = right - ball.radius; vx *= bounce; } else if (ball.x - ball.radius < left) { ball.x = left + ball.radius; vx *= bounce; } if (ball.y + ball.radius > bottom) { ball.y = bottom - ball.radius; vy *= bounce; } else if (ball.y - ball.radius < top) { ball.y = top + ball.radius; vy *= bounce; } if (ball.hitTestObject(Wall1)) { trace ("hit"); vx *= bounce; vy *= bounce; //vy += gravity; ball.x += vx; ball.y += vy; } if (ball.hitTestObject(Wall2)) { trace ("hit"); vx *= bounce; vy *= bounce; //vy += gravity; ball.x += vx; ball.y += vy; } } private function onMouseDown (event:MouseEvent):void { oldX = ball.x; oldY = ball.y; stage.addEventListener (MouseEvent.MOUSE_UP, onMouseUp); ball.startDrag (); removeEventListener (Event.ENTER_FRAME, onEnterFrame); addEventListener (Event.ENTER_FRAME, trackVelocity); } private function onMouseUp (event:MouseEvent):void { stage.removeEventListener (MouseEvent.MOUSE_UP, onMouseUp); ball.stopDrag (); removeEventListener (Event.ENTER_FRAME, trackVelocity); addEventListener (Event.ENTER_FRAME, onEnterFrame); } private function trackVelocity (event:Event):void { vx = ball.x - oldX; vy = ball.y - oldY; oldX = ball.x; oldY = ball.y; } } }
info