Chapter 3. Looking at the Changes in ActionScript 3.0

The documentation for Flash CS4 Professional includes a tremendously useful table titled “ActionScript 2.0 Migration.” An introductory caption humbly states, “The following table describes the differences between ActionScript 2.0 and 3.0,” which leads to a catalog so lengthy, it would fill over 50 pages if reproduced in this book. To locate this document, look in the appendixes of the ActionScript 3.0 Language and Components Reference or search the term “migration” in the Help panel. This document is also available on the Adobe online Help Resource Center:

http://help.adobe.com/en_US/AS3LCR/Flash_10.0/migration.html

This chapter will help you find your bearings from a migration standpoint, and navigate among these changes.

Major Changes and Obsolete Code

As programming languages evolve, existing workflows may change, new features are usually added, and older features are sometimes removed. This is as true for ActionScript as it is for Java, C#, Python, PHP, and countless others. In the company of programmers at large, you’re not alone. The changes in ActionScript 3.0 may seem startlingly plentiful, but historically speaking, Flash has been through this sort of remodeling before. Developers encountered a similar paradigm shift when Macromedia Flash 5 introduced the language that, for clarity, was later renamed ActionScript 1.0. The original naming scheme didn’t include version numbering, and was therefore referred to simply as “ActionScript.” This was true even in Flash 4, which featured a fundamentally different syntax in which objects were referenced by a relatively uncommon mechanism called slash notation. The dot notation syntax of ActionScript 1.0, in which nested objects are distinguished from one another by a dot (.), was a major step toward making the language more accessible to developers from other platforms. ActionScript 2.0 established a formalized structure for custom classes and, in many ways, introduced a transitional period, in which many of the current recommended best practices found a whispered beginning. ActionScript 3.0 expands and refines this formal class structure, while continuing to extend a welcoming hand to traditional timeline programmers. The pithy description by Adobe’s Kevin Lynch is apt: think of ActionScript 3.0 as evolutionary, not revolutionary.

So yes, there are changes in the language. The good news is that they’re designed to increase performance in Flash Player 9, 10, and future runtimes. Even better, the changes are designed to help you stay more organized.

Major Changes in the API, Classes, and Language

One of the first things Flash developers often notice in ActionScript 3.0 is the absence of underscores. Familiar MovieClip properties like _x, _y, _width, and _height are now referenced simply as x, y, width, and height. This is a bit of a jolt at first, but easy enough to remember with a mindset for change and authoring tool assistance like code completion. It was only the original property set, anyway, that featured the underscore prefixes. More recent ActionScript 1.0 and 2.0 properties, such as blendMode and cacheAsBitmap (introduced in Flash Player 8), had already dropped the underscores, so developers generally welcome the consistency offered by ActionScript 3.0. Values that used to range in integers from 0 to 100, such as MovieClip._alpha and Sound.setVolume(), now range in decimal values from 0 to 1. Even tweaks like these are straightforward enough, and arguably more cosmetic than anything else. The profound change comes in the very nature in which ActionScript 3.0 is organized. This understructure has shifted significantly, and insists on greater attention to detail.

