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.
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.
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.
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 } }
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.
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.
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);
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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;
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);
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.
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
.
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.niftystuff.¬ CheckBox(); 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.
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.
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 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);
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!
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).