July 7th, 2009
Just Another Flash IDE – Flex/Flash Builder Workflow
The usual flash sites that we build contain tons of huge graphics, animations, dynamic motion, videos, timeline transitions as well as tons of code, utilizing huge open source libraries like papervision 3d, tweening engines and so on.
We want everyone to be able to use their preferred design/dev environment to ensure a fast way to produce innovative high quality work. That includes using the Flash IDE and still being able to use Flex/Flash Builder as a developer.
So we are in need of a workflow where:
- Everybody can work on the project at the same time.
- Its easy to switch out artwork as well as organize it into multiple fla files.
- It must be possible to compile the project from the Flash IDE
- As few steps as possible to compile and test the project.
- Developers can use the coding environment of their choice.
- Everybody is using the same code.
There are many other possible workflows and different set ups, but following is a description of a basic flash set up/workflow that we used in many recent projects like Epsonality and Alienware: All Powerful. It allows designers to use the Flash timeline as they are used to. It allows devs to use any kind of text editor, but it also allows developers to code in Flex/Flash Builder (Eclipse), which is currently my preferred coding environment.
SEPARATE CODE AND ARTWORK
This method does not use any document root classes in the Flash IDE, and we generally dont link library assets directly to existing classes. We are using an Object-Composition like approach instead of inheritance, and pass the artwork (DisplayObject, usually a MovieClip) as a parameter to the constructor of a class. The class itself extends EventDispatcher to dispatch and listen to specific events. The main timeline and all library assets are just artwork that arent directly connected to any additional functionality through the linkage properties.
SETTING UP THE BASIC FILES
Create a new Flash file and save it as main.fla. Instead of a document root class, we are going to create our Main class instance on the 1st frame of the main timeline:
import com.bigspaceship.examples.flexflash.Main; new Main(this);
Notice that we pass the Main class a reference to the main timeline itself.
A basic Main.as class example:
package com.bigspaceship.examples.flexflash { import flash.display.MovieClip; import flash.events.EventDispatcher; public class Main extends EventDispatcher{ private var _mc:MovieClip; public function get mc():MovieClip{ return _mc; } public function Main($mc:MovieClip){ _mc = $mc; trace("Hello World"); } } }
Accessing any property or instance on the stage is as easy as writing:
_mc.myInstanceOnStageinstead of
this.myInstanceOnStage
To simultaneously work with Flex/Flash Builder we need to know if we are compiling from the Flash IDE or from Flex/Flash Builder.
Most sites we build follow the MVC pattern in one way or the other. So we are using a Singleton class called Model.as to store data, e.g. the current state of the site. Now we can just easily add a boolean variable named compiledFromFlex to the Model.as which is false by default.
package com.bigspaceship.examples.flexflash{ import flash.events.EventDispatcher; public class Model extends EventDispatcher{ public var compiledFromFlex:Boolean; private static var _instance:Model; public function Model($pvt:PrivateClass){ super(); if( _instance != null ) throw new Error( "Error:Model already initialised." ); if( _instance == null ) _instance = this; } public static function getInstance():Model { if (_instance == null){ _instance = new Model(new PrivateClass()); } return _instance; } } } class PrivateClass{ public function PrivateClass(){ } }
We only want to create a new Main instance on the first frame if we are compiling from the Flash IDE. To ensure that, we just need to change the script on the first frame to:
import com.bigspaceship.examples.flexflash.Main; import com.bigspaceship.examples.flexflash.Model; if(!Model.getInstance().compiledFromFlex){ new Main(this); }
Compile Main.fla and be happy to see the Hello World output.
SETTING UP A FLEX/FLASHBUILDER PROJECT
Create a new ActionScript project. Make sure that you set all classpaths and bin folders etc. correctly. (I dont wanna go in depth about the process of creating a project in Flex/Flash Builder here. Ill discuss that another time.)
The class that we set as the default application in Flex is basically just a wrapper that loads the main.swf that we compiled in Flash. I always name that class Flex_Main.as. Note that Flex_Main is only used to develop and test the project.
package{ import com.bigspaceship.examples.flexflash.Main; import com.bigspaceship.examples.flexflash.Model; import flash.display.Loader; import flash.display.MovieClip; import flash.events.Event; import flash.net.URLRequest; [SWF(width="518", height="400", frameRate="30", backgroundColor="#ffffff")] public class Main_Flex extends MovieClip{ public function Main_Flex(){ var l:Loader = new Loader(); l.contentLoaderInfo.addEventListener(Event.COMPLETE, _onLoadComplete); l.load(new URLRequest('main.swf')); Model.getInstance().compiledFromFlex = true; } private function _onLoadComplete($evt:Event):void{ var main:Main = new Main( $evt.target.content ); addChild(main.mc); } } }
In the Main_Flex.as file we set
Model.getInstance().compiledFromFlex = true;
so that the loaded file doesnt instantiate Main on the timeline but in the Main_Flex instance instead.
This process has a lot of advantages:
- It insures that we don’t have more than one instance of Main.
- It always compiles and runs the latest code. If you change something in the code you dont have to compile it from Flash first. You can directly compile and run it from within FlexBuilder. That works because the code gets compiled into main.swf as well as into Main_Flex.swf. If you compile/run it from within Flex/Flash Builder, Main gets instantiated within the Main_Flex.swf ApplicationDomain and uses the code that got compiled into Main_Flex.swf and vice versa.
- All the nice features in Flex/Flash Builder work: auto-completion, live code checking, code-refactoring, interactive step-through debugging, profiling.
HOW TO DYNAMICALLY CREATE AN INSTANCE FROM THE LIBRARY
Lets say we created a library Item and set its linkage name to MyLibraryItem. If I would write
new MyLibraryItem()
I would get an “ERROR 1180: Call to a possibly undefined method MyLibraryItem ” in Flex/Flash Builder because that class doesn’t exist in Flex/Flash Builder at compile time. But we now that it exists once we load the swf file that contains that library item. An easy fix for that is using the function
ApplicationDomain.getDefinition(name:String)
to create the class instead.
For an easier use we created a class called Lib.as:
package com.bigspaceship.utils { import flash.media.Sound; import flash.display.MovieClip; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.utils.getDefinitionByName; public class Lib { public static function createAsset($mc:MovieClip, $classname:String):DisplayObject { var c:Class = Class($mc.loaderInfo.applicationDomain.getDefinition($classname)); return new c(); } public static function createSound($mc:MovieClip, $classname:String):Sound { var c:Class = Class($mc.loaderInfo.applicationDomain.getDefinition($classname)); return new c(); } public static function createBitmapData($mc:MovieClip, $bitmap:String):BitmapData { var c:Class = Class($mc.loaderInfo.applicationDomain.getDefinition($bitmap)); return new c(0,0); } public static function createClassObject($classname:String):* { var c:Class = Class(getDefinitionByName($classname)); return new c(); } } }
So instead of
new MyLibraryItem();
we can use
Lib.createAsset(_mc, MyLibraryItem);
IT’S ALL ABOUT CLASSPATHS
Working with Flash components like FLVPlayback can get you in trouble sometimes because the Flex/Flash Builder Project doesn’t have a reference to the fl package. To solve that problem you just need to add the Flash-library-swc to your Flex/Flash Builder project.
Same problem can occur with custom components like Omniture’s ActionSource component. My quickfix for that is to create a MovieClip in a Flash library, add the component to the MovieClips timeline, export the MovieClip as a swc and add that swc to your Flex/Flash Builder project.
ADVANTAGES
- assets/artwork can easily split up into many fla/swf files that load on demand
- if code changes only Main.fla needs to be recompiled, all code is compiled into main.swf
- main.fla can contain any number of assets and layers.
- switching out artwork is as easy as switching out the swf file that contains the artwork
- no disadvantage to previous workflow
- designers can work in fla files although code is not ready yet
- developers can create all classes although theres no final artwork
- no need in creating swc files
- that the project also compiles from within the Flash IDE has a lot of advantages. No need for designers or for archived projects to use Flex/Flash Builder or any other software to compile and test the project.
