Categories Displayed in Flash

Archive for the 'Sketch' Category

Flash: Detecting the end of an animation

Say you have an animated character in a game, that others need to know when they are through playing their animation. Here are a few approaches:

Let's first assume we have a generic object named 'player_mc' that is our animation. We also have a centralized controller script closer to the root, that has a function like:

  1. function onAnimationComplete(evt:Event = null):void{
  2. //do something
  3.  currentPlayer = evt.target as MovieClip;
  4.  currentPlayer.gotoAndStop("still");
  5. }

Note that we use evt = null just in case we want to use call the function without passing it an event.

1) Using addFrameScript

  1. var init:Boolean;
  2. if(!init){
  3.        /// !init check, this block is only needed when pasting on timeline
  4.       // to keep it from activating more than once when the movieClip recycles
  5.         addFrameScript(totalFrames -1, onAnimationComplete);
  6.         init = true;
  7. }
  8.  
  9.  

Just remember if doing that approach, addFrameScript uses a frame number one less that what you see in Flash.

This approach is safer when indexing frame labels prior (e.g. with UIUtil) to map say a frame label of "AnimationFinishedFrame" to the frame number for addFrameScript to use.

  1. addFrameScript(com.troyworks.ui.UIUtil.getNumberOfFrameLabel("AnimationFinishedFrame", onAnimationComplete);
  2.  

2) Using Event Bubbling

Just communicate up the display list.

  1. player.addEventListener("ANIMATION_COMPLETE", onAnimationComplete);

Then

  1. //inside the player on last frame of the player animation
  2.  
  3. stop();
  4. dispatchEvent(new Event("ANIMATION_COMPLETE", true, true));

For more info see here.

3) Use Singleton/Static or Stage as a message bus.

like 2 but using the stage or some other easy to access to communicate without bubbling.

4) Poll for Progress

