24
Mar 10

Running Multiple Flex Builder Workspaces

Frustrated by the fact that you can only run a single Flex Builder workspace at-a-time? Well, I did a bit of digging and found several solutions (related to Eclipse, which is the basis for Flex Builder), best summed up in this Stack Overflow question: Open multiple Eclipse workspaces on the Mac.

I didn’t like popping open a Terminal window; and all of the options resulted in multiple instances of the Flex Builder app running, but they were indistinguishable from each other in the Dock or when task-switching. So, I figured out how to fix that.

First, create a copy of your Flex Builder.app. Fortunately (in this case), the app file references files located elsewhere, so it’s pretty small (a few hundred K), so you’re not duplicating the entire app (as you would be with most Mac apps).

Second, rename your copy to whatever you want to call it—I used the name of the workspace.

Third, right-click on the app and choose to Show Package Contents. In Info.plist, change the Eclipse key (which is an array) to include two additional strings (at the end), the first being “-data” and the second being the full path to your workspace, e.g. “/Users/troygilbert/Documents/Flex Builder Workspaces/Acme Dashboard/”.

Now edit MacOS/FlexBuilder.ini. Change the -Xdock:name option from “Flex Builder” to whatever you want the name to be in the Dock.

Last but not least, replace or edit the icons in Resources/flexbuilder.icns with icons of your choosing. Personally, I like to go to Icon Factory’s fine collection of freeware icons and find something whimsical (and memorable) to use. The Futurama icons are quite nice.

Now you can create a shortcut to your new app anywhere you want, or access it through your favorite app launcher, like Google Quick Search Box.


7
Sep 09

Events vs. Callbacks (revisited)

Just a follow-up on my post profiling events vs. callbacks. As pointed out in the comments, EventDispatcher will ignore successive calls to dispatchEvent() with the same Event object. Thus, you must allocate new objects for each dispatch.

So, just chop off the last few paragraphs of that post and go with the original point of the post: if you don’t need bubbling, you can get a significant speed-up by using callbacks.


3
Sep 09

Events vs. Callbacks

Before there were events, there were callbacks.

I remember finally grasping callbacks… it gave me a perspective on my code where different components could have an asynchronous conversation — you know, just like we do every day with email (and any other form of messaging).

Finally getting events, and in particular AS3′s event model, was a similar revelation. Components could be oblivious to each other, or even how far away they were from each other in the giant graph of your application, and still carryout a conversation of signaling and acknowledging various messages.

Of course, this isn’t new to anyone who learned to code in SmallTalk — which seems to be the language that nailed every good thing about OOP without tripping over any of the bad things. No surprise, it came out XEROX PARC. Damn, did those guys invent all of modern computing like 30 years ago?

Naturally, when we built Mockingbird we used events to notify the view of changes in the model. Very standard MVC stuff, folks have been doing it for decades. And like everyone doing it with AS3, we used the Flash Player’s native EventDispatcher class. In fact, since we were using the Flex Framework, we even used the [Bindable] tag to do all of the work for us.

Well, everything has a catch. And even though EventDispatcher is a native part of the Flash Player and an integral aspect of AS3 coding, it still has overhead that you may not need and can cut out by crafting your own solution.

Up front, let me say that premature optimization is the root of all evil. And by evil, I mean you’ll waste your time on it with no apparent benefit. The following information is really only useful if you’re dispatching tens of thousands of events per second. You’re probably not, or at least not very often. We were, but Mockingbird is a special case.

One thing bugged me about using EventDispatcher: it made a copy of my event every time I called dispatchEvent(). Now, my pesky lizard brain that I honed while working on the PS2 taught me that lots of little memory allocations every frame — in a garbage collected environment — is not the ideal for steady, consistent performance.

If EventDispatcher was a standard class we could hopefully just subclass it and try to plug in a pooled memory allocator or similar. But it’s native, so we can’t do that.

So, our only choice is to create our own event dispatching mechanism.

It’s actually not that hard, particularly if you don’t need features like bubbling or propagation control. We didn’t even need priorities as it was purely notification of change, usually to just one or two listeners. I ended up with a couple of methods for adding and removing functions to an array and a dispatch method that simply iterated through the array calling each function in turn (callbacks!).

And boy, did it work. Zero memory allocations. Even simplified the API compared to IEventDipsatcher. Here’s a quick performance test I built to show the benefits in a theoretical context.

