We usually treat movie clips just as we treat objects—we set their properties with the dot operator; we invoke their methods with the function-call operator (parentheses); and we store them in variables, array elements, and object properties. We do not, however, create movie clips in the same way we create objects. We cannot literally describe a movie clip in our code as we might describe an object with an object literal. And we cannot generate a movie clip with the new operator:
myClip = new MovieClip( ); // Nice try buddy, but it won't work
Although Flash Player 6 supports the new MovieClip( ) command, that command establishes a MovieClip subclass; it cannot be used to create a new movie clip instance in a movie. Instead, we normally create movie clips directly in the authoring tool, by hand. Once a clip is created, we can use commands such as duplicateMovieClip( ) and attachMovie( ) to make new, independent duplicates of it. As of Flash Player 6, we can also create a completely new movie clip at runtime with the createEmptyMovieClip( ) method.
Just as all object instances are based on a class, all movie clip instances are based on a template movie clip, called a symbol (sometimes called a definition). A movie clip's symbol acts as a model for the clip's content and structure. With the exception of clips created via createEmptyMovieClip( ), we generate a specific clip instance from a movie clip symbol stored in the Library. Using a symbol, we can both manually and programmatically create clips to be rendered in a movie.
A specific copy of a movie clip symbol is called an instance . Instances are the individual clip objects that can be manipulated with ActionScript; a symbol is the mold from which instances of a specific movie clip are derived. Movie clip symbols are created in the Flash authoring tool. To make a new, blank symbol, we follow these steps:
Select Insert New Symbol. The Create New Symbol dialog box appears.
In the Name field, type an identifier for the symbol.
For Behavior, select the Movie Clip radio button.
Click OK.
Normally, the next step is to fill the symbol's canvas and timeline with the content of our movie clip. Once a symbol has been created, it resides in the Library, waiting for us to use it to instantiate a movie clip instance. However, it is also possible to convert a group of shapes and objects that already exist on stage into a movie clip symbol. To do so, we follow these steps:
Select the desired shapes and objects.
Select Insert Convert to Symbol.
In the Name field, type an identifier for the symbol.
For Behavior, select the Movie Clip radio button.
Click OK.
The shapes and objects we select to create the new movie clip symbol are replaced by an unnamed instance of that new clip. The corresponding movie clip symbol appears in the Library, ready to be used to create further instances.
There are four ways to create a new movie clip instance. Three of these are programmatic; the other is strictly manual and is undertaken in the Flash authoring tool. All but one method, createEmptyMovieClip( ), require an existing movie clip symbol from which to derive the new instance.
We can create movie clip instances manually using the Library in the Flash authoring environment. By physically dragging a movie clip symbol out of the Library and onto the Stage, we generate a new instance. An instance created in this way should be named manually via the Instance panel (Flash 5) or Property inspector (Flash MX). You'll learn more about instance names later in this chapter. Refer to "Using Symbols, Instances, and Library Assets" in the Macromedia Flash Help if you've never worked with movie clips in Flash.
Any instance that already resides on the Stage of a Flash movie can be duplicated with ActionScript. We can then treat that copy as a completely independent clip. Both manually created and programmatically created clip instances can be duplicated. In other words, it's legal to duplicate a duplicate.
There are two ways to duplicate an instance using duplicateMovieClip( ):
We can invoke duplicateMovieClip( ) as a global function, using the following syntax:
duplicateMovieClip(target, newName, depth);
where target is a string indicating the name of the instance we want to duplicate, newName is a string that specifies the identifier for the new instance, and depth is an integer that designates where in the content stack (we'll discuss the content stack soon) we want to place the new instance.
We can also invoke duplicateMovieClip( ) as a method of an existing instance:
theClip.duplicateMovieClip(newName, depth);
where theClip is the name of the clip we wish to duplicate, and newName and depth both operate as in the previous example.
|
When created via duplicateMovieClip( ), an instance is initially positioned directly on top of its seed clip. Our first post-duplication task, therefore, is usually moving the duplicated clip to a new position. For example:
ball_mc.duplicateMovieClip("ball2_mc", 0); ball2_mc._x += 100; ball2_mc._y += 50;
Duplicated instances whose seed clips have been transformed (e.g., colored, rotated, or resized), via ActionScript or manually in the Flash authoring tool, inherit the transformation of their seed clips at duplication time. Subsequent transformations to the seed clip do not affect duplicated instances. Likewise, each instance can be transformed separately. For example, if a seed clip is rotated 45 degrees and then duplicated, the duplicate instance's initial rotation is 45 degrees:
seedClip_mc._rotation = 45; seedClip_mc.duplicateMovieClip("newClip_mc", 0); trace(newClip_mc._rotation); // Displays: 45
If we then rotate the duplicate instance by 10 degrees, its rotation is 55 degrees, but the seed clip's rotation is still 45 degrees:
newClip_mc._rotation += 10; trace(newClip_mc._rotation); // Displays: 55 trace(seedClip_mc._rotation); // Displays: 45
By duplicating many instances in a row and adjusting the transformation of each duplicate slightly, we can achieve interesting compound effects, such as stars in the sky, trails effects, and geometric animations (for an example, see "Load Event Starfield" in the online Code Depot).
Using duplicateMovieClip( ) offers other advantages over placing clips manually in a movie, such as the ability to:
Control exactly when a clip appears on the Stage, relative to a program's execution
Control exactly when a clip is removed from the Stage, relative to a program's execution
Copy a clip's event handlers
These abilities give us advanced programmatic control over the content in a movie. With a manually created clip, we must preordain the birth and death of the clip using the timeline.
Like duplicateMovieClip( ), the attachMovie( ) method lets us create a movie clip instance at runtime; however, attachMovie( ) creates a new instance from a Library symbol instead of from another movie clip instance. To "attach" a movie clip means to make one movie clip the child of either the main timeline or another movie clip in the movie clip hierarchy. In order to use attachMovie( ) to create an instance of a symbol, we must first export that symbol from the Library. Here's how:
In the Library, select the desired symbol.
In the Library's pop-up Options menu, select Linkage. The Linkage Properties dialog box appears.
Select the Export For ActionScript checkbox.
In the Identifier field, type a unique name for the clip symbol. The name can be any string—often simply the same name as the symbol itself—but should be different from all other exported clip symbols.
In Flash MX, we can also set the frame at which the link clip will be exported with the movie. For details, see MovieClip.attachMovie( ) in the ActionScript Language Reference.
Click OK.
Once a clip symbol has been exported, we can attach new instances of that symbol to an existing clip by invoking attachMovie( ) with the following syntax:
theClip.attachMovie(symbolIdentifier, newName, depth, [initObject]);
where theClip is the name of the movie clip to which we want to attach the new instance. If theClip is omitted, attachMovie( ) attaches the new instance to the current clip (the clip on which the attachMovie( ) statement resides). The symbolIdentifier parameter is a string containing the name of the symbol we're using to generate our instance, as specified in the Identifier field of the Linkage options in the Library (Step 4). The symbolIdentifier is not necessarily the same name as the symbol itself. The newName parameter is a string that specifies the identifier for the new instance we're creating. If newName is not a string that converts to a legal identifier, you're in for some potentially surprising results, as shown in the examples later in this section. Finally, depth is an integer that designates where in the host clip's content stack to place the new instance. Flash MX adds support for a fourth, optional parameter called initObject, which lets you copy properties from an existing object to the new clip. See MovieClip.attachMovie( ) in the ActionScript Language Reference for full details.
For example, to attach a new movie clip, based on the "square" symbol, to the current timeline, use:
this.attachMovie("square", "square1_mc", 1); square1_mc._x = 50; // Position the new clip at a horizontal position of 50 square1_mc._y = 200; // ...and a vertical position of 200
|
When we attach an instance to another clip, the instance is positioned in the center of the clip, among the clip's content stack. When we attach an instance to the main movie of a document, the instance is positioned in the upper-left corner of the Stage, at coordinates (0, 0).
In Flash Player 6, the attachMovie( ) function returns a reference to the newly created movie clip, which is useful for debugging. If the function returns undefined, then the movie clip creation failed (except in Flash 5, where attachMovie( ) always returns undefined). In this case, you most likely specified the wrong name for symbolIdentifier (remember to specify the quotes around the string, enter the name correctly, and use the export name specified in the Linkage options, not the symbol's Library name). The symbolIdentifier string is not case-sensitive, so if the symbol is exported as "Symbol 1", you can (but should not, as a matter of good form) specify "symbol 1" as symbolIdentifier.
The return value of the attachMovie( ) function is particularly useful if the operation succeeds and yet you are still having trouble referring to your newly created movie clip by the expected identifier. Technically, newName does not need to be a legal identifier, but you'll have trouble referring to the new clip if it isn't, unless you store the return value in a variable. That is, you can use the return value to access the clip after it is attached, if the newName parameter was specified incorrectly and could not be converted to a legal identifier. The newName parameter is actually converted first to a string, and later converted again to an identifier. Let's see how this works.
In our first example, newName is specified as "newClip 1", which has a space in it, so it can't be converted to a legal identifier. Luckily, you can use the return value stored in newClip_mc to refer to the newly created clip:
newClip_mc = _root.attachMovie("Symbol 1", "newClip 1", 0); trace(newClip_mc); // Displays: _level0.newClip 1 // (which is not a valid identifier)
In the next example, newName is specified as newClip1, which takes the form of a legal identifier but is missing the necessary quotes to make it a string. The attachMovie( ) command first tries to convert it to a string, but because newClip1 looks to the interpreter like an undefined variable, the conversion yields the empty string. Luckily, you can again use the return value stored in newClip_mc to refer to the newly created clip:
newClip_mc = _root.attachMovie("Symbol 1", newClip1, 0); trace(newClip_mc); // Displays _level0. // (the clip's name is an empty string!)
What if newName is specified as a legal identifier, such as ball_mc, that already refers to an existing movie clip? Again, the attachMovie( ) command first tries to convert it to a string, which (referring to Table 3-2 for string conversion of movie clips) yields the interim string "_level0.ball_mc" for newName. This series of conversions results in the newly created movie clip having the incorrect name "_level0._level0.ball_mc". Luckily, yet again you can use the return value stored in newClip_mc to refer to the newly created clip properly:
//Assume ball_mc is an existing (valid) movie clip identifier newClip_mc = _root.attachMovie("Symbol 1", ball_mc, 0); trace(newClip_mc); // Displays "_level0._level0.ball_mc"
As you can see, life is much easier when we make sure to provide a string that converts to a legal identifier for newName.
As of Flash Player 6, completely blank new movie clip instances can be created in an existing clip with the createEmptyMovieClip( ) method, which has the following syntax:
theClip.createEmptyMovieClip(newName, depth);
where theClip is the name of an existing movie clip to which we want to attach a new, empty movie clip. The new clip instance is given a name of newName and placed in theClip's content stack at the specified depth.
Movie clips created with createEmptyMovieClip( ) are not derived from a Library symbol; they are simply blank movie clip instances with one frame. Empty movie clips can be used as drawing canvases, script clips, or containers for nested clips (e.g., a form clip that contains text fields and a Submit button).
When we create instances, we assign them identifiers, or instance names, that allow us to refer to them later. Assigning identifiers to movie clips differs from assigning them to regular objects. When we create a typical object (not a movie clip), we must assign that object to a variable or other data container in order for the object to persist and in order for us to refer to it by name in the future. For example:
new Object( ); // Object dies immediately after it's created, and // we can't refer to it because we didn't store it. var thing = new Object( ); // Object reference is stored in thing, // and can later be referred to as thing.
Movie clip instances need not be stored in variables in order for us to refer to them. Unlike typical objects, movie clip instances are accessible in ActionScript via their instance names as soon as they are created, either programmatically or in the authoring tool. The manner in which an instance gets its initial name depends on how it was created. Programmatically generated instances are named at runtime by the function that creates them. Manually created instances are normally assigned explicit instance names in the authoring tool through the Property inspector, as follows:
Select the instance on stage.
In the Property inspector, for <Instance Name>, enter the instance name.
(In Flash 5, the instance name is set via the Instance panel.) Once the instance is named in the authoring tool, it can be accessed via ActionScript using the same name. (It is good practice to add "_mc" as a suffix to the identifier name of any movie clips you create during authoring or at runtime.) For example, if there exists an instance named ball_mc on stage, we can access its properties like this:
ball_mc._y = 200;
If a manually created clip is not given an instance name, it is assigned one automatically by the Flash Player at runtime. Automatic instance names fall in the sequence instance1, instance2, instance3, ...instancen, but these names don't meaningfully describe our clip's content (and we must guess at the automatic name that was generated). For example, the first unnamed clip instance can be accessed as:
instance1._y = 200;
|
Each clip's instance name is stored in its built-in _name property, which can be both retrieved and set. For clips defined manually during authoring, the default _name is a string version of the original clip identifier:
trace(ball_mc._name); // Displays "ball_mc"
For programmatically defined clips, the initial value for _name is specified by the newName parameter passed to duplicateMovieClip( ) or attachMovie( ).
The _name property is useful for debugging or displaying the name of a clip (without the fully qualified path). Note how the value returned by _name differs from other representations of a movie clip identifier (see also the legacy _target property discussed later in this chapter):
trace(instance1._name); // Displays instance1 trace(instance1); // Displays _level0.instance1 trace(String(instance1)); // Displays _level0.instance1 trace(targetPath(instance1)); // Displays _level0.instance1 trace(instance1.toString( )); // Displays [object Object]
Example 13-1 uses the _name property to find a particular movie clip. See Section 13.5.6.4 later in this chapter for an example showing how to use the _name property to prevent an infinite loop.
// Finds all movie clips inside gameboard_mc with the word "enemy" in their name. for (var prop in gameboard_mc) { if (typeof gameboard_mc[prop] = = "movieclip") { if (gameboard_mc[prop]._name.indexOf("enemy") != -1) { // Found an enemy movie clip...make it attack the player. gameboard_mc[prop].attackPlayer( ); } } }
If we change an instance's _name property, all future references to the instance must use the new name. For example, if we change the value of ball_mc._name, the ball_mc reference ceases to exist, and we must subsequently use the new name to refer to the instance:
ball_mc._name = "circle_mc"; // Change ball_mc's name to circle_mc trace(typeof ball_mc); // Displays "undefined" because ball_mc // no longer exists. circle_mc._x = 59; // After the name change, you must // use the clip's new name.
Therefore, you shouldn't change a movie clip's _name property at runtime, as it is can make your code fail, or at least make it very difficult to follow.
We've discussed creating movie clip instances within a single document, but the Flash Player can also display multiple .swf documents simultaneously. We can use loadMovie( ) — as either a global function or a movie clip method — to import an external .swf file into the Player and place it either in a clip instance or on a numbered level above the base movie (i.e., in the foreground relative to the base movie).
|
Dividing content into separate files gives us precise control over the downloading process and makes partial application updates easier. Suppose, for example, we have a movie containing a main navigation menu and five subsections. Before the user can navigate to section five, sections one through four must finish downloading. But if we place each section in a separate .swf file, the sections can be loaded in an arbitrary order, giving the user direct access to each section. To update a section, we can simply replace the appropriate .swf file with a new one.
When an external .swf is loaded into a level, its main movie timeline becomes the root timeline of that level, and it replaces any prior movie loaded in that level. Similarly, when an external movie is loaded into a clip, the main timeline of the loaded movie replaces that clip's timeline, unloading the existing graphics, sounds, and scripts in that clip.
Like duplicateMovieClip( ), loadMovie( ) can be used as both a standalone function and an instance method. The standalone syntax of loadMovie( ) is as follows:
loadMovie(url, location)
where url specifies the address of the external .swf file to load. The location parameter is a string indicating the path to an existing clip or a document level that should host the new .swf file (i.e., where the loaded movie should be placed). For example:
loadMovie("circle.swf", "_level1"); loadMovie("photos.swf", "viewClip_mc");
Because a movie clip reference is converted to a path when used as a string, location can also be supplied as a movie clip reference, such as _level1 instead of "_level1". Take care when using references, however. If the reference supplied does not point to a valid clip, the loadMovie( ) function has an unexpected behavior—it loads the external .swf into the current timeline. See Section 13.7 later in this chapter for more information on this topic.
The MovieClip method version of loadMovie( ) has the following syntax:
theClip.loadMovie(url);
When used as a clip method, loadMovie( ) assumes we're loading the external .swf into theClip, so the location parameter required by the standalone loadMovie( ) function is not needed. Therefore, we supply only the path to the .swf to load via the url parameter. Naturally, url can be either an absolute or a relative filename, such as:
viewClip.loadMovie("photos.swf");
When placed into a clip instance, a loaded movie adopts the properties of that clip (e.g., the clip's scale, rotation, color transformation, etc.).
Note that theClip must exist in order for loadMovie( ) to be used in its method form. For example, the following attempt to load circle.swf will fail if _level1 is empty:
_level1.loadMovie("circle.swf");
The loadMovie( ) function is not immediately executed when it appears in a statement block. In fact, it is not executed until all other statements in the block have finished executing.
|
Because loadMovie( ) loads an external file (usually over a network), its execution is asynchronous. That is, loadMovie( ) may finish at any time, depending on the speed of the file transfer. Therefore, before we access a loaded movie, we should always check that the movie has finished transferring to the Player. We do so with what's commonly called a preloader—code that checks how much of a file has loaded before allowing some action to take place. Preloaders can be built with the _totalframes and _framesloaded movie clip properties and the getBytesLoaded( ) and getBytesTotal( ) movie clip methods. See the appropriate entries under the MovieClip class in the Language Reference for sample code.