This is a bit more work, but get's you the ability to find out more about the progress, eg. showing a progress bar for percentage played.

  1. player.addEventListener(Event.ENTER_FRAME, onENTER_FRAME
  2.  
  3. function onENTER_FRAME(evt:Event):void{
  4.   var playermc:MovieClip = evt.target as MovieClip;
  5.  
  6.    if(playermc.currentFrame == playermc.totalFrames){
  7.        onAnimationComplete();
  8.    }
  9. }

Hope that helps.

Flash: Fast Prototyping and Sketching UI with FlowControl

While I enjoy the new power of Flash9/AS3.0, one of the things I really dislike about the AS3, is how much extra work to do basic things like setup buttons to do simple timeline control, this is especially hard on designers and animators who don't code. I agree with 5etdemi post 4 years ago, the basics have ironically gotten harder to do.

What wrong with ECMAScript/Actionscript? Well, it is not especially well suited for certain tasks, the worst of which is time-based/timeline-oriented animation and declarative drawing. I mean, it seems pretty ironic that ActionScript is derived from ECMAScript which was primarily oriented at scripting in a text-based environment, HTML and the DOM. Simply put, ActionScript has leaned more and more towards heavy lifting text operations (like RegEx and the new XML implementation) and less towards building programmatically what Flash was originally meant for, animation.

While there are useful utilities like Lee Brimelow's nice event generator utility, I wanted less, as even if being good with actionscript, when you have a few minutes to put together something for (or during!) a client/skype meeting, scripting isn't really an option. Thankfully AS3 has some tricks to help!

(Either JavaScript is not active or you are using an old version of Adobe Flash Player. Please install the newest Flash Player.) ( press RIGHT arrow key to advance)

One tennant of Sketch is using the type and instance names to do most the event binding work, largely this is to allow Designers to stay on the timeline, but the same could apply to objects created in actionscript as well. So in the above example there are 2 scenes, and a bunch of empty frames, and a nav control. What's special is the buttons you see are actually just semi-transparent buttons (I call InvisiButtons) that have instance names like 'next', 'prev', (shown in the button names) there is no other script on the timeline for them. That via the FlowControl listening to ADDED event are autowired up to call 'nextFrame()' , 'prevFrame()' etc. These InvisiButtonscould be used as a hotspot over a comped image or basic text and wireframe graphics, allowing low to high fidelity comps to be created.

In this example a ENTER_FRAME event updates the TextField off to the right to show the current scene, frame et, to show where you are.
FlowControl has some other nifty features.

  • optional features is the fadein, fadeout effect, with configureable color, so while there is a normal timeline, the fade effect is free and ads a bit of polish IMHO.
  • tooltips telling you where the buttons go/do.
  • keypress for left/right arrow to go to the next previous frame, escape to goto first frame-useful for slide shows
  • XXX_autoBtn. If it sees this pattern that button will generate an XXX event (e.g. sayHi_autoBtn generates a new Event("sayHi"), which anybody is free to listen to.

All these are in the example (as well as all the code for FlowControl) on code.google.
Flow Control can be used 'embedded' as the document level class (or extending from it) or loaded as an external swf, and I'll be demoing that in the future. That latter feature is more targetted to creating highly skinnable games which I'll be covering in future blog posts.

Flash: View Controller binding

When using actionscript to turn a MovieClip , Sprite etc into a component. There are several points that can be used to introspect the children to bind the displayObject, whatever configuration is on stage, with the ActionScript class. Different binding points allow different things, here's the order and

OUTPUT:

  • constructor: // displayObject children are already on stage in the displayList but haven' t been drawn yet. the Actionscript class for it is just getting instantiated.
  • onFrame1: // actionscript on or associated with frame1 has fired
  • activateHandler: //not always called
  • onFirstFrameRender: //first time ENTER_FRAME has hit the frame1, meaning it's script has fired, and everything is already visible.
  • renderHandler: //only if the stage.invalidate() is called.
  1. package {
  2.  
  3.  import flash.display.MovieClip;
  4.  
  5.     import flash.display.Sprite;
  6.  
  7.     import flash.display.StageAlign;
  8.  
  9.     import flash.display.StageScaleMode;
  10.  
  11.     import flash.events.Event;
  12.  
  13.  import flash.text.TextField;
  14.  
  15. public class StageExample extends MovieClip {
  16.  
  17. public var sizeMe:MovieClip;
  18.  
  19.         public var output_txt:TextField;
  20.  
  21.         public var textLog:Array = new Array();
  22.  
  23. public function StageExample() {
  24.  
  25.                 super();
  26.  
  27.             stage.scaleMode = StageScaleMode.NO_SCALE;
  28.  
  29.             stage.align = StageAlign.TOP_LEFT;
  30.  
  31. stage.addEventListener(Event.ACTIVATE, activateHandler);
  32.  
  33.             stage.addEventListener(Event.RESIZE, resizeHandler);
  34.  
  35. addEventListener(Event.ENTER_FRAME, onFirstFrameRender);
  36.  
  37.                 addEventListener(Event.RENDER, renderHandler);
  38.  
  39. addToLog("constructor: w " + stage.stageWidth + " h " + stage.stageHeight);
  40.  
  41.                 addFrameScript(0, onFrame1);
  42.  
  43.                 stage.invalidate();
  44.  
  45. }
  46.  
  47.         private function onFrame1():void {
  48.  
  49. addToLog("onFrame1: w " + stage.stageWidth + " h " + stage.stageHeight);
  50.  
  51.                 //resizeHandler(event);
  52.  
  53.         }
  54.  
  55.         private function activateHandler(event:Event):void {
  56.  
  57.             trace("onFirstFrameRender: " + event);
  58.  
  59.                 addToLog("activateHandler: w " + stage.stageWidth + " h " + stage.stageHeight);
  60.  
  61.                 //resizeHandler(event);
  62.  
  63.         }
  64.  
  65. private function onFirstFrameRender(event:Event):void {
  66.  
  67.             trace("onFirstFrameRender: " + event);
  68.  
  69.                 addToLog("onFirstFrameRender: w " + stage.stageWidth + " h " + stage.stageHeight);
  70.  
  71.         //      resizeHandler(event);
  72.  
  73.                 removeEventListener(Event.ENTER_FRAME, onFirstFrameRender);
  74.  
  75.         }
  76.  
  77.         private function renderHandler(event:Event):void {
  78.  
  79.         trace("renderHandler: " + event);
  80.  
  81.         addToLog("renderHandler: w " + stage.stageWidth + " h " + stage.stageHeight);
  82.  
  83.     }
  84.  
  85.  ///////////////////////////////////////////////////////////////////
  86.  
  87. private function resizeHandler(event:Event):void {
  88.  
  89.             trace("resizeHandler: " + event);
  90.  
  91.             trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight);
  92.  
  93. addToLog("resize: w " + stage.stageWidth + " h " + stage.stageHeight);
  94.  
  95.                 sizeMe.width = stage.stageWidth;
  96.  
  97.                 sizeMe.height = stage.stageHeight;
  98.  
  99.         }
  100.  
  101.         private function addToLog(str:String):void
  102.  
  103.         {
  104.  
  105.                 textLog.unshift(str);
  106.  
  107.                 output_txt.text = textLog.join("\r");
  108.  
  109.         }
  110.  
  111.     }
  112.  
  113. }

Flash: 4 Tricks for using MovieClips as Buttons

So here are 4 tricks I've found useful:

  1. Use actionscript to create the Button behavior (enlarging etc), centralizing it in one place.
  2. Use clips styled in the IDE, and copy their style/filters for mouse over, down, disabled state.
  3. Use mouseEnabled (and alpha/brightness) to temporarily disable a button
  4. Use the instance name as the label, and in the event parsing.

(Either JavaScript is not active or you are using an old version of Adobe Flash Player. Please install the newest Flash Player.)

Styling MOUSE_OVER, MOUSE_DOWN States

Similar to CSS centralizing style, centralizing behavioral style offers the same maintenance advantages.   Using clips on the stage for style allows the designer to visually see what's desired, rather than spending countless iterations tweaking actionscript to get it right, it's a good handoff in team based approach.   I also sometimes use describeType 'autowire' any buttons of particular classes.   For the disabled state I stacked two brightness filters ontop of each other.

Styling it to send all the events to a central controller (MVC UCM style), allows things to be debugged far easier than when events are going everywhere, and the controller has internal state, respond appropriately, say ignoring all clicks during loading, without having to rewire all the various components.

For 1 and 2 here's the underlying code. Notice I use mouseChildren = false to keep the label textfield from generating events.

  1.  
  2. function configureMC_Button(ary:Array, addL:Boolean = true):void {var a:MovieClip;
  3.  
  4. var i:int = 0;
  5.  
  6. var n:int =ary.length;
  7.  
  8. for (true; i < n; ++i) {
  9.  
  10. a = ary[i];
  11.  
  12. if (addL) {
  13.  
  14. a.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverHandler);
  15.  
  16. a.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutHandler);
  17.  
  18. a.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
  19.  
  20. a.addEventListener(MouseEvent.MOUSE_UP, onMouseOverHandler);a.addEventListener(MouseEvent.CLICK, onMouseClickHandler);
  21.  
  22. a.mouseChildren =false;
  23.  
  24. } else {
  25.  
  26. a.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOverHandler);
  27.  
  28. a.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOutHandler);
  29.  
  30. a.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
  31.  
  32. a.removeEventListener(MouseEvent.MOUSE_UP, onMouseOverHandler);
  33.  
  34. a.removeEventListener(MouseEvent.CLICK, onMouseClickHandler);
  35.  
  36. }
  37.  
  38. }
  39.  
  40. }
  41.  
  42. function onMouseClickHandler(evt:Event):void {
  43.  
  44. var mc:MovieClip = MovieClip(evt.target);
  45.  
  46. output_txt.text =(" \r"+ mc.label_txt.text + "**Clicked **");
  47.  
  48. }
  49.  
  50. function onMouseDownHandler(evt:Event):void {
  51.  
  52. var mc:MovieClip = MovieClip(evt.target);
  53.  
  54. mc.filters = downTreatment.filters;
  55.  
  56. }
  57.  
  58. function onMouseOverHandler(evt:Event):void {
  59.  
  60. var mc:MovieClip = MovieClip(evt.target);
  61.  
  62. mc.scaleX = mc.scaleY = 1.2;
  63.  
  64. mc.filters = overTreatment.filters;
  65.  
  66. }
  67.  
  68. function onMouseOutHandler(evt:Event):void {
  69.  
  70. var mc:MovieClip = MovieClip(evt.target);
  71.  
  72. mc.scaleX = mc.scaleY = 1;
  73.  
  74. mc.filters = [];
  75.  
  76. }
  77.  
  78. overTreatment.visible = downTreatment.visible = disabledTreatment.visible = false;
  79.  
  80. configureMC_Button([a_btn, b_btn, c_btn, d_btn, e_btn, f_btn, g_btn]);