Source | SWF

It runs 100,000 iterations on each test, and each iteration “dispatches” an Event object that has 3 registered listeners. I don’t time registrations, just the dispatch. The listeners are actually do-nothing functions, but since mxmlc doesn’t dead-strip empty functions the test should be representative.

On my machine, comparing dispatchEvent vs. a Function callback is 1275ms vs. 80ms. That means the callbacks are 15.9x faster. I didn’t test the memory usage, but there would be a significant difference there as well, which means eventually you’ll have to pay the garbage collector his timeslice, compounding the overall performance cost for your application.

Damn, I was proud of myself. I just found a 15.9x performance gain in an integral part of our technology that’s been a hotspot on the profiler since forever. So, I went and swapped all of our event dispatch code for callbacks, tweeted several times about my success, and patted myself on the back.

Then I started writing this blog post. And in the process of consolidating my thoughts and cleaning up my source code I had an idea.

What if EventDispatcher didn’t do any memory allocations? How much faster could it run?

You’re probably asking yourself, didn’t we already go over this a few paragraphs up? You can’t hook EventDispatcher‘s allocator. Or can we?

How does EventDispatcher allocate new instances of your custom events? It calls the clone() method, which you’re supposed to override if you need those clones to be typed as your custom event. The default clone() returns a new Event instance. How about a clone() that didn’t allocate any memory? What if we returned this?

Source | SWF

This performance test compares dispatchEvent with custom events that don’t allocate memory when cloned vs. Function callbacks. You can view it here.

Results? 7 ms vs. 83 ms That’s right, when you take the new out of dispatchEvent() it becomes 182x faster… 11.8x faster than a callback!

How fast is it? Well, the theoretical maximum speed for calling functions can be determined by inline the callbacks (instead of using an array of Functions, which have closure overhead). This runs around 2ms.

What does this mean? Well, it means that I need to go back and revert some of my commits from this afternoon. It also means you should check out your profiler and see if event dispatch is a hotspot for you, and if it is, try subbing in custom events that don’t allocate.

What do we lose by not allocating? No idea. I’ve not done that testing yet. Some suggested (based on the docs, I believe), that there’s something with EventDispatcher not being able to change the target property after an event has been dispatched, so it has to create a clone if you re-dispatch the event, but that just doesn’t make sense — I can’t think of an implementation that would require that or benefit from it. It could affect bubbling, not sure how though.

In the end, it may just be a safety measure to ensure that events can’t tamper with the original event before other events get a chance to consume it. Kinda like cloning a private array before returning it through a public method (for example, in collection classes). I’d be curious to know.

NOTE: My timings were done in the standalone Flash Player. Testing out this post I’m noticing that the numbers aren’t quite the same in the browser (not surprising).


26
Aug 09

Pixel-Perfect Collision Detection Revisited

I've finally gotten around to updating my pixel-perfect collision detection post (which is by far the most popular post on this blog with 48+ comments and a double-digit percentage of all my pageviews). Of course, now that I've gotten around to updating it there's not much point, as others have improved upon and trumped what I had. So, I'll just provide a quick recap of their pursuits (and my version of them).

First, The Actionscript Man grabbed my version, Grant Skinner's (which mine was indirectly based on), digested them, then spit out a new one that eliminated some draw calls. He even made a nice little app that lets you toggle between the various versions and compare them. The only drawback of his version is that it assumes the two objects have the same parent.

