The Natural Entry Point Method (Tutorial & Source Files) - Introducing A New Way to Create Flash Applications Using Swfmill and MTASC

We had some extra time during my recent Best Practices Flash and Flex class and, at the request of the class, decided to cover FAMES. As part of this, we downloaded the latest Swfmill, which has support for linking classes to clips directly in the SWFML file. I thought we might as well use this new functionality as it is cleaner than littering your code with Object.registerClass statements. Little did I know at the time that this little addition would actually result in a huge shift in workflow. Much head-scratching later, I was able to come up with a workflow that is actually much simpler to work with than the current workflow. The problem is that currently it does not work with FAMES (Flashout, the "F", doesn't currently support this workflow) so you will have to use the command line for the time being.

Before we look at this new method, though, let's review the original method for working with Swfmill that has been popularized both here and elsewhere:

The Skeletal Injection Method

The original way of using Swfmill and MTASC together involves injecting a Swfmill Skeletal SWF with code. I'm calling this the Skeletal Inject Method. In this workflow, the skeletal SWF created by Swfmill only contains library assets and the only way to create an entry point into the application is to use a static main() method and compile using the –main attribute in MTASC.

The steps for creating a Flash application using the Skeletal Injection Method are:

1. Describe a skeletal SWF with library items using Swfmill's Simple (swfml-s) dialect (eg. skeletal.xml)

2. Use swfmill to compile the skeletal SWF (eg. skeletal.xml -> skeletal.swf )

3. Create an ActionScript 2 class with a static main() method and compile and inject it into your skeletal SWF using MTASC. If you want to link movie clips in your library with classes, you need to use Object.registerClass to do so in your class. You can, of course, also choose to assimilate _root and do all sorts of wonderful things along the way.

Pros: Cons:

The Natural Entry Point Method

The Natural Entry Point Method involves the use of at least two SWFs in your application. The first one is your main Application SWF and the second is a Classes SWF that contains the compiled code of your classes.

Your Application SWF contains your library, including any forms (movie clips that you link to classes using the new class attribute of the Swfmill <clip> tag.) Unlike the Skeletal Injection Method, which only contains a library, your Application SWF has to actually place a form (movie clip) on the Stage to provide a natural entry point for the application.

Instead of keeping your classes in the main application SWF, you keep them in a separate Classes SWF and import them into the main SWF as an external asset (at compile time).

In the next section, you will recreate the particles sample application which was previously constructed using the Skeletal Injection Method using the Natural Entry Point method instead.

Prerequisites: You will need to have Swfmill and MTASC installed and on your path. This example uses the command line (you don't need FAMES or other GUI tools to complete it.)

Natural Entry Point Example

Download the source files (NaturalEntryPointExample.zip; 106kb)

First, create the main application SWFML file, application.xml. The listing for this shown below:

SWFML-S:
  1. <?xml version="1.0" encoding="iso-8859-1"?>
  2. <movie width="320" height="240" framerate="30">
  3. <background color="#ffffff"/>
  4. <!--
  5. The Application and Particle classes have been
  6. compiled into the classes.swf file, which we
  7. import as an asset. This makes the classes
  8. available for linking to our movie clips.
  9. -->
  10. <clip import="classes.swf" />
  11. <frame>
  12. <!--
  13. The Library contains the Application form, linked
  14. to the Application class and the EclipseLogo
  15. sprite, linked to the Particle class.
  16. -->
  17. <library>
  18. <clip id="Application" class="Application" />
  19. <clip id="EclipseLogo" class="Particle" import="library/eclipse32.png" />
  20. </library>
  21. <!--
  22. Place an instance of the Application form on Stage
  23. to instantiate it and provide the Natural Entry Point.
  24. -->
  25. <place id="Application" name="app" x="0" y="0" depth="1000" />
  26. </frame>
  27.  
  28. </movie>

Let's break this down:

At the very top, you set up the movie's dimensions and background color as before.

Next, you import your Classes SWF. This file will eventually contain the compiled versions of the classes you will be using in the application.

Next, you start the first frame of the application and define a library there. In it, you create two clips (movie clip symbols).

The first one is an empty movie clip. Its Symbol ID is Application and, using the new class attribute, you are linking it to the Application class in the default package (for the sake of brevity, I did not use a package structure in this sample – in the form org.flashant.naturalEntryPointSample.*) as would be the norm for any real application).

The second movie clip symbol has a Symbol ID of EclipseLogo and is linked to the Particle class in the default package. You are asking Swfmill to import the eclipse32.png found in the library folder of your project and place it in the movie clip.

Finally, you need a way to instantiate your application and to do this, you place an instance of the main Application form (the movie clip with Symbol ID "Application" that is linked to the Application class) on the Stage using the <place> tag. In the tag, you give the Application form an instance name (not really necessary as it will contain all other objects and thus none will refer to it using its instance name) and specify its location and depth.

Now, you need to create the Application and Particle classes and compile them into the Classes SWF.

The Particle class, shown below, has not changed at all from the previous example:

ActionScript:
  1. class Particle extends MovieClip
  2. {
  3. var vX:Number = null;
  4. var vY:Number = null;
  5. var randomness:Number = null;
  6.  
  7. function Particle ()
  8. {
  9. _x = _width + Math.random() * ( Stage.width - _width );
  10. _y = _height + Math.random() * ( Stage.height - _height );
  11. _rotation = Math.random() * 360;
  12. var randomness = Math.random()*5;
  13. vX = Math.random() * randomness + 1;
  14. vY = Math.random() * randomness + 1;
  15. }
  16. function onEnterFrame ()
  17. {
  18. _rotation += 1.69;
  19. _x += vX;
  20. _y += vY;
  21. if ( _x < 0 || _x > ( Stage.width - _width/2 ) )
  22. {
  23. vX *= -1;
  24. _x += 2 * vX;
  25. }
  26. if ( _y < 0 || _y > ( Stage.height - _height/2 ) )
  27. {
  28. vY *= -1;
  29. _y += 2 * vY;
  30. }
  31. }
  32. }

The Application class, however, is considerably different:

ActionScript:
  1. import LuminicBox.Log.*;
  2.  
  3. class Application extends MovieClip
  4. {
  5. var tfCaption:TextField;
  6. // Clips attached dynamically from Swfmill library
  7. var mcSpheres:MovieClip;
  8.  
  9. var sW:Number = null; // Stage width
  10. var sH:Number = null; // Stage height
  11. // Log
  12. var log:Logger;
  13. function Application ()
  14. {
  15. // Setup logging
  16. log = new Logger();
  17. log.addPublisher ( new ConsolePublisher() );
  18. log.info ( "Application::Constructor" );
  19. }
  20.  
  21. function onLoad ()
  22. {
  23. log.info ( "Application::onLoad" );
  24. log.debug ( "this = " + this );
  25.  
  26. // Store stage dimensions for easy look-up
  27. sW = Stage.width - 1;
  28. sH = Stage.height - 1;
  29. // Draw border around the stage
  30. lineStyle ( 1, 0x000000 );
  31. moveTo ( 0, 0 );
  32. lineTo ( sW, 0 );
  33. lineTo ( sW, sH );
  34. lineTo ( 0, sH );
  35. lineTo ( 0, 0 );
  36. //
  37. // Create a message
  38. //
  39. var captionTextFormat = new TextFormat();
  40. captionTextFormat.size = 12;
  41. captionTextFormat.font = "_sans";
  42. var captionText:String = "Swfmill + MTASC Natural Entry Point Sample";
  43. var captionTextExtent:Object = captionTextFormat.getTextExtent ( captionText );
  44. var captionWidth:Number = captionTextExtent.textFieldWidth;
  45. var captionHeight:Number = captionTextExtent.textFieldHeight;
  46. var captionX = sW / 2 - captionWidth / 2;
  47. var captionY = sH - captionHeight;
  48. createTextField( "tfCaption", 10000, captionX, captionY, captionWidth, captionHeight );
  49. // Write caption text
  50. tfCaption.text = captionText;
  51. // Add ten particles
  52. for ( var i = 0; i < 10; i++ )
  53. {
  54. // Attach a sphere clip
  55. attachMovie ("EclipseLogo", "eclipseLogo" + i, 1000 + i );
  56. }
  57. }
  58. }

The main difference is that it no longer has (or needs) a static main() method to provide an entry point. By placing the Application form on Stage using swfml, you have created a natural entry point: Flash will automatically call the Application class' constructor.

In it, you set up the LuminicBox Logger and trace out an info message to signal that the constructor has, in fact, initialized (I've included the latest version of LuminicBox Logger in the files for this tutorial.) The really juicy stuff happens in the onLoad() event handler that gets called automatically in Flash.

In the onLoad() event handler, you first trace out an info message to confirm that the event handler has fired and then trace a debug message to confirm that the movie clip has initialized with the correct scope (things you may feel the need to do when you first try this method, as I did.) Notice that the scope is different from when we were assimilating _root using the Skeletal Injection Method – the Application form is _level0.app not _level0.

The rest of the code in the onLoad() method hasn't changed at all from the version in the previous tutorial. The only difference is that instead of the Particle instances being attached to _level0, they are being attached to _level0.app.

If you use ARP, the open-source pattern-based framework for the Flash Platform, you will no doubt have noticed how directly this applied to ARP application development in Flash and Flex: It uses exactly the same workflow.

Now that your source files are ready, all that remains is to compile them. You are going to compile the classes and create the Classes SWF in one step, using MTASC's -header argument. The -header argument allows you to specify the width, height and frame rate of a SWF and makes MTASC create a SWF with those properties and inject the compiled classes into it. (So you don't have to create an empty skeletal SWF using Swfmill.)

To do this, switch to your project folder in the command line and type the following, replacing the path in the classpath (-cp) with the path to the Flash MX 2004 classes folder on your computer:

mtasc –cp "C:\Documents and Settings\Aral Balkan\Local Settings\Application Data\Macromedia\Flash MX 2004\en\Configuration\Classes" -header 1:1:30 –swf classes.swf Application.as Particle.as

(Note: If you want to use the LuminicBox Logger in your own applications, do not copy an instance of it into each of your project's folders. Instead, keep it in a single location and add another classpath while compiling by adding a second –cp argument.)

Finally, use Swfmill to compile your main application SWF by entering:

swfmill simple application.xml application.swf

Run the application.swf and you should see the animated particles.

Pros: Cons:

I definitely prefer the Natural Entry Point Method to the Skeletal Injection Method and will be using it in my own projects. On the whole, it is simpler, more flexible and the workflow is identical to that used in an ARP project (although you can also use the Skeletal Injection Method with ARP projects as long as you either assimilate _root or, better yet, assimilate an application movie clip that you've placed on the _root timeline using Swfmill.)

Regardless of whether you use the Natural Entry Point or Skeletal Injection methods, the combination of Swfmill and MTASC offer a world of possibilities for Flash developers and for that I am eternally thankful to Nicolas Cannasse, Daniel Fischer and Mark Winterhalder – thanks guys for making all this possible :)

Download the source files (NaturalEntryPointExample.zip; 106kb)

Comments