Enabling and Disabling via ActionScript

You can disable/enable a clip simply by using the mouseEnabled field which will turn on or off mouse events.   Contrast this with the approach of adding/removing listeners to enable or disable a button, which may have multiple listeners for different classes, you can't see, so you never know if you got them all.

  1.  
  2. function setEnabled(  ary:Array, enable:Boolean = true):void {var a:MovieClip;
  3.  
  4. var i:int = 0;
  5.  
  6. var n:int =ary.length;
  7.  
  8. for (true; i < n; ++i) {
  9.  
  10. a = ary[i];
  11.  
  12. if (enable) {
  13.  
  14. a.mouseEnabled = false;
  15.  
  16. a.filters =[];
  17.  
  18. } else {
  19.  
  20. a.mouseEnabled = false;
  21.  
  22. a.filters = disabledTreatment.filters;
  23.  
  24. }
  25.  
  26. }
  27.  
  28. }
  29.  
  30. setEnabled([d_btn, e_btn], false);

Using the instance name as id.

Using the instance as the basis for identification, saying parsing it for use the label, and in the event, can make things more uniform than having buttons dispatching events,.   In internationalized apps where the label changes based on whatever language is present, the instance name can serve as a key to finding the appropriate text in some multilanguage dictionary, or even show when a label is missing.

