Part 1 of this walkthrough ended off with a flash animation linked up to a main application class that would allow regular updates of that application. This time around we'll see how to access objects in your application added in Flash from the library. Once these are in place, your artists can go nuts customizing them, as long as the basic setup stays the same.
The Menu and Button classes
Let's set up a simple menu class that will allow us to continue to gameplay, or quit the animation. In Flashdevelop, create a new class in your package (right-click the package, Add->New Class) called Menu, specifying MovieClip as it's base class. Add two MovieClip members to it for the buttons, called playBtn and quitBtn:
1 2 3 4 5 6 7 8 9 10 | /** * Play button. */ var playBtn:MovieClip;
/** * Quit button. */ var quitBtn:MovieClip;
|
As with the App class, add basic singleton functionality, but this time initialize the singleton in the menu constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * The Menu instance. */ private static var instance:Menu;
/** * Singleton accessor. */ public static function getInstance():Menu { return instance; }
/** * Creates a new Menu and sets the singleton pointer. */ public function Menu() { instance = this; }
|
Note I'm not clearing the singleton pointer at any stage, but it will be replaced the next time an instance is created, releasing the reference to the last object and allowing it to be garbage collected. This isn't a singleton in the purest sense, but it does allow us global access to the most current instance. Actionscript doesn't have destructors and I'm yet to find a replacement that I'm 100% happy with.
Finally, add a function onButtonReleaseHandler to handle button presses, and implement the (extremely simple) menu logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /** * Handles an onRelease event. * @param source The MovieClip that triggered the onRelease event. */ public function onButtonReleaseHandler(source:MovieClip):Void { switch(source) { case playBtn: App.getInstance().playGame(); break; case quitBtn: fscommand ("quit"); break; } }
|
You'll also need to import the App class at the top of the file, using it's fully qualified name:
1 | import us.benic.matt.App; |
Note: I've switched on the actual source object here. Actionscript's switch statement is extremely flexible, unlike that in C++ and Java, any object can be used as a case, including strings.
Here is the completed Menu class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | import us.benic.matt.App;
/** * Menu for the Flash Workflow demonstration. * @author Matt Benic */ class us.benic.matt.Menu extends MovieClip { /** * Play button. */ var playBtn:MovieClip;
/** * Quit button. */ var quitBtn:MovieClip;
/** * The Menu instance. */ private static var instance:Menu;
/** * Singleton accessor. */ public static function getInstance():Menu { return instance; }
/** * Creates a new Menu and sets the singleton pointer. */ public function Menu() { instance = this; }
/** * Handles an onRelease event. * @param source The MovieClip that triggered the onRelease event. */ public function onButtonReleaseHandler(source:MovieClip):Void { switch(source) { case playBtn: App.getInstance().playGame(); break; case quitBtn: fscommand ("quit"); break; } } } |
Now we need a simpleMovieClip subclass called Button that will notify the menu when it is pressed. Flash does have it's own Button class, but it's really inflexible in that Button components can't use a custom Button subclass. The norm seems to be to just use a custom MovieClip subclass, and the functionality in the Flash Button class can easily be implemented anyway. For our purposes, a simple class that calls the Menu singleton's onButtonReleaseHandler when it is released will suffice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import us.benic.matt.Menu;
/** * Button for the Flash Workflow demonstration. * @author Matt Benic */ class us.benic.matt.Button extends MovieClip { /** * Creates a new button and sets it's onRelease to notify the Menu instance. */ public function Button() { // Notify menu instance when released onRelease = function ():Void { Menu.getInstance().onButtonReleaseHandler(this); } }
} |
Changes to the App class
At this point very little needs to be added to the App class, it just needs the function the Menu class called, playGame. This will move the main clip to the Level frame:
1 2 3 4 5 6 7 8 | /** * Play the game. */ public function playGame():Void { gotoAndStop("Level"); }
|
You may also want to remove the trace call in onEnterFrameHandler. It's demonstrative purpose has been served, and outputting text on every frame will just slow down the animation.
The Menu and Button MovieClips
Now we need the actual visual elements used for the menu. First create a new MovieClip in the library (Ctrl+L or Windows-Library to open the library, right click in the library->New Symbol...) called
PlayButton. Set it's
Type to
MovieClip, check the
Export for ActionScript checkbox, and specify it's
Class as your fully qualified
Button class's name (ie including the package):
Clicking Ok to dismiss the Create New Symbol dialog causes Flash to open that symbol for editing. Use the flash tools to create a simple button. Eg add a Rectangle for a background, and a TextElement for the text to end up with something similar to this:
Copy that symbol as a starting point for the Quit button, right click the symbol in the Library and select
Duplicate. In the
Duplicate Symbol dialog name the symbol
QuitButton and as before enable
Export for ActionScript and set it's class to your
Button class:
Again, dismissing the dialog with Ok opens the component on the stage. Edit it to say Quit instead of Play.
Now create another new Symbol, this time calling it
Menu and setting it's class to your
Menu class:
Drag the
PlayButton symbol from the Library to the stage, and then do the same with the
QuitButton. The result should look something like:
The buttons now need to be named to match the members added to the
Menu class earlier. To do this, click on a button on the stage to select it (
not it's template control in the library), open the
Properties window with
Ctrl+F3 or
Windows->Properties and replace the
<instance name> text in the topmost textbox with the appropriate name. Your
Play button should be called
playBtn and your
Quit button
quitBtn:
Putting it all together
The last thing to do is add your newly created menu to the appropriate frame at the top level of the timeline. Return to the top level by clicking on
Scene 1 above the stage. In the timeline, on the
Objects layer, add new keyframes (right click->Insert Keyframe) below the
Menu and
Level labels (should be frames 10 and 20 respectively). Now with the new keyframe under
Menu selected, drag the
Menu symbol from the library onto the stage. Frames 10 to 20 on the
Objects layer should now be grayed out to indicate content exists on those frames.
If you now run the animation with Ctrl+Enter, the menu will be displayed, and clicking Play will take you to the Play frame (which is currently empty). The Quit button may appear to do nothing, but that is just because fscommand ("quit") has no effect when run from the Flash environment. When the generated .swf file is run by double clicking it in an explorer window, the effect is as expected.
The full project so far is attached to the post. That's it for part 2, the next post will go into adding and manipulating objects programmatically.
A note on variables linked to stage objects
In this example, I used variables within a class who's only link to visual objects is their name. That's scary. Actionscript won't tell you if one of these objects is missing, it will just carry on as if nothing is wrong. You can't even expect an error or exception when you call a method on one of these nonexistent member objects. That's terrifying. Now you may be tempted to insist on creating all objects yourself, programmatically, but that's once again taking a certain amount of creative control (placement) away from the artists, which is kind of the whole point of Flash. The only way to be 100% sure objects you expect to be there are actually there is to check them against null and handle the missing case by throwing an exception/error or making the mistake known some other way.
Related to this is the interesting little issue that numeric values in flash are represented by the Number class, as opposed to some kind of integral type, and must be properly initialized like any other object or will be null. This can catch you by surprise with some really wierd behavior, so if you start seeing NaN's in output, there's an uninitialized Number somewhere.
Posted via email from Matt's thoughts