Animation and Tweens Class for AS2 – taterboy
February 25th, 2009 | Filed under: ActionScript 2, Animation, Flash, Free Components/Classes, Tutorials
This is a two in one post, the first half will demonstrate how to build a small but powerful animation engine. The second half is a demo and source for an expanded version of the same animation engine.
The goals for our engine are:
1. Light Weight - We do not want to apply an onEnterFrame event handler on every MovieClip. Each instance of an onEnterFrame event increases processor usage, so we will use one onEnterFrame event to process all animation.
2. Robust - Issues can arise with you try daisy chain onEnterFrame events, garbage collection and rendering sequences can cause the chain to break. So we need a powerful management system.
3. Flexible - Some projects we only need to animate _alpha, others will need to tween _x, _y, _rotation, _xScale or a combination of them all. With a little extra code we can make this animation engine process most MovieClip Properties.
When I program something new, I make a list of all the features I need for the app. Below are the notes for this engine, now we can add the variables and methods to support each feature.
// data, array and object management. // data handler, add data to array // remove data from array // enter frame event // process animation and make tweens happen |
The Array:
First is data, we will use an array to hold all the data for each animation while the engine runs. Keeping all the data in a central location means less issues caused by garbage collection and objects being added to and removed from the stage.
Arrays are super powerful, you may be familiar with an array that looks like this:
var tempArr:Array = new Array("red","blue","green","yellow"); trace(tempArr[0]); // "red" //or var tempArr:Array = []; tempArr[0] = "red"; tempArr[1] = "blue"; tempArr[2] = "green"; tempArr[3] = "yellow"; trace(tempArr[1]); // "blue" |
This engine will require much more information that is tightly organized. For this we will add a data object to each element of the array. A data object can look similar to an array, that is because they can be indexed like an array.
var tempObj:Object = new Object(); tempObj[0] = "hello"; trace(tempObj[0]); // "hello" |
Property type variables can also be set on a data object.
tempObj.mess = "world"; trace(tempObj[0] + " " + tempObj.mess); // "hello world" |
By combining a data object and an array we can keep a ton of data that is easily retrieved while the engine runs. First we need to declare our array. This is a short hand version of setting a new array.
// data, array and object management. var animationArr:Array = []; |
Adding Data To The Array:
That was too easy, but don’t worry, it will get a little more complicated in a minute. Now we need to add data to our array, so this section will cover a method to do just that. The function will need to receive the following information as arguments then add the data to the array.
1. obj:Object - This will reference the object (MovieClip) to be animated. Object can also reference TextFields and Buttons.
2. prop:String - the property (”_x”) of the object we want to animate. To animate multiple properties for the same object, we need to call this method one time for each property. It may seem like extra work, but gives the engine greater flexibility.
3. val:Number - the value we desire the object property to be when the animation has finished.
4. ease:Number - The amount of ease to apply to the object property as it changes over time. Easing can require complicated equations, but we will keep things as simple as possible. If a positive number, the tween will have no ease and process the property changes over the amount of frames specified linearly. If a negative number, we will use that value in a simple easing equation that will cause an ease effect by incrementing the property value less and less as it gets closer to it’s destination value.
5. end:Function - To allow daisy chaining, we will be able to call a function when each animation has ended.
This method will execute the following:
A. Send data to the removeTween method to make sure we are not duplicating an animation of any property animations, that would be bad. We will pass the object and the property string, so the removeTween method can validate and remove the correct element if it exists.
B. Find the tween increment amount (jump) to adjust the object property each frame.
C. Enter the new data as an object into the array using push. To do this we will use the curly brackets { } to designate an object. similar to how square brackets [ ] designate an array. Inside the curly brackets we will assign each variable (variable name, colon (:), variable value). A comma separates each element in the object list, similar to an array.
// data handler, add data to array function insertTween(obj:Object, prop:String, val:Number, ease:Number, end:Function){ //A. manage duplicates in the array removeTween(obj, prop); //B. figure out the tween increment for the frames specified. // target value minus the current value, as a positive number, divided by the number of frames. var dif:Number = Math.abs(obj[prop] - val) / ease; //C. add data to the animation array animationArr.push({ aniObj: obj, propStr: prop, propValue: val, jump: dif, aniEase: ease, endFunction: end }); } |
Remove Element or Duplicates:
We need to make sure that that the same object property is not trying to be animated twice. Say you have an animation connected to a button that slides something up, then when the button is clicked again, it slides back down. If the button is pressed before the object tween is complete we need to end the first animation, then we need to start the tween back down starting at it’s current location.
This method will loop through the animationArr and look for any duplicates, if one is found, then it is removed. We will also use this method when a tween is complete to remove the animation from the array.
We need two arguments to validate a tween match, the object and the property to be animated. There may be two instances of the object in the array as long as they are for different properties.
1. obj:Object - Object to be animated
2. prop:String - The property of the object to be animated.
As we loop through the array, we will compare the obj in the argument to the aniObject of each element. If we get a match on the Object and the property, then the element will be removed using Array.splice(), then we will stop the loop. As long as duplicates are always checked before data is entered into the array, there should never be an instance of more than one element matching.
// remove data from array function removeTween(obj:Object, prop:String){ for(var p:String in animationArr ){ if(animationArr[p].obj == targObj && animationArr[p].prop == prop){ animationArr.splice(Number(p),1); break; } } } |
To keep things running fast, we only want to use one onEnterFrame event to run everything. You may also add other method calls to this event listener to power other features in your application.
We will use another for loop to pass the object from each array element to the animation handler method. That is where all the real magic happens.
// enter frame event this.onEnterFrame = function(){ for(var p:String in animationArr){ animationHandler(animationArr[p]); } } |
Finally making things move, this method will receive a data object and increment the object properties to be tweened.
1. obj:Object - This is the data object from addTween method, it has references to the visible objects to be animated as well as the property and easing information.
A. Set the current property value to “tempVal”
B. Find out if the value is increasing or decreasing.
C. If the value is increasing, we need addition.
D. Let’s talk about easing, if the ease value is above 0, we are going to use the animation increment (jump) we made in the addTween method. This is super easy but makes for boring animation. If the value of ease is less then 0, we will use a simple easing equation. ((t - c )/ e) 1. We get the difference of the current value and the target value (t-c). 2. Then divide that value by ease amount. We are only using the negative value to differentiate if we want an ease or linear animation, so we need to convert the ease value to a positive number using Math.abs(ease). Every time the object property value moves closer to target value the difference is divided by much smaller numbers. This causes the tween to move slower as the target value is reached creating a very accentuated ease effect.
The only catch is if you have a function call set to go off at the end of this animation, the ease ,if set too high, can cause the animation to take an extra second to finish before the function is called.
E. Check to see if the destination has been reached or over shot, if so, set the value manually to the target value, remove the tween and fire off the function call if present.
F. Now do the same thing as above, but use subtraction this time.
// process animation function animationHandler(obj){ //A. get current value of object property. var tempVal:Number = obj.aniObj[obj.propStr]; //B. check if value should go up or down. var dir:Boolean; tempVal < obj.propValue ? dir = true: dir = false; //C. if value needs to increase we add. if(dir){ //D. check for an ease value, if linear, increase using jump amount, else use the ease equation. obj.aniEase > 0 ? tempVal += obj.jump: tempVal += (obj.propValue - tempVal) / Math.abs(obj.aniEase); //E. if destination has been reached, remove tween from array and call function. if(Math.round(tempVal) >= obj.propValue){ tempVal = obj.propValue; //remove tween removeTween(obj.aniObj, obj.propStr); //call end function if present if(obj.endFunction != null){ obj.endFunction(); } } // apply the tempVal to the object property. By setting the value in this way, // we are able to tween any property using the same code // Example: (boxMC["_x"] = 200; will set the _x property of a MovieClip instance // called "boxMC" to 200. obj.aniObj[obj.propStr] = tempVal; } else{ // same as above except using subtraction. obj.aniEase > 0 ? tempVal -= obj.jump: tempVal -= (tempVal - obj.propValue) / Math.abs(obj.aniEase); if(Math.round(tempVal) <= obj.propValue){ tempVal = obj.propValue; //remove tween removeTween(obj.aniObj, obj.propStr); if(obj.endFunction != null){ obj.endFunction(); } } obj.aniObj[obj.propStr] = tempVal; } } |
Now that all the stuff is done, we can initiate the engine with one line of code.
insertTween(boxMC, "_x", 360, 10, null); |
To animate more properties of an object we need to add a call for each property to be animated. The following example will animate the _x, _y, and _alpha properties of a MovieClip instance called “boxMC” over 15 frames. You can change the ease values to create more interesting animations.
insertTween(boxMC, "_x", 360, 15, null); insertTween(boxMC, "_y", 300, 15, null); insertTween(boxMC, "_alpha", 0, 15, null); |
The New Animation Class:
This was originally built in 2002, but it was too complicated to use with most projects. It has been revised a few times over the years into the latest, simpler incarnation. There are more powerful solutions out there like Tweener, but I think this is simple and light enough for most of my needs.
Features:
1. Tweens all common properties for MovieClip like: _x, _y, _width, _height, _xScale, _yScale, _rotation and _alpha.
2. Easing is not perfect code-wise, but looks and works pretty nice. Includes a Tweens class with Super Ease, ease in and ease out.
3. Simple to use.
4. Multiple instances of this class can run at the same time, not that you should need to, but it is possible.
5. This class uses an interval instead of onEnterFrame form more consistent animation across projects that are set to different frame rates. Based on 24 fps.
This version adds an argument for speed. With expanded ease functionality we needed an extra argument.
Class Usage:
The following sample will animate the _x property of a MovieClip instance named “boxMC” over twenty frames with a ease value of 80.
// import and register the Animation class. import com.hdi.as2.Animation; var ae:Animation = new Animation(); // sample call to Animation class to initiate a tween. ae.setTrans(boxMC,350,"_x",20,80,null); //Arguments: //ae.setTrans(objMC:Object, propValue:Number, property:String, speed:Number, ease:Number, endFunction:Function); |
In the demo below you can change the property, value, speed and ease values of each tween by updating the input fields and pressing the tween button. Ease values between 0 - 100 and -1 and -100 produce similar tweens to the standard Flash tween. Ease Values between greater than 100 and less than -100 will produce a super tween and the speed value will be ignored. Super Ease values between 103 - 120 and -103 - -120 produce the best results.
Demo
|
No comments yet.
RSS feed for comments on this post. TrackBack URL