In Closing
Using a single movieClip has many advantages,  only one skin has to be created instead of 4-5 for a SimpleButton.   When handled in the way outlined above it has many of the advantages of being a component, without the work of being a full blown one.   It minimizes the number of classes/library items when each button is created with a unique label hard coded into it.

In conjunction with a tweening engine, it allows for what I call momentum styled buttons.   Buttons that don't have crisp state changes like SimpleButton attempts on the mouse up/over/down. Things that fade up, and down, like real world lightbulbs, cars.   When used with ColorTransform this can allow for adaptively styled UI, like that of CSS.

There are also safety benefits. In AS3.0 the timeline is purely script driven, there are still bugs when using multiple frames to manage state on particular versions of the flash player.   I've had odd issues with sound not firing or not stopping firing, components blowing up when in tweens or in timelines.

AS3: Prelude to Tweeny, a fast and flexible Tweening engine

So I've been working over the weekend on the Tweening engine based on Cogs affectionately called Tweeny. I find it highly amusing as I've been working in Flash since Flash 4, I have built so many presentation engines it borders on comical when every new client inevitably asks for a powerpoint lite clone, and consider myself relatively advanced,..yet how much of a loop this one threw me for.

WhileI had an AS2.0 class already working, it's taken me 3 days to get the basics working in AS3.0. This is because yesterday I totally got schooled on Matrixes with Senocular's most excellent tutorial, (my brain still hurts) and then excited by Penner's lurking MatrixTransform and ended up scrapping several paragraphs that comprise most the standard tween engines. I find myself building this next presentation engine in a way that bears no resemblence to most engines I've used or seen, partly due on the architecture difference between AS3.0 and prior, and partly expanding the role of Tweening beyond the simple display classes. Every time I added a new feature or solved a bug, I found a way to make things simpler.
Matrixes are cool, as you can get away with just easing the Begin and End matrixes rather than doing every calculation, plus you get the ability to offset the registration point and skew, features both very useful for the Authoring type interfaces and kids content I'm creating. More power and more speed to boot! In running performance tests, it's about 2x faster than setting DisplayObject.rotate, DisplayObject.width, scale etc as most tween engines do. Not that running those scripts are typically the limiting factor relative to the overhead in rendering, but still it's nice to know that the underlying code isn't a bottle neck.
Like most Tween engines, Tweeny supports a pluggable easing that affects multiple properties (x, y, width etc) with individual ranges (x[-5, 100], pan[-1,1]). But something my math-whiz girlfriend help me realize was that since duration is fixed, the easing equasion (often filled with expensive Math operations) can be calculated once as the 'normal' and then scaled to the others ranges, cheaply with simple * + . This should significantly reduce tweening calculation overhead,...and dammit I like smart particles flying at 60fps!

