Archive

Archive for the ‘Flash Player 10’ Category

Grape Animation Library

September 2nd, 2009

logo

http://code.google.com/p/grape-as3/

Grape is the animation library I introduced last post. Its goal is to offer a modular means to animate along parametrized paths.

Here’s the updated demo:

Get Adobe Flash player

Architecture

Animations have 2 essential component parts: a path and a curve. Paths define position and curves define progression. Both paths and curves are stateless, meaning state is managed by the Animation type and the engine, Grape. This also means paths and curves are freely swappable without a need to clone, etc.

Grape’s Animation type by itself doesn’t assign values to your objects as you’re probably accustomed to with most popular tweening engines. Instead, it simply manages the state of the animation. Its state is a Vector of type Number. This allows the user to reference this state without necessarily tying it to a DisplayObject for instance. When you do want to bind the state of the animation to a DisplayObject, you’ll use a Binding, which simply applies the animation’s state to the appropriate object. The average user probably won’t ever touch a Binding directly, but it’s worth noting.

API

The engine is a singleton called Grape. It has creation methods for the 2 most likely animation scenarios, create2DAnimation and createPropertyAnimation. The latter should be fairly identical to how popular engines work. create2DAnimation will ideally be the typical use-case.

//create the path to animate along
var spiral:Path2D = new SpiralPath2D( new Point( 100, 100 ), 200, 0 );
 
//view is your DisplayObject and 2000 is the duration in milliseconds.
Grape.getInstance.create2DAnimation( view, spiral, 2000 );

And that’s it, your DisplayObject will tween in a spiral with original radius of 200 and end radius of 0 for 2 seconds. You can also set the start time (defaults to instantaneous start), the curve (defaults to linear), whether the animation loops (defaults to false), if the animation is to be played in reverse (defaults to false), and if the animation should reverse on loop (false).

The method returns the animation instance, in the case that you need a reference or would like to handle some of its events:

var anim:Animation = Grape.getInstance.create2DAnimation( view, spiral, 2000 );
 
//add listener for animation complete event
anim.addEventListener( Event.COMPLETE, onAnimationComplete );
 
function onAnimationComplete( event:Event ) : void
{
   //animation done!
}

You can also listen for an event everytime a looping animation loops and each tick of the engine:

//add listener for animation loop event
anim.addEventListener( Animation.LOOP, onAnimationLoop );
 
//listen for Grape's tick
Grape.getInstance().addEventListener( Grape.TICK, onGrapeTick );

Paths

LinearPath2D, CirclePath2D, SpiralPath2D, SineWavePath2D

These are all pretty self explanatory. Linear moves along a line segment. Circle moves around a circle’s edge; you can set the beginning and end radian. Spiral is the same as Circle but also interpolates radius. SineWavePath2D extends LinearPath2D, and so the wave deviates from the line segment.

ComplexPath2D

ComplexPath2D allows you to tween across multiple paths. You supply it a Vector of paths and a Vector of starting cues, specifically the percentage through the tween at which you want the corresponding path to begin.

//create the paths that will be added to the complex path
var spiral:Path2D = new SpiralPath2D( new Point( 100, 100 ), 200, 0 );
var linear1:LinearPath2D = new LinearPath2D( new Point( 100, 100 ), new Point( 200, 200 ) );
var linear2:LinearPath2D = new LinearPath2D( new Point( 200, 200 ), new Point( 200, 100 ) );
var linear3:LinearPath2D = new LinearPath2D( new Point( 200, 100 ), new Point( 100, 100 ) );
 
var paths:Vector.<Path> = Vector.<Path>( [ spiral, linear1, linear2, linear3  ] );
 
//spiral and linear1 will take 25% of the animation's duration, linear2 will take 10%
//and linear3 will take 40%
var cues:Vector.<Number> = Vector.<Number>( [ 0, 0.25, 0.5, 0.6 ] );
 
//create the complex path
var complex:ComplexPath2D = new ComplexPath2D( paths, cues );

Tweening along a twisty train track for example is something that just wouldn’t be possible with any other engine, and forget about trying to apply one cohesive interpolation across the whole path. Complex paths make these stupid easy. And because complex paths are paths, you can nest them inside of each other to create quickly compound, complex animations!

QuadraticBezierPath2D, CubicBezierPath2D

Quadratic Bezier curves are what you’re familiar with from Graphics. Cubic Beziers just offer one extra control point.