Next, the venerable Tink posted his version that uses the same technique as The Actionscript Man but factors a more complicated parent-child relationship. Tink's still assumes that both objects are on the stage. His code also gets bitten by a bug I've run into related to localToGlobal and nested SWFs (though I still haven't explored it enough to write about it yet), as discussed in the comments. Tink added an "accuracy" parameter that basically scales the bitmap used for testing up and down. I've not found this makes enough of a performance difference to warrant the dramatic differences it can make in a pixel-perfect test.

So, I updated my version to include the rendering method Tink and The Actionscript Man used, and included the "common parent" math my original one had. The upside of my version is that it doesn't care whether your objects are on the stage or not, just that they share a common parent.

CODE:
  1. /** Get the collision rectangle between two display objects. **/
  2. public static function getCollisionRect(target1:DisplayObject, target2:DisplayObject, commonParent:DisplayObjectContainer, pixelPrecise:Boolean = false, tolerance:int = 255):Rectangle
  3. {
  4.     // get bounding boxes in common parent's coordinate space
  5.     var rect1:Rectangle = target1.getBounds(commonParent);
  6.     var rect2:Rectangle = target2.getBounds(commonParent);
  7.    
  8.     // find the intersection of the two bounding boxes
  9.     var intersectionRect:Rectangle = rect1.intersection(rect2);
  10.    
  11.     // if not pixel-precise, we're done
  12.     if (!pixelPrecise) return intersectionRect;
  13.    
  14.     // size of rect needs to be integer size for bitmap data
  15.     intersectionRect.x = Math.floor(intersectionRect.x);
  16.     intersectionRect.y = Math.floor(intersectionRect.y);
  17.     intersectionRect.width = Math.ceil(intersectionRect.width);
  18.     intersectionRect.height = Math.ceil(intersectionRect.height);
  19.    
  20.     // if the rect is empty, we're done
  21.     if (intersectionRect.isEmpty()) return intersectionRect;
  22.    
  23.     // calculate the transform for the display object relative to the common parent
  24.     var parentXformInvert:Matrix = commonParent.transform.concatenatedMatrix.clone();
  25.     parentXformInvert.invert();
  26.     var target1Xform:Matrix = target1.transform.concatenatedMatrix.clone();
  27.     target1Xform.concat(parentXformInvert);
  28.     var target2Xform:Matrix = target2.transform.concatenatedMatrix.clone();
  29.     target2Xform.concat(parentXformInvert);
  30.    
  31.     // translate the target into the rect's space
  32.     target1Xform.translate(-intersectionRect.x, -intersectionRect.y);
  33.     target2Xform.translate(-intersectionRect.x, -intersectionRect.y);
  34.    
  35.     // combine the display objects
  36.     var bd:BitmapData = new BitmapData(intersectionRect.width, intersectionRect.height, false);
  37.     bd.draw(target1, target1Xform, new ColorTransform(1, 1, 1, 1, 255, -255, -255, tolerance), BlendMode.NORMAL);
  38.     bd.draw(target2, target2Xform, new ColorTransform(1, 1, 1, 1, 255, 255, 255, tolerance), BlendMode.DIFFERENCE);
  39.    
  40.     // find overlap
  41.     var overlapRect:Rectangle = bd.getColorBoundsRect(0xffffffff, 0xff00ffff);
  42.     overlapRect.offset(intersectionRect.x, intersectionRect.y);
  43.    
  44.     return overlapRect;
  45. }

Finally, if you really want some sophisticated, fully-featured, pixel-perfect collision detection *and* handling, check out Corey O'Neil's Collision Detection Kit. I haven't used it yet, but it looks pretty comprehensive with good performance.


26
Aug 09

Quick fix for scale-9 issues

I discovered last week that scale-9 grids only apply to a DisplayObject and its content, but not its children. At first, I thought, "what!? why not!? that's dumb!" But, after a bit a reflection I realized that it would be *very* tricky to cascade a scale-9 transformation down a display list. I'm not going to attempt to explain why, but just imagine the scenarios where child DisplayObjects straddle the scale-9 grid and you'll realize it quickly moves into "very hard" (and likely slow) territory.

For my scenario, though, the child DisplayObjects simply provided some runtime configurability that once set could be "baked in." This allows you to take the shortcut of flattening the display list to a single bitmap, then applying a scale-9 grid to that. Unfortunately, the Flash Player doesn't allow you to set a scale-9 grid at runtime, probably due to some pre-calculations it does or special structuring of the data.

Searching for answers along these lines I ran across this fantastic post from Ahmed Nuaman:

Scale Any DisplayObject with My ‘ScaleObject’ Class

His theory was dead-on: take a DisplayObject, render it to a bitmap (to flatten it), cut that bitmap into 9 pieces, then override your new class's width/height properties to apply the scale-9 math to the individual pieces.

What I didn't really like was the roundabout way he solved the problem. First of all, the code was far too verbose for my tastes. It also wasn't self-contained, relying on an external bitmap utility function. But most problematic was the use of shapes with bitmap fills: I may be wrong, but I don't see any advantage to this, and it has the distinct disadvantage of being more complex that straight bitmaps and likely less efficient internally in the Flash Player. So, I fixed all that.

CODE:
  1. class ScaleObject extends Sprite
  2. {
  3.     /** Scale9grid pieces. **/
  4.     private var _tl:Bitmap;
  5.     private var _tc:Bitmap;
  6.     private var _tr:Bitmap;
  7.     private var _ml:Bitmap;
  8.     private var _mc:Bitmap;
  9.     private var _mr:Bitmap;
  10.     private var _bl:Bitmap;
  11.     private var _bc:Bitmap;
  12.     private var _br:Bitmap;
  13.    
  14.     /** Constructor. **/
  15.     public function ScaleObject(original:DisplayObject, scaleGrid:Rectangle)
  16.     {
  17.         _tl = slice(original, 0, 0, scaleGrid.left, scaleGrid.top);
  18.         _tc = slice(original, scaleGrid.left, 0, scaleGrid.width, scaleGrid.top);
  19.         _tr = slice(original, scaleGrid.right, 0, original.width - scaleGrid.right, scaleGrid.top);
  20.        
  21.         _ml = slice(original, 0, scaleGrid.top, scaleGrid.left, scaleGrid.height);
  22.         _mc = slice(original, scaleGrid.left, scaleGrid.top, scaleGrid.width, scaleGrid.height);
  23.         _mr = slice(original, scaleGrid.right, scaleGrid.top, original.width - scaleGrid.right, scaleGrid.height);
  24.        
  25.         _bl = slice(original, 0, scaleGrid.bottom, scaleGrid.left, original.height - scaleGrid.bottom);
  26.         _bc = slice(original, scaleGrid.left, scaleGrid.bottom, scaleGrid.width, original.height - scaleGrid.bottom);
  27.         _br = slice(original, scaleGrid.right, scaleGrid.bottom, original.width - scaleGrid.right, original.height - scaleGrid.bottom);
  28.     }
  29.    
  30.     /** Create a slice. **/
  31.     private function slice(original:DisplayObject, x:Number, y:Number, width:Number, height:Number):Bitmap
  32.     {
  33.         // render a slice of the original
  34.         var bd:BitmapData = new BitmapData(width, height, true, 0);
  35.         bd.draw(original, new Matrix(1, 0, 0, 1, -x, -y));
  36.        
  37.         // create and position the bitmap
  38.         var bitmap:Bitmap = new Bitmap(bd, PixelSnapping.AUTO, true);
  39.         bitmap.x = x;
  40.         bitmap.y = y;
  41.         addChild(bitmap);
  42.        
  43.         return bitmap;
  44.     }
  45.    
  46.     /** Width. **/
  47.     override public function set width(value:Number):void
  48.     {
  49.         _tc.width = _mc.width = _bc.width = value - _tl.width - _tr.width;
  50.         _tr.x = _mr.x = _br.x = value - _tr.width;
  51.     }
  52.    
  53.     /** Height. **/
  54.     override public function set height(value:Number):void
  55.     {
  56.         _ml.height = _mc.height = _mr.height = value - _tl.height - _bl.height;
  57.         _bl.y = _bc.y = _br.y = value - _bl.height;
  58.     }
  59. }

NOTE: The width and height properties specifically don't call the super setters. Doing so causes Sprite to attempt and scale its contents by adjusting scaleX and scaleY.


29
May 09

TAG Presentation

I presented early this morning (7am!) at TAG Austin. Nice crowd, very interested, very receptive. I spoke for about 45 minutes, slides presented below.

The presentation was broadly about Mockingbird, but Mockingbird-specific talk really just occupied the first and last five minutes, everything in between was a discussion of what games are, what their history and place is in human culture, what their potential is as a form of expression, and what elements of that expression are critical in order to make "making" as accessible as possible.

As with previous presentations, the slides by themselves may not be that useful, but for those that heard the presentation the slides should serve as useful reminders.

SWF Export from Keynote
PDF Export from Keynote


22
May 09

Loading Flex-based SWFs in AS3-only SWFs

One would think that Adobe would make the integration of Flex-based SWFs and AS3-only SWFs (including those produced by the Flash authoring tool) seamless. Everything works spectacularly if you want to load AS3-only SWFs (or what some folks refer to as Flash SWFs) inside of Flex-based SWFs. But the other way around? Good luck.

In fact, some comments in the Adobe bug database suggest that loading Flex-based SWFs inside of non-Flex-based SWFs is officially unsupported. If so, I'd suggest they change that policy promptly as most of the world still uses non-Flex codebases.

What's the problem? It all revolves around the SystemManager and how it bootstraps Flex-based SWFs. I'll not go into the details, but the problem is that if you don't do things "just so" the SystemManger for your SWF gets confused and doesn't act like the top-level SystemManager that it is.

The fix is easy... down right trivial -- once you know it. It's not in any of the current docs, and Google only leads to a lot of solutions that work if you're not dependent on the SystemManager doing much (such as handling custom cursors or doing complex layout). After lots of stepping through the SystemManager source under different load scenarios I ran across a couple of events (whose names are hard-coded strings in the source) whose purpose was never explained in comments or docs. A bit of Googling revealed this handy doc from the Flex team: Developing and loading sub-applications [PDF]. I couldn't find it's original context nor could I find it navigating the official help docs, so I'm not sure what it's actually a "chapter 1." of (my suspicion is that "chapter 1." is a by-product of their documentation templates).

In the last few pages of that doc they give an example that loads a Flex SWF in a pure AS3 app. Their example is slightly more complicated in that they are trying to load a set of common RPC components to be shared between two potentially different codebases. Our needs are much simpler. Here's all you need to do:

Actionscript:
  1. var loader:Loader = new Loader();
  2. addChild(loader);
  3. loader.load(new URLRequest("my-flex-app.swf"));
  4. loader.addEventListener("mx.managers.SystemManager.isBootstrapRoot", systemManagerHandler);
  5. loader.addEventListener("mx.managers.SystemManager.isStageRoot", systemManagerHandler);
  6.  
  7. function systemManagerHandler(event:Event):void { event.preventDefault(); }

We load the Flex SWF with a Loader, just like normal. We then add two event listeners on our loader (these could probably be on the containing SWF as well as they bubble up). These two events are dispatched by the SystemManager during its initialization phase. If it dispatches these events and nothing happens it makes bad assumptions and gets confused. We need to acknowledge these events. We do this by canceling them (calling event.preventDefault()).

The end result is that your Flex SWF will act completely self-contained. The SystemManager will assume it's the only SystemManager and it will manager everything as if it's the root of the Flex app (which it is).

I've not tested loading multiple Flex SWFs side-by-side with this technique. Due to some singletons (bad, bad singletons!) in the Flex Framework, I'm not sure if this would solve all of your problems as it may not properly coordinate multiple SystemManagers. One solution would be to further wrap the Flex SWF in its own application domain so that there was no cross-pollination of Flex Framework classes.


25
Feb 09

Picnik: Damn Good Flex

I'm pretty sure I've mentioned Picnik before. I got a reminder this evening about updating my expired credit card (for my annual premium). So, I visited the site. I hadn't been in probably six months.

I know they use Flex to build the site (which is what we use for Mockingbird). I haven't read anything about what specifically they do (though, I'm about to go googling that after I post this). Whatever they're doing, it's silky smooth.