To further that I realized that the normalized calculation, is easy to broadcast, so that other listeners keep in synch, and can conceivably modularize or patchwork tweens. One of the things that I've wanted to try for awhlie was a way to balance out top-down easing approaches with bottom up physics routine. This usually involves Euler or Verlet accumulation and integration of forces, but most tweening engines are rather possessive when it comes to updating display objects...only one at a time else they start overwriting, where's the fun in that?
Some other features I like, Sound or SoundChannel volume and pan, colorTransform tweening (which I use a lot for configuration dynamically styled UI's) easier constructors for Bounce and Elastic's extra params which I use with kids games.

One thing that I'm not currently planning on supporting soon is a JSON or XML based interface. I find it poor programming practice to do

TweenEngine.create(target, duration, {x:1,y:2, ....})

in AS3.0 when you can do:

var t:Tweeny = new Tweeny();
t.initFrom(target,duration);
t.x = 1;
t.y = 2;

t.addEventListener(...)

t.startFromBeginning();

Because using fully typed classes allow for autocomplete to help fill out the values, offer stricter type checking and setting of defaults. When they are on a line by line they are easy to comment out to turn on and off. There isn't any overhead to parse XML or JSON into useable values. Since it's part of the Class definition it should also be significantly faster than dynamically allocated Object properties on the prototype.
Keeping a tween around, keeps memory and garbage collection from churning, especially for most applications where the tween is reused over and over again to put thing back and forth. I realize why many tween engines opt not to go this route as managing transitions on top of other transitions can get tricky. But know that I know about these issues I'm glad I spent the extra time. Since Tweeny uses the Playhead API, it supports stop, rewind, play, playbackward etc, it's not really different than keeping an mp3 or swf around.
So far it's about 10K, so 3x larger than say Tweenlite, but much of that is the inclusion of the framework/Cogs is useful for many other things, websites and games especially, and I'm still working on it.
The morning had an epiphany and I was able to tie together many of the related ideas into a cohesive model, so I have a roadmap for the next few months, and even a relatively simple Authoring UI for all the features exposed. Looking at the one pager of noteobook, probably 7 boxes, and only a few UI elements, in that largely what I'm working on is creating much of the same metaphors (e.g. timeline) of Flash inside of Flash...just like Web 2.0 is largely recreating the OS inside of the WebBrowser. And Virtualization is recreating the connectivity of electronics. One that note, if your looking for good book on self-referentiality check out Gödel, Escher, Bach .

AS3: MovieClip.addFrameScript

MovieClip.addFrameScript is a very powerful way to decouple script from the timeline. This post help you understand how it works.

When movies are compiled (including the top level swf), if no class is provided. Flash creates one for you behind the scenes. 1 Class is created per 1 MovieClip, typically sharing the same name. It helps to understand how the conversion happens, to make best use of it, and writing code on the timeline in general.

  1. imports are only needed once in a timeline
  2. variables declared on a frame, end up as class variables,
  3. you can't redeclare variables on multiple frames.
  4. only one function is called per frame, this can be tweaked, overridden or amended with addFrameScript

You can't redeclare variables,

on frame1 and frame2 put:
var aName:String = "A";
will give you the following error:
1151: A conflict exists with definition aFrame in namespace internal.

however putting on Frame 1

var aName:String ="A"; //frame1

and on Frame2

aName = "B" //frame2

works fine.

MovieClip.addFrameScript

Is the undocumented glue between the timeline frame and the class's function ( if bound). There can only be one script/function bound to a frame using the built in addFrameScript, though an alternative approach is to poll currentFrame changing (via Event.ENTER_FRAME).

This binding happens via addFrameScript(frameNumber-1, functionToCall...); Note that the first argument is ZERO BASED (while normally the timeline is 1 based)
During the Fla.compilation our script gets put into a function like:

Class Test_fla{

function frame1():*{

var aName:String = "A";

}

function frame2():*{

aName = "B";
}
}

The important thing to note is the frame1...frameN naming, where N is the frame number 1-whatever, We can validate this by decompiling the swf, using the AbcDump utility, which takes our test.swf and outputs:

function Test_fla::frame1():* /* disp_id 0*/
{
// local_count=1 max_scope=1 max_stack=2 code_len=16
0 getlocal0
1 pushscope
2 findpropstrict trace
4 pushstring "onFrame1"
6 callpropvoid trace (1)
9 findproperty aFrame
11 pushstring "aFrame"
13 initproperty aFrame
15 returnvoid
}

So the end will be a frame to function mapping (which we can't see) like:

  • [0]= frame1()
  • [1]= frame2()

Note again the frameNumber is zero based, while the script is 1 based.

Consequences

which has 2 consequences,

  1. you can't name your own functions frameN if one already exists. This is the same error as compiling in the same *.as:
    function frame1(){

    }

    function frame1(){

    }

  2. you can call and over ride these functions,
    addFrameScript(0, overrideFrame1);
    function overrideFrame1():*{

    //do something before
    frame1();
    //do something after
    }

In addition the top level Class, will be shared across different scenes, but these can have on the timeline different configuration values (say which language to use), and when used with jsfl to publish all scenes, create a handful of starter files.

Conclusion

This is kick ass for completely separating a UI.swf from a Controller.swf Keeping designers working on the timeline, and coders out of the design.

For the coders, and others working in Flash, is the ability to use addFrameScript to perform actions when a frame is fully initialized and children are on stage, similar to onLoad() in AS2, where-as use of ENTER_FRAME gets called prior to children in that movieclip getting called...this is similar to onInit() in AS2. So for example you can have a button only on a particular frame and setup it's event listener to call your controller...in another swf, regardless of what the playhead is doing.

In someways I find this cleaner than watching using addedToStage, rendered etc.

REFERENCES:

[1] Senoculars post
[2] FlashGurus info as Senocular
[3] BIG SPACESHIP, Extending addFrameScript to use the frame labels instead of framenumbers... the same glue I'm using as well, though I'm more interested in events being generated, than script.

[4] an alternate approach on evilzug's journal using ENTER_FRAME


You should be aware if using that approach that when the framescript is called via the ENTER_FRAME, items in the frame may have not been added yet, so this approach can't be used to bind to objects on the timeline (e.g. to registerListeners to buttons), this the addFrameScript does not suffer from.

Here's an output with one on the timeline the other added via oizys's frameScript approach:


entered frame 3
onReachedInstructions instructions null <- no button yet, via modified addFrameScript
timeline reached instructions [object SimpleButton] <- normal addFrameScript
onReachedInstructions instructions [object SimpleButton] <- now the button exists

UI: reusing flash.display.Loader and non-visible preloading

This covers how to preload images then scale/center appropriately prior to showing them.

Going Old Skool doing it Flash8 Style
In Flash8 preloading assets required all sorts of trickery to load a clip prior to resizing it, centering it and then displaying it. There were two techniques

1) loading the clip offstate

2) putting it into a nested clips, one was a container, the other a sacrificial clip. Any changes to realClip would get blown away as the new image/clip gets loaded in.

  • PictureThumbnail //200x300 normally scaled down to 100x100
    • loadTarget_mc
      • realClip_mc