var bez:CubicBezierPath2D = new CubicBezierPath2D( new Point( 20, 20 ), new Point( 400, 200 ), new Point( 400, 600 ), new Point( 600, 600 ) );

Bezier’s take one other argument, the Boolean reparametrize. Beziers are like stapling saltwater taffy to the table and lifting it at the center. A really small bit of the curve in terms of its parametrization is left at the staples, while a lot is in your hand near the control points, even though the opposite may be true spatially. What does this mean? It means that even with a linear curve, it’s not likely you can keep constant velocity along the curve. So this Boolean flags whether to reparametrize the curve by arc length. This is a somewhat expensive process and so it defaults to false. Here’s an example of this, the black tween has been reparametrized, the red has not.

Get Adobe Flash player

LinearSplinePath2D, CosineSplinePath2D, HermiteSplinePath2D, CatmullRomSplinePath2D

Each of the splines takes a Number Vector (x,y pairs) and interpolates between them in their own special way. Linear connects each point linearly, Cosine steps over part of a wavelength to give a (OK, not too nice) curve through each point. Hermite also takes a vector of tangents to give the most amount of control. You can read about them on wikipedia. Catmull-Rom Splines are a subset of Hermite curves. They offer less control, but do not require the user to specify the tangents. Here’s an image of the different interpolations. Top to bottom it’s Linear, Cosine and Catmull-Rom:

splines

This is the code that created the image above:

var p1:Vector.<Number> = Vector.<Number>( [ 20, 20, 100, 100, 120, 50, 300, 300, 400, 50, 500, 200 ] );
var p2:Vector.<Number> = Vector.<Number>( [ 20, 120, 100, 200, 120, 150, 300, 400, 400, 150, 500, 300 ] );
var p3:Vector.<Number> = Vector.<Number>( [ 20, 220, 100, 300, 120, 250, 300, 500, 400, 250, 500, 400 ] );
 
var path:Path2D = new LinearSplinePath2D( p1 );
path.render( graphics );
path = new CosineSplinePath2D( p2 );
path.render( graphics );
path = new CatmullRomSplinePath2D( p3 );
path.render( graphics );

These have great applicability to dynamically generated paths and lots of other game-related functions.

PhysicsPath2D

This should maybe be called projectile instead of physics. You supply initial position, starting velocity and acceleration vectors and how many seconds the entire tween represents.

Debug Rendering

Each path is renderable as a helpful measure. Because of the way paths work, it was easy to handle all paths in one render method, keeping file size down. You can see the spline API above, just pass a graphics instance to draw into.

Alright, I’m excited to see what you make of this and what sorts of cool things you can make! It’s hosted on Google Code under the MIT license. I’ll try to get it commented soon enough, send me any bugs or feature requests.

http://code.google.com/p/grape-as3/

drew ActionScript 3.0, Animation, Flash Player 10

AVM2 Memory Considerations and Bit Vectors

May 14th, 2009

Arrays are memory hogs when compared to their strong-typed, faster, rich and handsome cousins, Vectors, for a whole bunch of reasons that you can read about elsewhere. I was surprised, though, to find how enormous the difference in their memory footprints were:

  • (10000000 / 32) uints in a Vector (10000000 bits) = ~7k
  • (10000000 / 32) uints in an Array (10000000 bits) = ~1228k

Yikes! But Booleans are far worse:

  • 10000000 Booleans in a Vector = ~8k
  • 10000000 Booleans in an Array = ~39070k

I didn’t do the math, but I’m fairly sure that’s like 3 terabytes per Boolean. Now, it makes sense that Booleans don’t get packed anywhere close to perfectly… does anyone know how much memory gets opened for a Boolean? Probably 4 bytes, but I don’t know, to be honest.

What’s interesting, however, is that it appears, based on ratio, that Booleans are being intelligently stored by the VM in the case of Vectors. Not as tightly as possible, and hence the probably masturbatory BitVector.

Michael, over at Polygonal Labs, posted a nice and as-usual informative article on bit vectors back when people still knew what AS2 was. There’s also an implementation in his data structures library. We can definitely get much better performance, though, using a Vector.<uint>. Here’s my implementation. The bitwise OR and AND are much faster than Boolean logic of the same case and obviously setting all bits on or off will be faster than iterating over a Boolean Vector. If anyone thinks they’d actually use it, I’d be happy to add all the bitwise operations.

So what would this be used for? Firstly, it naturally extends the ability to store Boolean logic via bitwise operations in an unsigned integer’s bits past its 32 threshold all the way… to n!