For all those folks that say "why do we need Flash on the web?" send them to Picnik. They'll tell you that you can do most of that stuff in JavaScript (not anywhere as smoothly with most web browsers). They'll argue that an app like that should be native -- except that if it was the development cost would have been significantly higher and it would be functionally less useful for users who use multiple machines. The point is that Picnik was made in Flex, by incredibly talented designers and developers, and it was not made in those other environments. There are clearly advantages to the Flash platform that don't exist elsewhere, and those advantages are critical for the right application. Best tool for the job.

I know we made the right decision with Mockingbird (to have it built on Flash). I may be tempted at times by the extra horsepower others may offer -- but in the end, the Flash development environment and distribution platform is a warm, comfortable embrace.


8
Feb 09

The Unfinished Swan

The Unfinished Swan is a beautiful concept for a game. And, judging by the video provided, is being beautifully implemented. Within seconds of seeing it my whole perspective on first-person mechanics was flipped on its head.


19
Jan 09

Building Gaming’s Future

This morning I read Edge's article on building gaming's future. It's a decent summary of the growing audience/market for consumer game making. Of course, we've been talking about this at Mockingbird for a few years now, but Edge really hits it home with all of their examples that it's truly becoming a mainstream activity (for gamers).

Needless to say, I'm bummed that we didn't even warrant a mention, even though we were one of the first in the present push to get out there and do this. I'm cool with that because I know all of the other folks mentioned in the article (XNA, Whirled, LittleBigPlanet, Playcrafter) know who we are. ;-)