so we would do something like

realClip_mc.loadMovie("image.jpg"); //400x500

loadTarget_mc._visible = false;

In addition we'd have to do some juggling of the scale/centering of realClip to match that of the PictureThumbnail.
Flash 9 suave.
Thankfully with flash.display.Loader, and the display list there is the capability to avoid jumping through these hoops.

One of the major changes for me was wrapping my head around the flash.display.Loader object and it's relation to the display list. The idea of adding a Loader to the display list as in the Adobe example, is similar the old school approach of putting it in undesired containers, we'd really prefer getting PictureFrame.image.jpg Getting at the loaded clip is even more difficult: loader.contentLoaderInfo.content ! In addition the need to addListeners to the

configureListeners(_loader.contentLoaderInfo);

instead of the loader object directly was non-intuitive.

The nice thing is with the DisplayList there is a whole world of nonrendered visual behavior. So we can Load our clips independent, then manipulate them prior to adding them to the display list exactly where we want/expect. And then proceed to reuse the Loader for other assets (as in a sequential preloader). Here's an example, in where we can reuse the loader, and then when it's complete match it's size to an onstage Flash IDE drawn placeholder.

Sketch: View Controller separation, MovieClip.addFrameScript

There are several approaches to using Flash assets in Flex, as assets. Predominately centered around using getDefinitionByName(), e.g. here we are going to pull from a library of 5 possible graphics and attach it to stage.