I used it recently in mapping key presses to emulate AS2’s Key.isDown convenience method. When a key is pressed, the bit sitting at that key’s code is switched on. When the key is released, the corresponding bit is switched off. Combinations could easily be stored as BitVector masks. Here’s the Key class.

Christer Ericson, in his awesome Real Time Collision Detection suggests using a bit vector in avoiding retesting 2 objects for collision detection. He shows that this can be stored in n(n - 1)/2 bits instead of n², where n is the number of objects. The unique index for any 2 objects is then:

bit = min(2n - min - 3)/2 + max - 1

Where min and max represent the indices of the objects being tested.

Using my BitVector class, the implementation would look something like this:

//offline
var n:uint = objects.length;
var bv:BitVector = new BitVector( null, n * ( n - 1 ) / 2 );
 
//test
function handleCollision( minObj:CollidableObject, maxObj:CollidableObject ) : void
{
 
   var bit:uint = minObj.index * ( 2 * n - minObj.index - 3 ) / 2 + maxObj.index - 1;
   if( !bv.getBit( bit ) )
   {
       //test for collision
      bv.setBit( bit, true );
   }
 
}

BitVector.as
Key.as

drew ActionScript 3.0, Flash Player 10

SLRBS demo

February 11th, 2009

demo image

Here’s a demo of the auto-gen physics engine (SLRBS- Super Lightweight Rigid Body Simulator) I’ve been working on which allows you to select images to use as rigid bodies. First, the image is analyzed, ignoring transparent pixels, to determine its convex hull. Then mass/center of mass/inertia tensor are solved for. Finally, the rigid body is created and dropped into the world.

I’m sure that in playing with it, you’ll notice all sorts of wonky behavior… but I think it’s fun anyway. Let me know if there’s a class or type of shape/image that tends to be particularly misbehaved!

demo
image pack

drew ActionScript 3.0, Flash Player 10, Geometry, Mathematics, Physics

SLRBS (Super Lightweight Rigid Body Simulator)

February 1st, 2009

For the past few months I’ve been working on a general purpose geometry library for representing your interactive elements in a more precise geometrical way. There are some pretty cool features like collision detection management, auto-generation of geometry, and an easy way to bind the model to your DisplayObject hierarchy (or not!). 

It will be released under the MIT license alongside SLRBS- a physics engine that uses the library. The absolute last thing the Flash world needs is another physics engine, but this has some really nice features for beginner developers, prototypers etc. And it does a great job of showing how to use the geometry library.

One of the most frequent questions or requests I get/got with FOAM was something along the lines of “how do I make my MovieClip a rigid body?” It can be hard from my position to know how to tell someone who probably uses the Flash authoring tool a proper way to handle the difference between the model and view. So with SLRBS, I set out to address this issue.

With SLRBS, you have a typical API to create physics simulation, but it also has some streamlined auto-creation methods that make setting it up a breeze. To ensure this, I even downloaded a copy of Flash CS4 to play with it!!! AAAAHH!!! wtf happened to Flash!? 

So, all you do is lay out your MovieClips on the stage, and call a single static method and voila! physics simulation! There’s some pretty cool stuff going on that determine how to handle overlapping DisplayObjects (each demo does it differently), and it’s just a single parameter.

Here’s a screenshot of the stage in a demo where a car is being created:

Car Demo

Because 2 of the tires overlap the car, they get pinned to the car at their center of mass.

 

This is the only ActionScript in the .fla:

1
2
3
4
5
6
7
8
9
import org.generalrelativity.slrbs.SLRBSimulator;
 
var simulator:SLRBSimulator = SLRBSimulator.createSLRBSimulatorFromContainer( this );
addEventListener( Event.ENTER_FRAME, onEnterFrame );
 
function onEnterFrame( event:Event ) : void
{
   simulator.step( 1 / 60 );
}

 

Here’s a second demo’s stage in the Flash authoring tool- this time a ragdoll. The overlap this time gets pinned at the center of overlap instead of the center of mass as in the car demo- this works well for creating ragdoll joints.

Ragdoll Demo

drew ActionScript 3.0, Flash Player 10, Mathematics, Physics

Green Threads

November 27th, 2008

This is a small framework I wrote that addresses some of the issues associated with ActionScript being executed linearly in one thread. If the language offered an API for threading, computationally expensive routines could be run in a separate thread, keeping the application’s UI appropriately responsive.

