Pew Pew Chronicles: From Flash To Flex

This is part three of a series of blog posts about a crazy journey that eventually led to Pew Pew becoming one of the first apps in Microsoft’s App Store. On a long flight from Seattle to Omaha in December 2011 I learned about Metro and got immediately hooked.  After installing Windows 8 Developer Preview and playing with Metro I was surprised to see a simple cross-compiled app running in Metro without any problems. Would it be possible to get even more complicated apps like Pew Pew up and running in Metro?

 

Digging out an old prototype

It only took me a few minutes to find that old prototype I wrote in February 2011 to convince skeptics within Adobe that you can  cross-compile a game from ActionScript to JavaScript. There it was: Pew Pew, a cute space shooter game originally written by a Mike Chambers, a colleague of mine. The version that Mike open sourced  Pew Pew in 2011 at github was pretty much the same I had used for my prototype. But I had to do a few important adjustments back then before I could cross-compile Pew Pew to JavaScript.

 

Converting from Flash to Flex

In Pew Pew’s github depot you will notice a file called Main.fla, which is a Flash IDE project file. If you open Main.fla with the Flash IDE you might be surprised to see that there is really nothing going on in this project. There is only one key frame, which kicks off the game through adding an instance of Main to the stage. Somewhere in Main.fla there is also one transition for animating explosions. Most of the logic is in a cluster of ActionScript files that contains code which gets attached to Symbols defined in Main.fla. (For more details about Pew Pew’s project I recommend reading Ian Lobb’s code review of PewPew.)

Later I learned that many games are written that way: one frame, a few transitions, a bunch symbols, and the rest is all ActionScript code. I can see why many developers prefer this kind of Flash Pro setup, because using Symbols in combination with ActionScript code is very convenient. You would have to do a lot more extra work in order get the same game running in Flex. Herein lies the problem, though. From a Flex developer’s point of view ActionScript code from Flash projects often appears incoherent and messy. The part that Flash Pro is adding to SWFs may seem convenient. But to me (and I suspect many other developers coming from the Flex world would agree) all that “magic” that Flash adds is just opaque. In many cases ActionScript code recovered from Flash project doesn’t even compile.

Back in January 2011 it quickly became clear to me that before I could cross-compile Pew Pew from ActionScript to JavaScript I had to convert the original Pew Pew Flash project into a coherent Flex project. In other words, first I needed to be able to create a Pew Pew SWF from a Flex project. You simply can’t compile incoherent code. Your compiler will (and should) throw compile errors if it detects inconsistencies. I realized, my job was adding that missing “Flash Magic”, that hidden extra code that game developers are fond of not having to worry about.

 

Implementing Flash Magic

You might think that you can easily convert a Flash project to a Flex project just by importing those ActionScript files and compiling everything. That’s unfortunately not the case. There are a few weird things I call “Flash Magic”, which Flash Pro adds to the SWF in order to make everything work. The two most important oddities every Flex developer needs to know about Flash projects are automatic instantiation of symbol members, and automatic promotions of local variables to members. Both concepts seem utterly counterintuitive and unnecessary to me. But that doesn’t matter. It’s part of reality and one has to deal with it. Fortunately automatic promotions of local variables to members didn’t seem to be a problem in Pew Pew. So let’s focus on automatic instantiation of symbol members and have a quick look at Main.as :

public class Main extends MovieClip
{
  ...
  //instantiated within FLA
  private var gameArea:GameArea;

  //main game menu view
  private var gameMenu:GameMenu;
  ...
  //background
  public var background:MovieClip;
  ...
  //main entry point for applications
  public function Main()
  {
    //note stage quality is always set to best in Adobe AIR. Have it
    //set to HIGH here in case we are running in browser
    stage.quality = StageQuality.HIGH;
    stage.align = StageAlign.TOP_LEFT;
    stage.scaleMode = StageScaleMode.NO_SCALE;

    //listen for when we are added to the stage
    addEventListener(Event.ADDED_TO_STAGE, onStageAdded, false, 0, true);

    //cache bitmap so it can be accelerated
    background.cacheAsBitmap = true;

    //dont need mouse events on background, so disable for performance
    background.mouseEnabled = false;
    background.mouseChildren = false;

    stop();
  }
  ...
}

 

You will probably agree with me that the code above as it has been checked into github looks very suspicious. Our Main class declares a background member of type MovieClip. Then Main’s constructor code uses background before it has been created. Where is the code that does “new MovieClip()” ? You won’t find it, because it is part of the “Flash Magic”. If you open Windows/Library in the Flash IDE you will find an entry for Background. As far as I know Flash automatically instantiates class members if they are listed in the Library.

 

Adding a SymbolManager