var dc:String ="missle"+num+"_MC";
var classRef:Class = getDefinitionByName(dc) as Class
var mcMissleGraphic:* = new classRef(); //might be a movieclip, shape or sprite)
addChild(mc); //similar to attachMovie of old

or extending this example to make it a user-defined class, by passing it into the constructor (using composition)

var missleMC:Sprite = new Missle(mcGraphic);

addChild(missleMC);

In this example the visual tree will be

  • Child
    • MissleController (has a graphic)
      • MissleGraphic
        • FlameAnimation
        • BlinkingLight
          • RedBlinkClip
          • GreenBlinkClip

The downside is that if something ever needs to access the MissleGraphic or child clips (like hiding red or green blink clips) it must go through an additional layer. When coding it's easy to get these out of synch, and the layer is one more thing to keep track off. It's the difference between IS A and HAS A, and also doesn't faciliate timeline control of the user experience.
But it's a useful approach, and Grant Skinner even has a library to help deal with the loading of external swfs. That must be fully initialized prior to attempting this.
http://www.gskinner.com/blog/archives/2007/03/using_flash_sym.html

and if your going to be embedding it you can also follow this

http://www.digitalflipbook.com/archives/2007/03/associating_cus.php

Sketch's approach Designers First, Script later
Sketch differs from this as these all imply the script has control first and is pulling from the library of assets designed in Flash IDE. Sketch prefers that designers can lay things out on stage, working on the timeline to process, and then later load up script and have it embue them with life, primarily as placing and sizing things laying out things is best for visual tools, when outside of grid layout controls for data driven components. This is not to say that you can't have AS code generate all manner of things, just that most approaches don't give you the option for the first. In the mini-game industry, and animated content this is far more acceptable to smooth production and good users experiences.

UI and Controller synchronization via MovieClip.addFrameScript
So I was looking through the old paperwork when I was a part of the Flash8/9 beta. Since it's been released, and out for awhile it's interesting to compare what made it where and what's changed. It's also much easier to read having gotten up to speed on AS3. One of the things I've rediscovered is MovieClip.addFrameScript, which totally fits into the Sketch way separating UI from controller synchronization, though I hadn't seen it before. Of course I wasn't the first to discover this:
http://www.flashguru.co.uk/undocumented-actionscript-3/

http://www.kirupa.com/forum/showthread.php?p=2098268

BigSpaceShip in particular is on a similar approach, he has an interesting take of introspecting the movieClip labels and attaching a script to when they are fired. This fits into the Sketch philosophy of using minimal or no code in the view to indicate state.
http://labs.bigspaceship.com/blog/?p=43
Though Sketch again differs in it's approach, while it too introspects the framelables, creates a registry of sorts, it treats changes of the playhead, as an event to broadcast so more than one party can listen, addFrameScript is like removing that intermediate event and directly calling a script instead. The downside to the addFrameScript approach is it will override any script inside inside the timeline, so in some ways polling based on EnterFrame events is less prone to breaking something the designer is doing (e.g. play/stop or configuration);

Generate UI Class Linkage via ByteArray?
The other cool thing I found which is a lower level hack is the Tag in byteCode that the Flash Player that binds the library symbol with the actionscript class, in a streaming fashion so that not everything has to be put on frame1. Looking through the source code documentation it's still in, and seems similar to the Object.registerClass.
I'm unsure if the security model will permit a loaded swf to append to the parent, but it seems likely given we are already doing that with getClassDefinition etc.. It's also possible that something similar may be done via getClassDefinition, if however the linking mechanism in the player can be called. This would be a huge advantage to Sketch as currently the conversion of instances one stage to classes is via addChild/removeChild on the displayList. Not horrible but not clean, in particular keyframes can break some things as a new reference will be on stage but if it keeps the same name the displayList doesn't get notified.

Ideally this can be via ByteArray it's possible to read and write swfs at a binary level. So generation of the linkage might be possible via AS3, without the need for composition.
In Object.registerClass, an library item IS a user defined class. With AS3, it's similar but the linking is defined at compile time in the IDE, thus we have to use composition instead for late/lazy view binding. So Object class HAS a view. It's possible to use Proxy to make this more seamless but then we lose the typing of MovieClip/Sprite. e.g. a Ball clip is a ball, is also a MovieClip.