The idea is to offer a simple means to decompose:

  • deep recursion
  • large loops
  • computationally expensive tasks

I’ve created 2 demos (FP10) to illustrate the issue.

The first demo is an implementation of the A* algorithm. If you select to execute the path-finding normally, you’ll notice the Flex UI becomes unresponsive; if you’re lucky, you might even get the spinning beach ball of death. But when you run the algorithm in a green thread, the application UI remains responsive. Select start and end nodes at diagonally opposite corners to force the algorithm through a lot of work.

The second demo encodes 3 images. This is a little harder to visualize in a demo, so here’s what’s happening: I take an embedded image, encode it as a .png 3 times and then decode and render the ByteArray to show the encoding’s success. As with the path-finding demo, you’ll see the UI become unresponsive when you run the encoding normally. But decomposed to run over a few seconds, everything runs well.

A thread’s chief responsibility, aside from running its processes, is to maintain frame rate. The API offers the user the ability to set execution frequency and processing share (percentage). It polices itself according to these allocation constraints. If a process exceeds its allotment, it is penalized in the next cycle. This works surprisingly well to maintain frame rate and adhere to the user’s request.

The source is pretty well commented and should be easy to implement! And if nothing else, there’s an object-oriented implementation of the A* algorithm… (based on Ian Millington’s pseudo code in his Artificial Intelligence For Games).

path-finding demo

image encoding demo

source

I started this framework because of (and got the name from) my awesome friend, Roger Braunstein, who shared his elegant and simple solution with me a couple months ago, and referenced the obscure deprecated Java API.

drew ActionScript 3.0, Flash Player 10

Z-sorting 3D DisplayObjects for Flash Player 10

October 13th, 2008

The next release of the Flash Player has support for 2.5D- which boils down to 3D properties, appropriate perspective, but no depth sorting. This method takes a DisplayObjectContainer, transforms its children into world space and sorts them along that z-axis. It’s sloppy because it’s DisplayObject-based and so there will be noticeable “pop” when a DisplayObject is sorted onto the top of the display list in close proximity to another. That said it should be perfect for carousels and other views that aren’t super tight.

Here’s a demo

And here’s the algorithm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public static function simpleZSort3DChildren( doc:DisplayObjectContainer, recurse:Boolean = true ) : void
{
 
    //transforms from local to world oordinate frame
    var transform:Matrix3D = doc.transform.getRelativeMatrix3D( doc.stage );
 
    var numChildren:int = doc.numChildren;
 
    //v = ( n * 3 )- (x,y,z) set for each child
    var vLength:int = numChildren * 3;
 
    var vLocal:Vector.<Number> = new Vector.<Number>( vLength, true );
    var vWorld:Vector.<Number> = new Vector.<Number>( vLength, true );
 
    //insertion point for child’s coordinates into state vector
    var vIndex:int = 0;
 
    for( var i:int = 0; i <numChildren; i++ )
    {
 
        var child:DisplayObject = doc.getChildAt( i );
        if( recurse && child is DisplayObjectContainer ) simpleZSort3DChildren( DisplayObjectContainer( child ), true );
 
        vLocal[ vIndex ] = child.x;
        vLocal[ vIndex + 1 ] = child.y;
        vLocal[ vIndex + 2 ] = child.z;
 
        vIndex += 3;
 
    }
 
    //populates vWorld with children coordinates in world space
    transform.transformVectors( vLocal, vWorld );
 
 
 
    //bubble sorts children along world z-axis
    for( i = numChildren - 1; i > 0; i-- )
    {
 
        var hasSwapped:Boolean = false;
 
        vIndex = 2;
 
        for( var j:int = 0; j < i; j++ )
        {
 
            //z value at that index for each child
            var z1:Number = vWorld[ vIndex ];
 
            vIndex += 3;
 
            var z2:Number = vWorld[ vIndex ];
 
            if( z2> z1 )
            {
 
                //swap
                doc.swapChildrenAt( j, j + 1 );
 
                //swap z values (don’t need to change x and y because they’re not used anymore)
                vWorld[ vIndex - 3 ] = z2;
                vWorld[ vIndex ] = z1;
 
                //mark as swapped
                hasSwapped = true;
 
            }
 
        }
 
        //if there was no swap, we don’t need to iterate again
        if( !hasSwapped ) return;
 
    }
 
 
}

drew ActionScript 3.0, Flash Player 10, Mathematics