In order to convert the code in Main’s constructor to proper, coherent ActionScript I had to add these two lines:

  //main entry point for applications
  public function Main()
  {
    if( !background )
      background = SymbolManager.instance().createBackground();
    ...
  }

 

By doing so I could use the same ActionScript code in Flash and in Flex. In the Flash version the background member would be automatically instantiated but in the Flex version we would instantiate background manually. I could have initialized background with just “new MovieClip()”. But I decided early on to encapsulated all the work for emulating “Flash Magic” into one class, which I named SymbolManager.

 

Lost in space

Some symbols like Ship have an initial transform, that SymbolManager also has to emulate. For example if you select GameItems/graphics/ship_graphic in Flash IDE’s Window/Library and observe the Transform panel you’ll notice that Ship is rotated by 90 degrees and also scales x and y axis by about 51.8%. The Info panel tells us that the initial x/y positions are set to 0.0/0.0.

Unfortunately I didn’t find the values that the Flash IDE gave me very reliable. In order to get correct values I had to export my symbols to FXG (File/Export/Export Selection, format=FXG). The exported Ship.fxg for GameItems/graphics/ship_graphic was just an XML file and looked like this:

// Ship.fxg:
<?xml version="1.0" encoding="utf-8" ?>
<Graphic version="2.0" viewHeight="800" viewWidth="480"
 ATE:version="1.0.0" flm:version="1.0.0" d:using=""
 pd:backgroundColor="13421772"
 xmlns="http://ns.adobe.com/fxg/2008"
 xmlns:ATE="http://ns.adobe.com/ate/2009"
 xmlns:d="http://ns.adobe.com/fxg/2008/dt"
 xmlns:flm="http://ns.adobe.com/flame/2008"
 xmlns:pd="http://ns.adobe.com/xfl/2008/pd">
  <Library>
    <Definition name="GameItems_graphics_ship_graphic"
     flm:originalName="GameItems/graphics/ship_graphic"
     pd:symbolType="1">
      <Group pd:symbolType="1">
        <Group d:type="layer" d:userLabel="Layer 1">
          <BitmapImage
           rotation="90" scaleX="0.51818848" scaleY="0.51817322" 
           source="@Embed('Ship.assets/images/ship_bmp.png')" fillMode="clip"/>
        </Group>
      </Group>
    </Definition>
 </Library>
 <Group d:type="layer" d:userLabel="Selected Items">
   <GameItems_graphics_ship_graphic x="-12" y="-15.35"/>
 </Group>
 <Private/>
</Graphic>

 

As you can see rotation and scale values are listed as attributes of the BitmapImage tag. The interesting part is in the GameItems_graphics_ship_graphic tag, which is the container of the ship image. That container tag sets x and y to -12 and 15.35. You will never find those values in the Flash IDE. But they are very important in order to correctly set up the initial transform.

With the FXG information I was able to implement SymbolManager.createShip() and onShipLoaded, which gets called when the ship bitmap has been loaded:

private function onShipLoaded(e:Event):void
{
  const bitmap : DisplayObject = getBitmapFromEvent(e);
  if( bitmap )
  {
    bitmap.scaleX = 0.51818848;
    bitmap.scaleY = 0.51817322;
    bitmap.rotation = 90;
    bitmap.x = -12; bitmap.y = -15.35;
    bitmap.width = SHIP_WIDTH;
    bitmap.height = SHIP_HEIGHT;
    ...
    _removeEventListenerFromTarget(e, onShipLoaded);
  }
}

 

There were a few other minor things I had to adjust in the Pew Pew. But I was eventually able to create a Pew Pew SWF from Flex. Once I had a coherent Flex project it was pretty easy to cross-compile Pew Pew to JavaScript.

 

A fine line

In the end it took me about three days to reanimate the old Pew Pew prototype with the latest version of my cross-compiler. Along the bumpy way I fixed a bunch of regression bugs and a few new ones as well. It was quite a moment of victory when I hit the debug button in Visual Studio 11 and Pew Pew finally worked in Metro. I thought, if I put in a little bit of work I might be even able to submit this game to the Windows 8 First App Contest I had just read about.

Being able to cross-compile that old Pew Pew prototype to Metro was a big accomplishment for me, which I wanted to share with my wife, who was sitting right next to me. The conversation that followed went like this:

Me: Hey, I got Pew Pew up and running in Metro!
She: That’s great, let me see.
Me: Here it is. It needs a little bit of work, though. I am thinking of submitting it to the contest.
She: You will never win the contest with something like that.
Me: What do you mean? The graphics? It’s supposed to look crummy. It’s charming.
She: There is a fine line between “crummy” and “crappy”.

I knew I was in trouble. Cross-compiling ActionScript to JavaScript was one thing, developing a cool game that is not crappy is a completely different beast I would have to tame.