Archive for July, 2009

A$ugar: Syntactic sugar for ActionScript 3

When I first started working in AS3 I absolutely hated it. I had been doing AS2 for a few years and as much as AS2 had it’s flaws, AS3 just bothered me to no end.

First, it seemed like Adobe was moving from the Javascript style language that I loved to more of a .NET / Java language which I hated. Throw on top of it the XML based language that is FLEX and you had me spouting monologues of hatred to anyone that was willing to listen.

So, after a while I finally gave up and started using AS3 (I still use AS2 whenever working in the embedded space, and am now wishing that I had access to the some of the power that AS3 gives you), trying to write utils that made it act more like AS2 whenever I could. After attacking the problem from a few different angles I finally found (or created) an abstraction (actually more like an abbreviation) that I’m happy with and now I am giving it to you, in hopes that a few of you will find it useful.

So here it is, I’ll give you an example and then explain how it works.

Let’s say that I want to draw a square on the stage and then trace “Hello” when the user clicks it.
Normally it would go like this:

import flash.display.Sprite;
var square = new Sprite();
this.addChild(square);
square.beginFill(0xff0000);
square.drawRect(0,0,100,100);
square.endFill();
square.x = 100;
square.y = 100;
import flash.events.MouseEvent;
var squareClicked = function(e)
{
   trace("Hello");
}
square.addEventListener(MouseEvent.MOUSE_DOWN, squareClicked);

Ok, so I created that without running it, so there may be some errors. Anyway, I’ve created a $ object (actually it’s a function that has some other properties on it), that allows me to do the same stuff, but in a much less verbose way.

var square = $.create.Sprite.addTo(this)
.beginFill(0xff0000).drawRect(0,0,100,100).endFill
.x(100).y(100)
._mouseDown(function(e)
            {
                trace("Hello");
            })
.$;

This $ object is defined and sits in the root package so you don’t need to do any importing to utilize it.

As you can see here, I’m ripping off the JQuery $ magic thing. The first step is to either create a new object or to wrap an existing object. In the example above I’m creating a new one, you can call $.create. and then any class that exists in the runtime, it does it’s magic by just looping through all the built-in packages looking for a class by that name, so $.create.Sprite, $.create.TextField, $.create.Timer, any of those work without the need to import the package. The other option is to wrap an existing object just by using $ as a function, so $(stage) or $(someMovieClip) or whatever.

The result of either of those actions is to provide a Proxy object that has a pointer to either your new object or the wrapped object. Then you can begin calling things on it, each time you call something the return is the same proxy object, this is what allows you to do this JQuery-esque chaining.

There are a few different options here:

if it’s a method you can call it like a method

.beginFill(0xff0000)

if it is a method that doesn’t have any args you can call it like

.endFill()

or just plain

.endFill

also, if it takes a single argument, you can call set it (although this isn’t something that winds up being very useful because you can’t keep chaining.

.beginFill = 0xff0000

if it’s a property you can do pretty much all the same things

.x(10)
.x = 10

or you can leave off the value and it just sets to true

.visible
.embedFonts

The other bit of magic that we can do is with events, all events start with an underbar, and the name of the event is the actual value of the constant (usually this is the camel-case version of the constant name, so MOUSE_DOWN becomes mouseDown, ENTER_FRAME becomes enterFrame etc).

._mouseDown(function()
            {
               trace("hello");
            })

or

._mouseDown = function()
              {
                 trace("hello");
              };

the main difference here is that the object can only have 1 event type setup, so if you set _mouseDown on an object it will clear out any previous mouseDown event that was there before (only the ones added through the $ syntax, others are not affected), this is also how we delete them.

._mouseDown(null)

or

._mouseDown

When you are done chaining you call .$ on it and it will return the object that you are pointing to.

In the past when telling people about this they wind up always asking about performance. The answer I usually give is, in my experience the places where I have used this in the past don’t tend to be places that are performance intensive, and therefore the fact that it’s a few times slower than the normal way of doing things isn’t a big deal.

It probably goes without saying that one has to use this utility with responsibility. One could use the chaining mechanism to create unreadable one-liners that are a pain to read. As you can see in my examples I wind up putting things that go together on the same line, this allows me to comment out things with a single comment.

There are a few things that may need a little clearing up. As you probably noticed, with the sprite in the example above, we are calling beginFill and drawRect on the sprite when those are calls that need to be called on sprite.graphics. This is set up behind the scenes with some add-ons, which allows you to route calls to different objects if the property isn’t available on the current one. So in the example of the sprite we tell it to check .graphics if it can’t find it on the sprite object. Another example of this is on the TextField object. The TextField sets up a textFormat works similar to the graphics so one can say:

$.create.TextField.addTo(this)
.font("Arial").size(10).align("center")
.embedFonts.multiline.wordWrap
.text("This is a test").$

Another shortcut setup is for the Loader, the first part is the .load call which will accept a string in addition to a URLRequest. The second part is that it routes certain events to the contentLoaderInfo object so that you can do:

$.create.Loader.addTo(this)
.load("image.jpg")
._complete(function(e,o)
           {
               o.content.smoothing = true;
           });

That is the other change: in addition to the event being passed into the eventListener, you also get the actual object (not the wrapper, but the object being pointed to) passed in as the second parameter, although as long as you are using an anonymous function there is no need to have the arguments so you can do:

._mouseDown(function(e) {} )

or

._mouseDown(function(e,o) {} )

or

._mouseDown(function() {} )

All of these shortcuts are setup in the $Extension.as file, in addition to this the ExtendProto.as file extends the prototypes of a handful of objects providing more functionality that is useful. The only bit you’ve seen so far in the examples is the addTo which just acts just like addChild only backwards (a.addChild(b) is the same as b.addTo(a)).

Here’s the link to a zip of the source, I’ll probably put it out on Google Code, but for now this will have to do. Source

Also, here’s the list of all the other prototype extensions.

InteractiveObject.sendToBack
InteractiveObject.bringToFront
InteractiveObject.position
InteractiveObject.pos                     Same as position
InteractiveObject.remove               remove from parent
InteractiveObject.addTo