Tuesday, February 22, 2011

AS3 Effect Fly

The Fly effect launches an object into the air.
Define properties velocity and degrees/radians.
You can define fromX and fromY (target's position by default).
You can also apply gravity and/or friction.
Define friction through properties frictionVelocity and frictionDegrees/frictionRadians.
You can change the speed/precision of the animation using property frameRate.
Property bounds is necessary to end the animation. Property bounds has a default value that is usually outside of the screen.


mitch/effects/Fly.as

package mitch.effects
{
import flash.geom.Rectangle;

import mitch.effects.effectClasses.FlyInstance;

import mx.effects.IEffectInstance;

import spark.effects.Animate;

/**
* The Fly effect extends the Animate effect.
* Though, property <code>motionPaths</code> is created dynamically on the effect instance.
*
* @see mitch.effects.effectClasses.FlyInstance
*
* @author Mitch
*
*/

public class Fly extends Animate
{

//------------------------------------------------------------
//
// Static Members
//
//------------------------------------------------------------

/**@private */
private static const PI2:Number = Math.PI * 2;

//------------------------------
// getRadians()
//------------------------------

/**
* Converts degrees to radians.
* @param degrees Angle in degrees
* @return Angle in radians.
*
*/

static public function getRadians(degrees:Number):Number {
return normalizeRadians(degrees * Math.PI / 180);
}

//------------------------------
// getDegrees()
//------------------------------

/**
* Converts radians to degrees.
* @param radians Angle in radians.
* @return Angle in degrees.
*
*/

static public function getDegrees(radians:Number):Number {
return normalizeDegrees(radians * 180 / Math.PI);
}

//------------------------------
// normalizeDegrees()
//------------------------------

/**
* Returns a value between 0 and 360. <br/>
* For example; if parameter <code>degrees</code> is set to 450, the return value would be 90.
* @param degrees The anngle to normalize.
* @return The specified angle, normalized between 0 and 360.
*/

static public function normalizeDegrees(degrees:Number):Number {
while(degrees > 360) {
degrees -= 360;
}
while(degrees < 0) {
degrees += 360;
}
return degrees;
}

//------------------------------
// normalizeRadians()
//------------------------------

/**
* Returns a value between <code>(Math.PI * -2)</code> and <code>(Math.PI * 2)</code>.
* @param radians The angle to normalize.
* @return The specified angle, normalized between <code>Math.PI * -2</code> and <code>Math.PI * 2</code>.
*/

static public function normalizeRadians(radians:Number):Number {
while (radians >= PI2 )
{
radians -= PI2;
}
while (radians < -PI2 )
{
radians += PI2;
}
return radians;
}




/**
* @private
* Storage for property degrees/radians.
*/

private var _degrees:Number = 0;
/**
* @private
* Storage for property frictionDegrees/fricitonRadians.
*/

private var _frictionDegrees:Number = 0;

//------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------

/**
* Constructor.
*
* @param target The component to animate with this effect.
*
*/

public function Fly(target:Object=null)
{
super(target);
easer = null;
instanceClass = FlyInstance;
}




//------------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------------

//------------------------------
// fromX
//------------------------------

/**
* The initial x coördinate, in pixels.
*/

public var fromX:Number = NaN;

//------------------------------
// fromY
//------------------------------

/**
* The initial y coördinate, in pixels.
*/

public var fromY:Number = NaN;

//------------------------------
// gravity
//------------------------------

/**
* The amount gravity to apply, in pixels per frame.
*/

public var gravity:Number = 1;

//------------------------------
// velocity
//------------------------------

/**
* The initial velocity, in pixels per frame.
*/

public var velocity:Number = 25;

//------------------------------
// radians
//------------------------------

/**
* The initial angle, in radians.
*/

public function get radians():Number { return degrees * Math.PI / 180; }
/**@private */
public function set radians(value:Number):void {
degrees = getDegrees(value);
}

//------------------------------
// degrees
//------------------------------

/**
* The initial angle, in degrees.
*/

public function get degrees():Number { return _degrees; }
/**@private */
public function set degrees(value:Number):void {
_degrees = normalizeDegrees(value);
}


//------------------------------
// frictionRadians
//------------------------------

/**
* The friction angle, in radians.
*/

public function get frictionRadians():Number { return frictionDegrees * Math.PI / 180; }
/**@private */
public function set frictionRadians(value:Number):void {
frictionDegrees = getDegrees(value);
}

//------------------------------
// frictionDegrees
//------------------------------

/**
* The friction angle, in degrees.
*/

public function get frictionDegrees():Number { return _frictionDegrees; }
/**@private */
public function set frictionDegrees(value:Number):void {
_frictionDegrees = normalizeDegrees(value);
}


//------------------------------
// frictionVelocity
//------------------------------

/**
* The friction velocity, in pixels per frame.
*/

public var frictionVelocity:Number = 0;

//------------------------------
// bounds
//------------------------------

/**
* The minimum and maximum values for properties <code>x</code> and <code>y</code> in this effect.
* The effect ends when the x and y coördinates exceed the rectangular region defined by this property.
*/

public var bounds:Rectangle = new Rectangle(-1000, -1000, 3000, 3000);

//------------------------------
// frameRate
//------------------------------

/**
* The effect speed, in frames per second.
* <p>Larger values cause the effect to play faster. Smaller values cause the effect to play slower.</p>
*/

public var frameRate:uint = 30;






//------------------------------------------------------------
//
// Overridden Methods
//
//------------------------------------------------------------

//------------------------------
// initInstance()
//------------------------------

/**
* @copy spark.effects.Animate#initInstance()
*/

protected override function initInstance(instance:IEffectInstance):void
{
//set motionPaths before initializing.
var inst:FlyInstance = FlyInstance(instance);
inst.fromX = fromX;
inst.fromY = fromY;
inst.bounds = bounds;
inst.degrees = degrees;
inst.frameRate = frameRate;
inst.frictionDegrees = frictionDegrees;
inst.frictionVelocity = frictionVelocity;
inst.gravity = gravity;
inst.velocity = velocity;
super.initInstance(instance);
}

}
}



The FlyInstance class sets it's own motionPaths property in method play().
Rotation is handled in overridden method animationUpdate().

mitch/effects/effectClasses/FlyInstance.as

package mitch.effects.effectClasses
{
import flash.geom.Rectangle;

import mitch.effects.Fly;

import spark.effects.animation.Animation;
import spark.effects.animation.Keyframe;
import spark.effects.animation.MotionPath;
import spark.effects.supportClasses.AnimateInstance;

/**
* The FlyInstance class implements the instance class for the Fly effect.
* Flex creates an instance of this class when it plays a Fly effect; you do not create one yourself.
*
* @see mitch.effects.Fly
*
* @author Mitch
*
*/

public class FlyInstance extends AnimateInstance
{
/**
* @private
* Storage for the current x coordinate. Updates when the effect updates.
*/

private var _currentX:Number = NaN;
/**
* @private
* Storage for the current y coordinate. Updates when the effect updates.
*/

private var _currentY:Number = NaN;
/**
* @private
* Storage for property degrees/radians.
*/

private var _degrees:Number = 0;
/**
* @private
* Storage for property frictionDegrees/frictionRadians.
*/

private var _frictionDegrees:Number = 0;




//------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------

/**
* Constructor.
* @param target The component to animate with this effect.
*
*/

public function FlyInstance(target:Object)
{
super(target);
}




//------------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------------

//------------------------------
// fromX
//------------------------------

/**
* @copy mitch.effects.Fly#fromX
*/

public var fromX:Number;

//------------------------------
// fromY
//------------------------------

/**
* @copy mitch.effects.Fly#fromY
*/

public var fromY:Number;

//------------------------------
// gravity
//------------------------------

/**
* @copy mitch.effects.Fly#gravity
*/

public var gravity:Number;

//------------------------------
// velocity
//------------------------------

/**
* @copy mitch.effects.Fly#velocity
*/

public var velocity:Number;

//------------------------------
// radians
//------------------------------

/**
* @copy mitch.effects.Fly#radians
*/

public function get radians():Number { return degrees * Math.PI / 180; }
/**@private */
public function set radians(value:Number):void {
degrees = Fly.getDegrees(value);
}

//------------------------------
// degrees
//------------------------------

/**
* @copy mitch.effects.Fly#degrees
*/

public function get degrees():Number { return _degrees; }
/**@private */
public function set degrees(value:Number):void {
_degrees = Fly.normalizeDegrees(value);
}


//------------------------------
// frictionRadians
//------------------------------

/**
* @copy mitch.effects.Fly#frictionRadians
*/

public function get frictionRadians():Number { return frictionDegrees * Math.PI / 180; }
/**@private */
public function set frictionRadians(value:Number):void {
frictionDegrees = Fly.getDegrees(value);
}

//------------------------------
// frictionDegrees
//------------------------------

/**
* @copy mitch.effects.Fly#frictionDegrees
*/

public function get frictionDegrees():Number { return _frictionDegrees; }
/**@private */
public function set frictionDegrees(value:Number):void {
_frictionDegrees = Fly.normalizeDegrees(value);
}


//------------------------------
// frictionVelocity
//------------------------------

/**
* @copy mitch.effects.Fly#frictionVelocity
*/

public var frictionVelocity:Number;

//------------------------------
// bounds
//------------------------------

/**
* @copy mitch.effects.Fly#bounds
*/

public var bounds:Rectangle;

//------------------------------
// frameRate
//------------------------------

/**
* @copy mitch.effects.Fly#frameRate
*/

public var frameRate:uint;




//------------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------------

//------------------------------
// play()
//------------------------------

/**@copy spark.effects.supportClasses.AnimateInstance#play() */
public override function play():void
{
createMotionPaths();
super.play();
}

//------------------------------
// createMotionPaths()
//------------------------------

/**
* Creates a Vector containing a MotionPath object for each of the properties
* <code>x</code>, <code>y</code> and <code>rotation</code>.
* And sets property <code>motionPaths</code> to that vector.
*
*/

public function createMotionPaths():void
{
//Vector containing MotionPaths
var result :Vector.<MotionPath> = new Vector.<MotionPath>(2, true);

//x
result[0] = new MotionPath("x");
result[0].keyframes = new Vector.<Keyframe>();
//y
result[1] = new MotionPath("y");
result[1].keyframes = new Vector.<Keyframe>();

//Numbers
var time :Number = 0;
var delay :Number = 1000 / frameRate;
var xcurrent :Number = isNaN(fromX)==false?fromX:(target?target.x:0);
var ycurrent :Number = isNaN(fromY)==false?fromY:(target?target.y:0);
var xvelocity :Number = Math.cos(radians) * velocity;
var yvelocity :Number = Math.sin(radians) * velocity;
var xfriction :Number = Math.cos(frictionRadians) * frictionVelocity;
var yfriction :Number = Math.sin(frictionRadians) * frictionVelocity;
var rcurrent :Number = getRotation(xvelocity + xfriction, yvelocity + yfriction);
//add first keyframes
var xframes:Vector.<Keyframe> = result[0].keyframes;
var yframes:Vector.<Keyframe> = result[1].keyframes;
result[0].keyframes.push(new Keyframe(time, xcurrent));
result[1].keyframes.push(new Keyframe(time, ycurrent));


//while not out of bounds
while(bounds.contains(xcurrent, ycurrent))
{
//adjust time
time += delay;

//add gravity to velocity
yvelocity += gravity;

//add friction to velocity
if(Math.round(rcurrent) < frictionDegrees + 90 -1 || Math.round(rcurrent) > frictionDegrees + 90 + 1) {
xvelocity += xfriction;
yvelocity += yfriction;
}
//apply velocity
xcurrent += xvelocity;
ycurrent += yvelocity;
//apply rotation
rcurrent = getRotation(xvelocity, yvelocity);
//save keyframes
result[0].keyframes.push(new Keyframe(time, xcurrent));
result[1].keyframes.push(new Keyframe(time, ycurrent));
}
super.motionPaths = result;
}

//------------------------------
// animationUpdate()
//------------------------------

/**@copy spark.effects.supportClasses.AnimateInstance#animationUpdate() */
public override function animationUpdate(animation:Animation):void
{
if(isNaN(_currentX)) {
_currentX = animation.currentValue.x;
}
if(isNaN(_currentY)) {
_currentY = animation.currentValue.y;
}
var xv:Number = Number(animation.currentValue.x) - _currentX;
var yv:Number = Number(animation.currentValue.y) - _currentY;
_currentX = Number(animation.currentValue.x);
_currentY = Number(animation.currentValue.y);

// Update the rotation during animation.
// Instead of using MotionPath
// and relying on the effect framework.
target.rotation = getRotation(xv, yv);

super.animationUpdate(animation);
}

//------------------------------
// animationEnd()
//------------------------------

/**@copy spark.effects.supportClasses.AnimateInstance#animationEnd() */
public override function animationEnd(animation:Animation):void
{
_currentX = NaN;
_currentY = NaN;
super.animationEnd(animation);
}

//------------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------------

//------------------------------
// getRotation()
//------------------------------

/**
* Calculates the rotation in degrees, using the specified point.
* <p>The x and y coördinates can be interpreted as a Vector,
* assuming that the second point is <code>(0, 0)</code>. And an angle can be extracted from the Vector.</p>
* @param x The x coördinate to use for calculating the angle.
* @param y The y coördinate to use for caluclating the angle.
* @return The angle in degrees, calculated using the x and y coördinates.
*
*/

protected function getRotation(x:Number, y:Number):Number
{
//calculate degrees
var result:Number = Math.atan(y/x) * 180 / Math.PI;
if(x < 0) {
result -= 180;
}
return Fly.normalizeDegrees(result);
}
}
}

No comments:

Post a Comment