Pages

Google+

Sunday, September 13, 2009

Optimizing Flash Animations Via Lookup Tables

This is cool.


Computer Science guys know plenty of coding Kung Fu; for us Flashers,  we have to either need some CS friends hangin' around, or discover it ourselves. This time I got lucky and had both.



Check out the example below ( if your cache is disabled, it will take a bit to load ):



[SWF]http://blog.alanklement.com/files/examples/lookup_tables/shoescroller.swf, 500, 300[/SWF]

This is a sample of a navigation system I built on Friday, for demonstration purposes I am having it loop. Now, this animation is nothing particularly exciting, navigation systems like this have been done many times. Each item is having 5 properties considered when it is being animated: 'x', 'y', 'alpha', 'scaleX' and 'scaleY'.  Again, nothing remarkable; however, the interesting part is: the applet is not calculating the properties each item needs to animate to.  The vales are all pre-calculated, potentially saving a great deal of processing power and probably coding headache.


Consider a common methodology for coding an applet like this.  The designer would figure out some formula for determining depth and focal length, and then use that formula to layout the items.  When an animation is desired, the designer then writes an algorithm that considers the current item's position and then calculates where it needs to go next, probably using the same formula when laying out the items.


The cool part is, the 2nd calculation isn't needed. Let's look at some code:



for (var i : int = 0;i < shoes.length;i++)
{
zTotalDepth -= amountToOffSetZ;
var scale : Number = Math.min((focalLength / (focalLength + zTotalDepth)),1);

var shoe : InteractiveShoe = shoes[i];
shoe.scaleX = shoe.scaleY = scale;
shoe.y = shoeOffsetY;
shoe.x = shoeOffsetX;
shoe.alpha = 0;

addChild(shoe);

shoePositions[i] = {x:shoe.x, y:shoe.y, scaleX:scale, scaleY:scale, alpha:1};

shoeOffsetX -= shoe.width * multiplier;
shoeOffsetY += shoe.height * .08;
}

This is the loop I used to layout the shoes. Pretty standard but, did you notice this line of code:
 shoePositions[i] = {x:shoe.x, y:shoe.y, scaleX:scale, scaleY:scale, alpha:1};

When the items are laid out an array of generic objects is created. Each object has the 5 altered properties assigned to it. The purpose is to save these values to be called at a later time. This array is known as a Lookup Table. When the applet is called upon to shift all the items to new positions, the values are not recalculated; rather, the values are retrieved from the lookup table:
private function shiftShoes(event : TimerEvent) : void
{
for (var i : int = 0; i < shoes.length - 1; i++)
{
TweenLite.to(shoes[i], .25, shoePositions[i + 1]);
}

If you are familiar with Tweenlite, you will know that whenever a tween is created, you dynamically create an object that has your tween values assigned to it:
TweenLite.to(mc, 1, {x:65, y:117});

In the lookup table example, a reference to the object already created ( when the items were first laid out ) is passed Tweenlite. This eliminates the need to create a new object for Tweenlite and eliminates the necessity to do any calculations to determine the properties each item needs to animate to.

Compile Time or Run Time?



This example creates the lookup table at run time, but there is an option to pre-calculate the lookup table and include it at compile time. The most straight forward way to do this is to create a class that has only has one property, an array, and this array would be used to both layout the objects and to animate them. This is something to consider if there are a great many (1,000s) or processor intensive calculations to be made.

Using Lookup Tables in the wild....



While this example might not benefit greatly from such a process, there are circumstances that do benefit from it - using extensive 3D calculations or trig functions such as Sine and Cosine. Something else to consider would be to use Value Objects that have the values typed for faster access and then have these stored in a Vector.