ActionScript has historically been a very forgiving language. In some ways, you can draw a comparison between older versions of ActionScript and older versions of HTML. In the early days of web development, HTML was deceptively unfussy. Styling was handled with straightforward <font> tags, which all too often became a redundant jumble. Closing </p> tags were optional, nested tags could be closed out of sequence from how they were opened, and dozens of other lenient practices led—or had the tendency to lead—to overtime headaches. Popular websites like The Web Standards Project (http://www.webstandards.org/) and CSS Zen Garden (http://www.csszengarden.com/) have since sparked a surge of interest in a practice called semantic markup, in which great care is taken to cleanly separate styling and formatting from content. This separation usually relies on XHTML specifications, which are considerably stricter than HTML, and coupled with Cascading Style Sheets (CSS). Ironic as it may seem, adherence to a stricter standard has gradually made things easier for web developers. It’s a bit like the idea that picking up after yourself throughout the day saves you from facing an overwhelming mess at the end of the week. ActionScript 3.0 is more disciplined than its predecessors in a similarly helpful way.

ActionScript 3.0 is stricter

As an example of ActionScript 3.0’s strictness, consider one of programming’s most basic building blocks: variables. Since its introduction in ActionScript 1.0, the var keyword has been optional (but always recommended!) for timeline code. The following lines work just fine in a FLA file configured for ActionScript 1.0 or 2.0 and placed in a frame script:

lumps = 2;
trace("I'll have " + lumps + " lumps, please.");
// Displays: I'll have 2 lumps, please.

Ideally, this variable should be declared with the var keyword:

var lumps = 2;

but before ActionScript 3.0, the compiler can (and does) declare lumps automatically. While convenient on one level, this relaxed approach can lead to unexpected behavior. How? The var keyword does more than merely announce new arrivals; it defines variables in terms of a specific scope, which, in a few words, determines a variable’s “point of view,” its availability to other objects. Take a look at this revision (changes in bold):

function sample() {
    lumps = 2;
    trace(lumps); // Displays: 2
}
sample();

trace("I'll have " + lumps + " lumps, please.");
// Displays: I'll have 2 lumps, please.

Here, the lumps variable is declared and traced inside a custom sample() function, which is immediately called after the function is defined. This is followed by a second trace() statement outside the function that also references lumps. Because of the omission of the var keyword, both traces are successful, even if the developer’s intention is to keep this variable corralled to its function. Add a second function, and all three scopes can still see the variable:

function sample() {
    lumps = 2;
    trace(lumps); // Displays: 2
}
sample();

trace("I'll have " + lumps + " lumps, please.");
// Displays: I'll have 2 lumps, please.

function test() {
    trace(lumps); // Displays: 2
}
test();

This sort of spillover can be detrimental in cases where typical naming conventions overlap among numerous functions. For example, if a temporary string, str, is used to manipulate data in one function, havoc could ensue in another if the same variable name is used elsewhere. Think of how often n, i, or x are used to represent numeric values!

You’re much better off purposefully declaring a variable in its intended scope. If spillover happens to be the desired effect, it’s still possible ... it just depends on where the variable’s scope occurs. Note the output differences in these revisions. In the first example, the variable is only available to one function; in the second, the variable is available to two functions and the timeline. First, when lumps is scoped to the sample() function:

function sample() {
    var lumps = 2;
    trace(lumps); // Displays: 2
}
sample();

trace("I'll have " + lumps + " lumps, please.");
// Displays: I'll have undefined lumps, please.

function test() {
    trace(lumps); // Displays: undefined
}
test();

Second, when it’s scoped to the main timeline:

var lumps = 2;

function sample() {
    trace(lumps); // Displays: 2
}
sample();

trace("I'll have " + lumps + " lumps, please.");
// Displays: I'll have 2 lumps, please.

function test() {
    trace(lumps); // Displays: 2
}
test();

To underscore the notion of ActionScript 2.0 as a transitional language, consider that undeclared variables aren’t allowed in classes, even if they do sneak by in timeline code. When compiled, this no-frills ActionScript 2.0 class generates a compiler error, “There is no property with the name ‘lumps’.”:

class Sample {
    public function Sample() {
        lumps = 2;
        trace("I'll have " + lumps + " lumps, please.");
    }
}

You can address this error by preceding the lumps variable with the var keyword, which scopes it to the class’s constructor function, Sample(), or by declaring the variable as a class property, which scopes it to the whole class, available to any method:

class Sample {
    private var lumps:Number;
    public function Sample() {
        lumps = 2;
        trace("I'll have " + lumps + " lumps, please.");
        demo();
    }
    private function demo():Void {
        trace(lumps); // Also available here
    }
}

Note

Class constructor functions must not define a return data type, which explains the omission of :Void after Sample(), but the presence of :Void after demo().

In ActionScript 3.0, FLA files, as a whole, are associated with something called a document class, which defines the main timeline’s functionality. While you may optionally write your own document class, you certainly don’t have to. By default, the compiler automatically generates one for you. This default document class is called MainTimeline and extends the MovieClip class. On the surface, nothing has changed. The main timeline is still a movie clip, as it always has been, but under the hood, a new structure is in place. The main timeline is now defined by a class, which means that even timeline variables must be formally declared, just as they were in ActionScript 2.0 classes.

Note

The main timeline can also extend the Sprite class, if you like. For more information on document classes, see Chapter 7.

In an ActionScript 3.0 FLA file, the following, now-familiar keyframe script generates the compiler error “1120: Access of undefined property lumps” because of the missing var keyword:

lumps = 2;
trace("I'll have " + lumps + " lumps, please.");

Adding var corrects the situation:

var lumps = 2;
trace("I'll have " + lumps + " lumps, please.");

Holding to a stricter standard encourages you to give more thought to the code you write. Variables are just the beginning.

ActionScript 3.0 encourages programming with purpose

In ActionScript 2.0, all classes and functions in the Flash Player API were global. These classes were aimed specifically at functionality provided by the Flash Player runtime, over and above the core functionality outlined in the ECMAScript specification. In this free-for-all, if you wanted to refer to the MovieClip class in your code, you could do so without using an import directive, even inside a custom class. An obvious benefit is that you could save a bit of typing:

import MovieClip; // This line is not needed,
                  // because the MovieClip class
                  // is already understood
var mc:MovieClip = this.createEmptyMovieClip("myClip", 0);

Note

The import directive lets the compiler know which class definition to use for interpreting the code you’ve written. Even in ActionScript 2.0, import was necessary for classes in packages like flash.filters, flash.geom, and flash.external, and was often necessary for custom classes.

In ActionScript 3.0, only core classes are considered global, in the sense that they belong to the top level of the overhauled packages hierarchy. Packages are an organizational means of arranging classes into groups, usually based on similar functionality. The lion’s share of ActionScript 3.0 classes is now arranged into such packages. The remainder, global functions and core classes, is listed under the All Packages→Top Level classes topic in the ActionScript 3.0 Language and Components Reference.

As with ActionScript 2.0, top-level classes don’t require the import directive—either in timeline code or custom classes—but in contrast to how it used to be, MovieClip is now categorized under flash.display.MovieClip, and must be imported when used in external class files:

package {
    public class SampleClass {
        import flash.display.MovieClip;
        public function SampleClass() {
            var mc:MovieClip = new MovieClip();
            // Additional code here ...
        }
    }
}

In frame scripts, classes in the flash package join top-level classes in not requiring an import compiler directive. This line, for example, works just fine on its own in an ActionScript 3.0 frame script:

var mc:MovieClip = new MovieClip();

The concepts of packages and importing are discussed in greater detail in the section Major Syntax and Structure Changes in this chapter. The subject is worth touching on at this point, however, because it helps prepare you for the massive organizational shift you’ll find in the documentation’s ActionScript 2.0 Migration table. This new structure’s benefit may not be self evident, but it does reinforce a developer’s motivation to program with purpose.

Here’s an example of how the new packages arrangement can lead to better focus. Before ActionScript 3.0, you had numerous ways to load external assets into a SWF file. The loadMovie() function was among the first and is still in surprisingly wide use in ActionScript 2.0, as indicated by Adobe support forum questions. In the beginning, this function was capable only of loading external SWF files, but this changed as successive versions of Flash Player added support for other dynamically loadable file types, including JPEG, GIF, and PNG. Note that a hint of potential confusion has already raised its head for newcomers: this function, loadMovie(), expressly alludes to a “movie.” This is a term commonly used to describe SWF files, but doesn’t suggest support for images, even though loadMovie() easily loads image files in recent versions of Flash Player. ActionScript 3.0 helps clear up such pitfalls in semantics.

Consider the following code exercises from the standpoint of an evolutionary journey—a journey that, comparatively speaking, begins in somewhat ambiguous terms and develops into ActionScript that more clearly states its purpose.

  1. In a new ActionScript 2.0 FLA file, select frame 1 in the Timeline, and then open the Actions panel. Type the following minimal ActionScript:

    loadMovie("sample.png", container);

    Create a new movie clip symbol and position it on the stage. Using the Property inspector, give the new symbol the instance name container. Put a PNG file named sample.png into the same folder as the FLA file, and then select Control→Test Movie to see the image appear on the stage at runtime. On its own, this code is all you need to load an image prior to ActionScript 3.0, but it doesn’t provide any data on load progress or completion. If you want to display load progress or reposition the image when loading completes, then you have to set up a loop of some sort to continuously compare the number of loaded bytes against the total number of bytes the image contains.

  2. Using the Actions panel, enter these additional lines of code:

    var timer:Number = setInterval(checkProgress, 50);
    function checkProgress():Void {
        container._visible = false;
        var loaded:Number  = container.getBytesLoaded();
        var total:Number   = container.getBytesTotal();
        var percent:Number = Math.round(loaded/total * 100);
        trace(percent);
        if (percent == 100 && container._width > 0) {
            clearInterval(timer);
            container._x = Stage.width / 2  - ¬
                 container._width / 2;
            container._y = Stage.height / 2 - ¬
                 container._height / 2;
            container._visible = true;
        }
    }

    Here, setInterval() repeatedly executes a custom checkProgress() function every 50 milliseconds. This function turns off the visibility of container, so that it doesn’t seem to jump when later repositioned, and then declares and sets the values of three variables. The first two, loaded and total, are taken directly from the getBytesLoaded() and getBytesTotal() methods of the MovieClip class, as invoked on container. The third, percent, pits the previous values against each other to derive a percentage, which could potentially be routed to a text field. Here, the value is traced to the Output panel.

    Finally, an if statement checks if percent is equal to 100. As a safety backup, it also checks to see if container has a width greater than 0. The second condition is present because of a timing issue. In this particular solution, percent might actually reach 100 before the image is displayed, even if only by a few milliseconds. That could be enough to throw off the repositioning code, because until the image shows up, container has a default width of 0. When both of these conditions are met, clearInterval() exits the setInterval() loop, centers container to the stage, and then turns its visibility on.

    Go ahead and test this revision. In the menu bar of the resultant SWF file, select View→Simulate Download to imitate the loading at a slower pace. The Output panel displays a mounting percentage, which leads to the centered image at 100 percent.

    So far, you’ve seen one approach out of many possible solutions. While it makes reasonable sense when explained, the loadMovie()setInterval() combination isn’t as graceful as something that relies on event handlers. Introduced with ActionScript 2.0, the MovieClipLoader class transforms this loading process into something more like a conversation. After an instance of the MovieClipLoader class is created, it is “spoken to” by way of the loadClip() method and then “listened to” by way of the onLoadProgress and onLoadInit event handlers.

  3. Delete the existing code in your FLA file and replace it altogether with the following new ActionScript 2.0:

    var mcl:MovieClipLoader = new MovieClipLoader();
    mcl.loadClip("sample.png", container);
    
    var listener:Object = new Object();
    listener.onLoadProgress = progressHandler;
    listener.onLoadInit = loadInitHandler;
    mcl.addListener(listener);
    
    function progressHandler(mc:MovieClip, loaded:Number, ¬
        total:Number):Void {
        var percent:Number = Math.round(loaded/total * 100);
        trace(percent);
    }
    function loadInitHandler(mc:MovieClip):Void {
        mc._x = Stage.width / 2  - mc._width / 2;
        mc._y = Stage.height / 2 - mc._height / 2;
    }

    The percentage calculation and repositioning portions are identical in principle to the previous version. The difference comes in the way these portions are now carried out. In this case, the instruction to load is given to an instance of the MovieClipLoader class (mcl), which manages the necessary looping internally. This action removes a bit of clutter because it sidesteps the need for something like setInterval(). Now that the timing issue has been remedied, you no longer need to temporarily hide the container symbol and reveal it later.

    To manage the events, an arbitrarily named variable, listener, is declared and set to an instance of the Object class. This simple object acts on behalf of mcl, pairing up functions with the onLoadInit and onLoadProgress events of the MovieClipLoader class. These functions receive parameters automatically, which progressHandler() uses to determine percentage and repositioning values.

    Note

    As mentioned in Chapter 1, earlier versions of the language had at least five ways to handle events. In ActionScript 3.0, these five have been consolidated into a single streamlined approach (with a few minor exceptions). For more information, see the DOM3 Event Model of Chapter 1; the ActionScript Can No Longer Be Attached to Objects of Chapter 6; and the practical examples in Part IV of this book.

  4. Select Control→Test Movie to verify that the SWF file behaves the same as before, including the View→Simulate Download exercise.

    This revision arranges the endeavor into coherent, simplified steps, where separate functions perform the sub-goals of percentage reporting and repositioning. Bear in mind, the previous all-in-one function, checkProgress(), is perfectly valid. From a nuts-and-bolts technical standpoint, neither approach is superior, but if support forum questions are any reflection of common workday scenarios, many developers take a copy-and-paste approach to learning. They do what it takes to get the job done and, under hectic schedules, acquire knowledge as time allows. When solutions come in single blocks of code, the underlying principles can be harder to digest.

    ActionScript 3.0 tightens up the benefits initiated by the MovieClipLoader class, reinforcing the theme of programming with purpose. For starters, the loading mechanism is now defined by the Loader class, which drops the seemingly “movie”-specific bias of previous functions and classes. At this point, neither the listener object nor the container movie clip is needed.

  5. So it’s time to change gears. In a new ActionScript 3.0 FLA file, type the following code into a frame script in frame 1:

    var myLoader:Loader = new Loader();
    myLoader.load(new URLRequest("sample.png"));
    addChild(myLoader);
    
    myLoader.contentLoaderInfo.addEventListener(
        ProgressEvent.PROGRESS,
        progressHandler
    );
    myLoader.contentLoaderInfo.addEventListener(
        Event.COMPLETE,
        completeHandler
    );
    
    function progressHandler(evt:ProgressEvent):void {
        var loaded:int  = evt.bytesLoaded;
        var total:int   = evt.bytesTotal;
        var percent:int = Math.round(loaded / total * 100);
        trace(percent);
    }
    function completeHandler(evt:Event):void {
        myLoader.x = stage.stageWidth / 2  - ¬
             myLoader.width / 2;
        myLoader.y = stage.stageHeight / 2 - ¬
             myLoader.height / 2;
    }

    Once again, the percentage calculation and repositioning portions are nearly the same. In this updated version, a variable myLoader is declared and set to an instance of the Loader class, which is capable of loading SWF files and image files (JPEGs, GIFs, and PNGs). Note that in this case, the file path to sample.png isn’t merely a string, as before. In this case, it’s an instance of the URLRequest class. In addition, the event handlers are associated not with myLoader itself, but with a contentLoaderInfo property of that object.

    These objects are certainly new, presumably useful, and possibly overwhelming. But what exactly are they? Clutter? Not a bit of it! Flash has always been a creative toolbox. ActionScript 3.0 has tidied up the toolbox and put labels next to each tool. You’ll learn more about this rigorously organized new arrangement in the very next section.

  6. Select Control→Test Movie to see that the SWF file behaves the same as before. In the file menu of the SWF file, select View→Simulate Download to test the percentage output.

ActionScript 3.0 is more organized, which makes it more efficient

On the face of it, the URLRequest class, seen in the previous example, acts as nothing more than a container for storing file locations. It seems to be a five-dollar way of saying “sample.png,” much like “salutations” is a five-dollar way of saying “hello.” So what’s the point? Is URLRequest really necessary? What was wrong with the simple string approach of earlier functions and classes? To answer these questions, think again of the overhauled ActionScript 3.0 packages structure.

Before ActionScript 3.0, the MovieClip class supported a loadMovie() method, which was practically equal in purpose to the standalone loadMovie() function. Wait a minute! Were there formerly two versions of loadMovie()? There were. This sort of redundancy was frequent in older ActionScript. There were also two versions of gotoAndPlay()—both function and method—and many others, besides. This duplication was introduced in Flash 5, when the MovieClip class began taking ownership of movie-clip–related functionality. Longstanding functions became MovieClip methods overnight, yet the function versions remained for backward compatibility.

The trouble is, this duplication sometimes went too far. The loadMovie() method, especially, is a case in point. Because the MovieClip class defines movie clip objects, these objects should certainly be able to do the things a movie clip symbol can do: display animated timelines, move around the stage, change dimensions, and so on—but the act of loading is a categorically distinct discipline.

It makes good sense to coordinate the traits and functionality of loading into an object that specializes in the field, so to speak. In ActionScript 3.0, precisely this sort of thoughtful arrangement has occurred. As a subject matter expert on loading, the Loader class should indeed feature an impressive array of loading related skills. In this light, it’s not surprising that Loader should work in collaboration with a subject matter expert on HTTP requests, which is what the URLRequest class is. More than just a fancy way of describing file locations, URLRequest objects have the potential to manage an HTTP request’s header, its method (GET versus POST), its POST data, MIME content type, and so on.

This sort of rich granularity is echoed throughout the ActionScript 3.0 API. For example, in the previous code exercise, the event handler was associated with the Loader.contentLoaderInfo property of the myLoader instance. As it happens, this property points to an instance of yet another class, LoaderInfo, which specifically manages byte data and other information about SWF files and image files. This class’s skill set paves the way for the bytesLoaded and bytesTotal properties used by the progress event handler. Again, each step is categorized neatly.

This sort of approach wasn’t unheard of, by the way, in older versions of ActionScript. It just wasn’t as prevalent. The TextFormat class, for example, compartmentalizes formatting from the text fields it collaborates with (relevant code in bold).

// ActionScript 2.0
var tf:TextField = this.createTextField("sampleText", ¬
     0, 10, 50, 100, 20);
tf.selectable = false;
tf.autoSize = "center";

var styling:TextFormat = new TextFormat();
styling.font = "Blackadder";
styling.color = 0xBA1424;
styling.letterSpacing = 1.5;

tf.setNewTextFormat(styling);
tf.text = "Cooperation!";

For good measure, here are another two examples that show how ActionScript 3.0 expands on this sort of helpful compartmentalization.

In ActionScript 1.0 and 2.0, the MovieClip class featured a handful of methods collectively known as the Drawing API. You could reference a movie clip directly by its instance name and invoke, say, lineTo() and curveTo() to draw shapes at runtime. In ActionScript 3.0, this same Drawing API has been reallocated to a more suitable Graphics class, which is now associated with movie clips by way of the MovieClip.graphics property:

// ActionScript 2.0
myClip.lineTo(300, 200);

// ActionScript 3.0
myClip.graphics.lineTo(300, 200);

In ActionScript 3.0, the Sound class collaborates with three new classes—SoundChannel, SoundTransform, and SoundMixer—to manage audio-related functionality. These duties were previously consigned to the Sound class alone. Previously, the concept of sound channels was managed by a non-intuitive association between a Sound instance and a movie clip. In order to separate audio into individual “channels,” you had to feed individual movie clip instance names to each use of the new Sound() constructor. It was an easy procedure to miss, and developers often wondered why adjusting the volume of one Sound instance affected the volumes of others. Now, the improved, decentralized functionality calls on specialized companion classes as needed.

// ActionScript 2.0
var mySound:Sound = new Sound();
mySound.loadSound("music.mp3", true);
mySound.setVolume(50);
mySound.stop();

// ActionScript 3.0
var mySound:Sound = new Sound();
mySound.load(new URLRequest("music.mp3"));

var myChannel:SoundChannel = mySound.play();
var myTransform:SoundTransform = myChannel.soundTransform;

myTransform.volume = 0.5;
myChannel.soundTransform = myTransform;
myChannel.stop();

By delegating functionality to numerous classes, the new API keeps its objects lean and focused. MovieClip instances are no longer burdened with loading tasks or the Drawing API, but all the same, are easily associated with companion classes that handle those duties. The concept goes even further: if you want some of the basic characteristics of a movie clip but don’t need internal animation—that is, if you don’t need to shuttle around a movie clip’s playhead with gotoAndPlay()—then you now have the option of using the Sprite class instead, which doesn’t carry the overhead of a timeline. Ultimately, this makes your tools more refined, giving you functionality that suits the object at hand, and leaving the extra tasks to other objects.

Obsolete Code

Waking up in a hotel room can sometimes be disorienting. You might reach for your glasses or a cup of water that, at home, is always right where you expect: at pillow height on the nightstand. Of course, hotel rooms are temporary. Soon enough, a red-eye flight takes you back to your humble abode, where the comforts of familiarity snuggle their way back into your daily routine.

No so with obsolete code! Unless you’re involved specifically with legacy systems, where you know users are locked in to an older version of Flash Player, you’ll have to leave certain once-familiar paradigms in the dust. Some of these ways have been deprecated for many versions of Flash, which means the term in question was officially frowned upon at some point because it was likely to be removed in the future. With ActionScript 3.0, that theoretical future has finally arrived. The ActionScript 2.0 Migration table provides an exhaustive list of features that ActionScript 3.0 no longer supports, but the following collection provides a summary of many common—yet no longer usable—practices.

on()/onClipEvent()

It is no longer possible to attach event handlers directly to objects, such as movie clips, buttons, and components. This is a significant change, because on() and onClipEvent() have been popular for years. Using direct attachment, you could previously program a button to respond to a mouse click, for example, by selecting the button symbol on the stage, opening the Actions panel, and then typing something like this:

on (release) {
  // Desired code here
}

This was optional as recently as ActionScript 2.0—an alternate approach to referencing event handlers by instance name. In ActionScript 3.0, the object in question must have an instance name, which is what uniquely identifies that symbol or component as something ActionScript can speak to. In contrast, direct attachment didn’t require instance names because the intended recipient of your instruction was self-evident.

You can supply an instance name to an object by selecting it on the stage, and then typing the instance name into the Property inspector. Assuming an instance name myButton, here’s how ActionScript 3.0 associates the occurrence of a mouse release with a function to be trigged by that occurrence:

myButton.addEventListener(MouseEvent.MOUSE_UP, function);

If you think of the Timeline as a grid, this code appears in a frame script that aligns vertically in the same “column”—the same frame—as the button it refers to. The term function in the previous line of code refers to an actual function definition, such as the following arbitrarily named mouseUpHandler():

myButton.addEventListener(MouseEvent.MOUSE_UP, ¬
    mouseUpHandler);

function mouseUpHandler(evt:MouseEvent):void {
    // Desired code here
}

The evt parameter refers to an instance of the MouseEvent class, which features numerous useful properties you can optionally reference inside the function. To find out what events are available for a button symbol, look up the SimpleButton class in the ActionScript 3.0 Language and Components Reference. Click the “Show Inherited Events” hyperlink in the Events section, and take your pick. One of these is mouseUp, and if you click on that, the Help panel shows that the mouseUp event belongs to the MouseEvent class and is referenced with the MouseEvent.MOUSE_UP constant. (A constant is simply a variable whose value doesn’t change. Many classes store properties and events as constants in this way. By using the constant, instead of the string “mouseUp”, you gain the benefit of code coloring to show you’ve entered the right code.) In the same way, the MovieClip class entry indicates what events are available for movie clips, the ComboBox class shows events for the ComboBox component, etc.

Note

The practical examples in Part IV of this book go into greater detail on event handling, including keyboard events (responding to keystrokes) and optional aspects like event bubbling.

getProperty()/setProperty()/tellTarget()

These functions still show up in hundreds of online tutorials, but they’re no longer supported. Ever since ActionScript 1.0, their purpose has been simplified by dot notation. Consider a movie clip symbol with the instance name myClip. To set its width using setProperty(), you would refer to the instance name like this:

setProperty(myClip, _width, 200);

The updated approach is much easier on the eye (note the change from _width to width):

myClip.width = 200;

attachMovie()/attachSound()

The procedure for pulling assets from the library at runtime has changed. It still requires linkage information, but instead of a linkage identifier, ActionScript 3.0 requires a linkage class, which is designated by the same Symbol Properties dialog box you’re used to. Right-click (Ctrl-click) on an asset in the library, and then choose Properties. When the dialog box opens, click the Advanced button if it’s showing. This expands the Symbol Properties dialog box to its full extent. Select Export for ActionScript, and then enter a name into the Class field, as shown in Figure 3-1 (note that the Identifier field is disabled).

Rather than invoking attachMovie() or attachSound() on a related MovieClip or Sound instance, the library asset is attached by way of the new operator:

var mc:myClip = new myClip();

Visual objects, like movie clips and graphics, are then added to the display list, which manages a SWF file’s visual objects:

addChild(mc);

Note

For more information on this process, see Chapter 8, and the practical examples in Part IV of this book.

Specifying linkage properties
Figure 3-1. Specifying linkage properties

createEmptyMovieClip()/duplicateMovieClip()/createTextField()

In similar fashion, the MovieClip and TextField classes can now be instantiated directly with the new operator. In both cases, the resultant objects must be added to the display list.

// ActionScript 2.0
var mc:MovieClip = this.createEmptyMovieClip("myClip", 0);

// ActionScript 3.0
var mc:MovieClip = new MovieClip();
mc.name = "myClip"; // traditional instance name
addChild(mc);

In the ActionScript 2.0 version, the MovieClip.createEmptyMovieClip() method is invoked on a timeline with the global this property, but that could be replaced with any valid movie clip reference, which would then become the immediate parent of the new MovieClip instance. The Property-inspector–style instance name (the string "myClip") is a required parameter, as is the second parameter, depth, which here happens to be 0 (the lowest depth). Because createEmptyMovieClip() returns a movie clip reference, the new instance can be referred to in subsequent code either by the myClip instance name or the mc variable.

In the ActionScript 3.0 version, depth is handled automatically (no depth parameter is required) and the Property-inspector–style instance name is optional, as the new object can, in any case, be referenced by the mc variable.

eval()

The eval() function crops up often in legacy code and in many online tutorials. In older ActionScript, it was used to evaluate expressions as variables, properties, or objects. When a variable or property name was evaluated, its value was returned. When an object name or reference was evaluated, a new reference to that object was returned. One typical use of eval() was to iterate through sequentially named movie clip instances using a for loop. Here, three movie clips with the instance names mc0, mc1, and mc2 are conveniently set to a horizontal position of 200 all at once:

// ActionScript 2.0
for (var i:Number = 0; i < 3; i++) {
    eval("mc" + i)._x = 200;
}

While eval() is no longer available in ActionScript 3.0, the bracket notation approach to the same task, using the array access operator ([]), still works:

// ActionScript 3.0
for (var i:int = 0; i < 3; i++) {
    this["mc" + i].x = 200;
}

Bracket notation requires that an object reference precede the array access operator. In this case, the object reference is this, which refers to the timeline in which these movie clips appear. If the three movie clips were nested inside the timeline of another movie clip with the instance name container, then the same ActionScript 3.0 for loop would look like this:

for (var i:int = 0; i < 3; i++) {
    container["mc" + i].x = 200;
}

You can iterate through movie clips with an object reference, in which a variable points to a given MovieClip instance, or by Property-inspector–style instance name (that is, the MovieClip._name property in ActionScript 2.0 and the MovieClip.name property in ActionScript 3.0). In ActionScript 3.0, array access operator iteration through MovieClip.name property values only succeeds when those instance names are provided by hand using the Property inspector. The MovieClip.name property indicates the movie clip’s instance name, but is not synonymous with it, as was the case with ActionScript 2.0’s MovieClip._name property. If you prefer to iterate through MovieClip.name values generated by code, make sure to use the DisplayObjectContainer.getChildByName() method to locate those name values in the display list later:

for (var i:int = 0; i < 3; i++) {
    var mc:MovieClip = new MovieClip();
    mc.name = "mc" + i;
    this.addChild(mc);
}

for (i = 0; i < 3; i++) {
    mc = MovieClip(this.getChildByName("mc" + i));
    mc.graphics.lineStyle(3, 0xFF0000);
    mc.graphics.lineTo(0, 20);
    mc.x = 20 * i;
    // Locates dynamically generated movie clips
    // by name property and draws a short vertical
    // line in each
}

You can alternately use the DisplayObjectContainer.getChildAt() method to locate display objects by their index number in a given display list. Both of these methods can be invoked on the main timeline or on movie clips because the MovieClip class inherits from DisplayObjectContainer. Note, however, that the return value of both methods is typed as DisplayObject. For this reason, you may need to cast the return value as MovieClip, as shown in the previous example (MovieClip(object) or object as MovieClip)—otherwise the compiler will not let you to reference MovieClip-specific members, such as currentFrame or scenes.

Major Syntax and Structure Changes

The overwhelming majority of ActionScript’s native classes are now arranged into packages, and packages must be imported into class files to be used. This importing is accomplished by way of the import directive, like this:

import fl.controls.CheckBox;
import flash.display.MovieClip;
import flash.events.MouseEvent;

Lines like these tell the compiler exactly which classes are meant by any subsequent references to CheckBox, MovieClip, and MouseEvent in your code. After all, you might very well be using the Adobe-supplied CheckBox component, but you could just as easily be using some third-party user interface component, whose package might be com.niftystuff.CheckBox. The import directive clarifies any ambiguity by setting the record straight from the beginning.

If, by chance, you intend to use two distinct classes that share the same name, you must precede each reference with the fully qualified package for clarity. Otherwise, the class name alone is sufficient:

import com.niftystuff.CheckBox;
import fl.controls.CheckBox;
import flash.display.MovieClip;

var cb1:com.niftystuff.CheckBox = new com.niftystuffCheckBox();
var cb2:fl.controls.CheckBox = new fl.controls.CheckBox();
var mc:MovieClip = new MovieClip();

When appearing in class files, import directives are generally positioned immediately inside the package declaration:

package {
    import flash.display.MovieClip;
    public class SampleClass {
        public function SampleClass() {
            // Constructor code here
        }
    }
}

This practice makes any imported classes available to the whole package. If placed inside the class declaration, the imports are available only to the class:

package {
    public class SampleClass {
        import flash.display.MovieClip;
        public function SampleClass() {
            // Constructor code here
        }
    }
}

In frame scripts, import directives must appear once in each frame used.

Importing and Packages

If you’re already familiar with importing, you won’t discover anything new with the technique; it’s just that in writing ActionScript 3.0 classes, you’ll find that your blocks of import statements are more crowded than they used to be. The ECMAScript specification defines a set of core functionality that, in ActionScript 3.0, appears as a small collection of top-level classes, listed under the All Packages→Top Level classes topic in the ActionScript 3.0 Language and Components Reference. There are only a couple dozen of these, which include such customary classes as Array, Function, Math, and Object. These classes are readily available, without importing, in custom classes and timeline code alike. The rest, comprising hundreds of other classes, including the Flash Player API (all the features unique to the Flash Player runtime) necessitate imports when used in custom classes. Fortunately, you need only a single import per referenced class. That is, importing flash.text.TextField once in a custom class lets you create as many text fields as you like in that class.

Though you don’t encounter it as often in timeline code, the import directive is valid only for the frame in which it’s placed. If you import a class inside a script on frame 1 and wish to use the same class in frame 5, then you have to import the referenced class again in frame 5. This step is necessary because, in ActionScript 3.0, timeline frames are effectively treated as methods of the MovieClip instance they belong to—methods of the default MainTimeline document class. Just as imports inside a class declaration are available only to that class, but not that class’s package, imports inside a method are available only to that method.

In addition to the top-level classes, the ActionScript 3.0 packages hierarchy has three main branches: flash, adobe, and fl. Of these, the flash and adobe packages have a sort of “backstage pass” when referenced in timeline code: none of them requires the import directive when used in frame scripts. The flash packages consist of the Flash Player API and encompass most of the traditional Flash classes like MovieClip, TextField, and SimpleButton (button symbols). The adobe package contains functions and classes used to automate the authoring tool. These correspond to the Flash JavaScript application programming interface (JavaScript API)—also known as JSFL—outlined in the Extending Flash section in the documentation. The JavaScript API lets you run batch scripts on large volumes of FLA files and even create new panels and tools. The other main branch, fl, does require imports in frame scripts and tends to involve components, so not only do you need one import directive for each referenced type of component, you also need a copy of that component in the FLA file’s library. Remember, custom classes always require imports when dealing with packaged classes that ship with the Flash authoring tool.

Note

ActionScript 2.0 provided a sneak peek of the thorough package hierarchy currently in effect. The mx packages (mainly components) were fairly analogous to the current fl packages, and some of the flash packages were available for Flash Player 8, including flash.filters, flash.display for the BitmapData class, and flash.geom for a handful of geometry-related classes like Matrix, Point, and Rectangle.

Namespaces

Namespaces give you a way to control access to properties and methods in custom classes. ActionScript 2.0 had only two built-in namespaces: the access control specifiers public and private, which affected (and still affect) the availability of class members to outside code. ActionScript 3.0 increases this number to four by introducing protected and internal. These built-in specifiers work only in class files, and must precede class, property, and method declarations:

package {
    public class SampleClass {
        private var numValue:Number;
        public function SampleClass() {
            // Constructor code here
        }
    }
}

By default, ActionScript 2.0 members belonged to the public namespace unless specified otherwise. In ActionScript 3.0, this has changed to internal, which lets class members be accessed by any code in the same package. Members specified as protected are available only to the class that defines them, and to any subclasses of that class. Members specified as private are available only to the defining class, and public members are accessible to any outside code.

Developers now have the option to create custom namespaces to further manipulate object access. This is possible with class access control specifiers and useful when employed in advanced scenarios. On the other hand, namespaces can be a stumbling block if they unexpectedly sneak up on you. In ActionScript 3.0, this can happen with loaded XML data.

In XML, namespaces, when present, are specified with an xmlns attribute. Consider the XML example introduced in Chapter 1—but with one key difference: the presence of a namespace indicating a hypothetical music service (namespace in bold):

<?xml version="1.0" encoding="iso-8859-1"?>
<library xmlns:albums="http://www.adobe.com/albumlistings/">
    <artist name="The Beatles">
        <album name="Abbey Road">
            <track title="Come Together" />
            <track title="Something" />
            <track title="Maxwell's Silver Hammer" />
            <track title="Oh! Darling" />
            <track title="Octopus's Garden" />
            <track title="I Want You (She's So Heavy)" />
            <track title="Here Comes the Sun" />
            <track title="Because" />
            <track title="You Never Give Me Your Money" />
            <track title="Sun King" />
            <track title="Mean Mr. Mustard" />
            <track title="Polythene Pam" />
            <track title="She Came in Through the Bathroom Window" />
            <track title="Golden Slumbers" />
            <track title="Carry That Weight" />
            <track title="The End" />
            <track title="Her Majesty" />
        </album>
    </artist>
</library>

Numerous XML sources feature this sort of identifying data, such as iTunes playlists, blog RSS feeds, and even XHTML documents. ActionScript 2.0 ignored XML namespaces, but in ActionScript 3.0, XML namespaces cascade from parent elements to their children. In this case, for example, the <library> element’s xmlns attribute is applied automatically to the remaining elements in the document.

At this point, tracing all <track> elements displays the following output:

var myXML:XML = new XML();
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.load(new URLRequest("cds.xml"));
xmlLoader.addEventListener(
    Event.COMPLETE,
    function(evt:Event):void {
        myXML = XML(evt.target.data);
        trace(myXML..track);
    }
);
// Displays:
// <track title="Come Together"
//     xmlns:albums="http://www.adobe.com/albumlistings/"/>
// <track title="Something"
//     xmlns:albums="http://www.adobe.com/albumlistings/"/>
// <track title="Maxwell's Silver Hammer"
//     xmlns:albums="http://www.adobe.com/albumlistings/"/>
// ...

Note the presence of the albums namespace as an attribute of each <track> element, even though the original XML only shows this attribute for the <library> element. Why is this a problem? The tricky part is that XML namespaces aren’t required to have an identifier, such as the one shown (albums). Note the lack of the albums identifier in this revision:

<?xml version="1.0" encoding="iso-8859-1"?>
<library xmlns="http://www.adobe.com/albumlistings/">

At this point, a trace of all <track> elements comes back with nothing at all. In fact, you can’t see any of the elements now, because the namespace has no identifier.

To address this issue, you can use the new Namespace class:

var myXML:XML = new XML();
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.load(new URLRequest("cds.xml"));
xmlLoader.addEventListener(
    Event.COMPLETE,
    function(evt:Event):void {
        myXML = XML(evt.target.data);
        var ns:Namespace = new Namespace("http://¬
            www.adobe.com/albumlistings/");
        trace(myXML..ns::track);
    }
);

Here, an arbitrarily named variable, ns, is declared and set to an instance of the Namespace class, whose constructor function receives the namespace Uniform Resource Identifier (URI) specified in the xmlns attribute. This allows ns to be used as a prefix for subsequent element references, by way of the :: operator (myXML..ns::track).

If you don’t know the URI beforehand, you can use the XML.namespace() method to retrieve it:

var ns:Namespace = new Namespace(myXML.namespace());
trace(myXML..ns::track);

Data Types and Typing

Before ActionScript 3.0, the default value of declared, but uninitialized, objects was always undefined, even if strongly typed:

// ActionScript 2.0
var b:Boolean;
trace(b);   // Displays: undefined

var str:String;
trace(str); // Displays: undefined

var d:Date;
trace(d);   // Displays: undefined

var a:Array;
trace(a);   // Displays: undefined

var n:Number;
trace(n);   // Displays: undefined

Due to the more memory-efficient nature of objects in ActionScript 3.0, this has changed. Now, the special undefined value applies only to variables that are untyped, such as var n (that is, declared, but not typed and not given an initial value). As a recommended best practice, objects should be strongly typed as a rule, so that the compiler will request only the minimum system memory required for each object. The default value of variables now depends on the corresponding data type:

// ActionScript 3.0
var b:Boolean;
trace(b);   // Displays: false

var str:String;
trace(str); // Displays: null

var d:Date;
trace(d);   // Displays: null

var a:Array;
trace(a);   // Displays: null

var n:Number;
trace(n);   // Displays: NaN (Not a Number)

var i:int;
trace(i);   // Displays: 0

Clearly, code that may have compared values to undefined in the past will no longer behave as expected. Even comparisons to null can no longer be assumed as useful, because some data types default to other values.

var someValue:Number;
if (someValue == undefined || someValue == null) {
    // In ActionScript 3.0, someValue is none of these
}

The upshot is that a theme discussed earlier in this chapter is bolstered yet again, that of programming with purpose. In ActionScript 3.0, in a more fundamental way than ever, each type of object has its own characteristics and consumes its own unique portion of system resources. This variety elicits an attention to detail that, with practice, leads to better programming. ActionScript 3.0 is the chess coach that encourages you to consider your move before even touching a piece. That’s good advice!

Additional ActionScript 3.0 Resources

ActionScript 3.0 is an extensive subject, more so than any of its forerunners. An exhaustive exploration is beyond the scope or focus of this book, but additional resources are certainly available. For a solid foundation, consider Learning ActionScript 3.0: A Beginner’s Guide (O’Reilly), by Rich Shupe and Zevan Rosser. For hundreds of ready-to-use solutions to real-world problems, consider the ActionScript 3.0 Cookbook (O’Reilly), by Joey Lott, Darron Schall, and Keith Peters. For a comprehensive overview of the language, consider Essential ActionScript 3.0 (O’Reilly), by Colin Moock, which steps through ActionScript 3.0 in a thorough 900+ pages.

The Adobe Developer Connection website features a constantly rotating assortment of free articles and tutorials written by top community experts. Each of Adobe’s developer tools has its own entry point, and relevant URLs for ActionScript include the following:

http://www.adobe.com/devnet/actionscript/
http://www.adobe.com/devnet/flash/
http://www.adobe.com/devnet/flex/
http://www.adobe.com/devnet/air/

Trevor McCauley has been working with Flash since 2000, a passion that eventually led to his being hired by Adobe. Trevor is an avid developer, trainer, writer, and conference speaker on topics related to Flash. His “ActionScript 3 Tip of the Day” thread at http://kirupa.com (http://www.kirupa.com/forum/showthread.php?t=223798) became something of a legend after the release of Flash CS3 and continues to help developers make the transition from old to new. He also provides free tutorials and sample files at his website, http://senocular.com (http://www.senocular.com/flash/tutorials.php).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.41.148