Some work on dumping the tags is already being done, so it might not be too hard to find it out.

http://www.5etdemi.com/blog/archives/2007/01/as3-decompiler/

http://joeberkovitz.com/blog/2007/04/08/secret-life-of-swfs/

Sketch: Advantages of using.

Since the UI/Skin is one swf and the code in a separate, loosely coupled. It provides many benefits in team development. Here's a short list.

  • For Design teams, Reskinning games is drop in replacement. Just the Fla, no code, compiler, SVN and dependencies to set up for a designer, no worries about changing a piece of code that they should have.
  • For a coder, no fonts, cross platform issues pop up, no worries about bumping things.
  • It helps enforce good boundaries/interfaces between teams. frameLabels can be checked to conform to an API and script complain on compile.
  • Facilitates reuse. Code "engines" become easier to maintain as they can only be changed by developers, preferably thorough a unit testing framework. Similarly Existing skins can be turned into completely different games if the new logic uses the same view/controller API.
  • Working with contractors/outsources allows you to send just the compiled file instead of the source tree.
  • bug fixes and new features can be rolled out by dropping in new files. Say a puzzle whose boards are generated purely by script, gets new levels or features. This is very useful when you have multiple skins, or versions (if configuration controlled) for the same engine in production, an Ant script can just update the folders with the new file rather than needing to recompile all of them. One one project we have close to 30 minigames, each about 5 minutes long, just different assets and configuration files. So it's compile once, copy 30, rather than compile 30.

Sketch: Working on the Timeline, with currentFrame, currentLabel, and FrameTracker

Sketch is a way for letting designers work on the timeline with essentially no code, and coders in pure code no visuals, where they are happiest, typically in separate swfs. Which introduces the key questions, how does they talk to each other? and coordinate actions?

There are two methods one is lateViewBinding which turns MovieClips into components at runtime via composition, which I'll cover in a future post, the other is using the frameLabel as a contract. Current 'active' framelabels as events indicating when the playhead has changed.

Things to be aware of when using currentFrame and currentLabel
There are few things that one should be aware of when working in this fashion.

  • currentFrame changes when hitting a new frameLabel, regardless of which way the playhead is going. It's essentially a rendered a frame named this event, not a box indicating the framelabel is valid from say it's start's Frame to some other frame as it's often intended to mean when looking at the timeline. This maps onto a finite state machine one labelled frame == one active State, only one active at a time.
  • New frames with no frame labels don't change the currentLabel So if you have an empty keyframe after a labelled keyframe, it will keep the value until it's changed to something not empty. So if your looking to clear out a frame label, you should use something like " " (which is a valid if somewhat odd frame label)
  • FrameLabels are intended to be used one at a time. While the FlashIDE allows for multiple layers, each potentially having overlapping frame labels, this isn't easily accessible in script. So if you hit a particular frame with multiple layers on it, each having a label, it will choose as the currentLable whatever the level load order is. So say you had frameA on layer 1 and frameB on layer 2. currentLabel only reflects frameB. Most the time this isn't an issue. But there are times when using changes as events.
  • you'll have to roll your own change event to deal with frameLabel changes. This strikes me a bit wierd given all the other changeEvents. But so be it, it's only 2 paragraphs of code.

Creating and Using FrameChangedEvents

All Labels of a given are known via the currentLabels array property. which gives you a list of frame labels, each having a name and a frame number. As enterFrame events happen, changes to the currentFrame and currentLabel can be dectected by comparing them to what they were on the last onEnterFrame event. Querying this collection when the frameNumber changes is how we can get access to multiple frame labels being active or not, and then by keeping a history of when they are active or not, broadcast events to the controller/model.
Using a FrameTracker MovieClip

An alternate approach for frame Enter and frameLeave events is using a frameTracker, basically a special basically empty MovieClip that is put on the timeline and based on when it loads and when it's unloaded it generates the appropriate events. The adding and removal of this is watching the DisplayObject for add and removal, and using the instance name as the base of the event. e.g. myFrameTracker becomes "myFrameTracker_ENTER", "myFrameTracker_LEAVE"

The detection of adding and removal of the FrameTracker from the stage is the basis of LateViewBinding.

« Previous Entries