Working with Movie Clips

     

The movie clip is by far the most important and commonly used object. It is also unique. Although the MovieClip class was created to bring movie clips under the umbrella of the object-oriented programming (OOP) framework introduced in Flash 5, movie clips are not like other objects and require special treatment in many ways.

For example, you can't create new MovieClip instances by using the new operator, as you can for most other classes. This statement, for instance, does not create a new movie clip:

 myClip = new MovieClip(); // does NOT create a new movie clip!! 

Unlike other constructor functions, the MovieClip class alone doesn't come close to being a complete factory for making movie clips. Instead, to create a new empty movie clip programmatically, you use the createEmptyMovieClip() method of the MovieClip class. The createEmptyMovieClip() method is necessary because the movie clip class has its roots in the pre-OOP era, and many of the most basic properties of a movie clip (such as _x , _y , _xscale , _yscale ) have no connection to the MovieClip class but are built into the interpreter.

Movie clips are also unique in having a timeline with one or more keyframes ”that is, frames in which you either add ActionScript or add or change a graphic, a button, a text field, or another movie clip. Many movie clip methods (such as gotoAndPlay() , stop() and prevFrame() ), three movie clip properties ( _currentframe , _framesloaded , and _totalframes ), and the ubiquitous onEnterFrame event relate to the Timeline.

NOTE

Buttons also have a specialized type of Timeline, although this Timeline does not support ActionScript. Nor are any methods, properties, or events associated with it.


Other than the Stage, the only objects that can contain graphical content in Flash are buttons and movie clips. Graphic symbols contain graphics, but graphic symbols are not objects.

See Appendix A, "ActionScript Reference," page 909 , for a full listing of the methods and properties belonging to the MovieClip class (except the drawing methods, which are covered in drawing.htm on the CD accompanying this book).


Flash MX 2004's New Movie Clip Features

Flash MX 2004 adds two new movie clip properties and four new methods, falling into four categories:

  • Replacing or extending the standard context menu ” The new menu property enables you to use the ContextMenu class to create a new context menu for a movie clip.

    For more on the ContextMenu class, see "Using Event Handlers to Trigger Action," earlier in this chapter; Appendix A, "ActionScript Reference," page 909 ; and contextMenu.fla on the CD accompanying this book.


  • Managing child clip depth ” getNextHighestDepth and getInstanceAtDepth give you information you need to intelligently assign depth numbers to new movie clips, as you create them on a Stage, so that clips are visually " stacked " correctly, and so that you do not accidentally destroy existing content when you load new content, whether external content or content from the Library.

    For more information on depth management, see "Visual Stacking for Siblings: Depth Numbers," later in this chapter, page 526 .


  • Loading external content ” The new _lockroot property and the getSWFVersion method help you manage the process of loading external content, such as SWFs and JPEGs. Even more important in this regard is the MovieClipLoader class, which replaces the loadMovie method for content targeting FP7.

    The content-loading features are covered later in this chapter, under "Loading and Unloading External Content," page 520 .


  • Managing static text ” The new getTextSnapshot method returns a TextSnapshot object containing the static text in a movie clip, or an empty string if the clip contains no static text. This works in FP7 only, though the clip with the static text can be targeted for FP6 or FP7.

The TextSnapshot object is covered in "The TextSnapshot Class," page 611 , in Chapter 21.


Creating and Removing Movie Clips

You can create movie clips in four ways. Each has implications for how the new clip fits into the hierarchy of clips and where it is positioned on the Stage.

You can also remove a movie clip, displace it with another clip, or remove just the graphical contents of a clip, while the movie clip object remains.

NOTE

Clip positioning is managed via the registration point , indicated by a cross-hatch that appears when a clip is edited in the Library. By default it's in the center of a clip, but in the upper left of the Main Timeline.


The Parent-Child Hierarchy

The hierarchy of clips in Flash, which determines their visual stacking order , is based on parent-child relationships.

A parent-child relationship between two movie clips is a "container-contained" relationship: The parent contains the child. A parent can have any number of children, but a child can have only one parent. Two children of the same parent are siblings .


For more details on the visual stacking order, see "Controlling the Visual Stacking Order of Movie Clips," later in this chapter, page 524 .


Creating Movie Clips

As mentioned earlier, you create a clip in one of four ways. Each of the following examples creates clip2 as a child of clip1 . The last three examples also give clip2 a depth number of 1.

The four ways of creating a clip are as follows :

  • Manually ” For instance, while you're authoring, if clip1 and clip2 already exist in the Library, you open clip1 in the Library panel by double-clicking on the symbol icon, and then you drag and drop clip2 into clip1 . You can also create and select a graphic inside clip1 ; select Modify, Convert to Symbol; type clip2 in the Name field; click the Movie Clip radio button; and click OK to convert the graphic into a movie clip named clip2 .

  • Using the attachMovie() method of the MovieClip class ” At runtime, if clip1 is on the Stage and a clip in the Library has the linkage ID clip , for example, the following statement creates a clip named clip2 , position clip2 , on the Stage with its registration point aligned with the registration point of clip1 , and makes clip2 a child of clip1 :

     clip1.attachMovie("clip", "clip2", 1); 

  • Using the duplicateMovieClip() method of the MovieClip class or the duplicateMovieClip() global function ” At runtime, if myClip is on the Stage and is a child of clip1 , either of the following statements creates a clip named clip2 , position clip2 , on the Stage with its registration point aligned with the registration point of myClip , and makes clip2 a child of clip1 :

     myClip.duplicateMovieClip("clip2", 1); duplicateMovieClip("myClip", "clip2", 1); 

  • Using the createEmptyMovieClip() method of the MovieClip class ” At runtime, if clip1 is on the Stage, the following statement creates a clip named clip2 , position clip2 , on the Stage with its registration point aligned with the registration point of clip1 , and makes clip2 a child of clip1 :

     clip1.createEmptyMovieClip("clip2",1); 

    If you use createEmptyMovieClip() to create a movie clip in the root, the new clip's registration point is aligned with the upper-left corner of the Stage.

attachMovie() , duplicateMovieClip() , and createNewMovieClip() return references to the clips they create. Creating references to frequently used movie clips can be helpful, because evaluating a reference is less processor- intensive than evaluating a string. Here's an example with a return reference, ref :

 ref = clip1.attachMovie("clip", "clip"+i, i); 

NOTE

attachMovie() can be used to attach buttons. Buttons can't be duplicated programmatically. In addition, they cannot be removed. Neither removeMovieClip() nor unloadMovie() works with buttons.


Removing Movie Clips

You can use the removeMovieClip() method of the MovieClip class to remove a clip created with attachMovie() , duplicateMovieClip() , or createEmptyMovieClip() . If you no longer need the graphics or code contained in a movie clip, removing it gives Flash a chance to reclaim the memory that the clip is using, and ( especially if there are a lot of such clips to be removed) make your program run more efficiently . Here's an example:

 myClip.removeMovieClip(); // myClip is gone 

In addition, you can displace any existing clip by creating a new sibling with the same depth number as the existing clip.

You can remove the graphical content from a movie clip, leaving an "empty" movie clip object behind, using unloadMovie() . The empty clip could be used as a "container" to attach clips from the Library, for instance, or to load external content.

In contrast, removeMovieClip() completely removes the clip, leaving nothing behind. If you remove a clip using removeMovieClip() , you can't attach anything to it or load anything into it; it is simply not there.

Loading and Unloading External Content

With the advent of attachMovie() in Flash 5, loading external SWFs became much less of a necessity. However, in some situations, you still might want to load external SWFs.

Suppose a Flash application provides access to several SWFs and/or JPEGs, and each user is likely to access only a small portion of the content. It doesn't make sense to force every user to download all the content, as happens when you store the content in the Library and use attachMovie() .

Or you might want to provide alternative "skins" for an application, each of which will be used by only a minority of users.

Or you might be asked to "frame" an existing SWF, perhaps adding an introduction, new music, or credits at the end. The easiest way to accomplish this task may be to write an application that contains the additional material and loads the existing SWF.

Another reason to load external SWFs might be to give the user a choice between different versions of your content ”for instance, a large, high-resolution image and a small, low-resolution one.

You can load an external SWF or JPEG in four ways: using MovieClipLoader (highly recommended, but the loading movie can run only in FP7), the loadMovieNum() and loadMovie() global functions, or the loadMovie() method of the MovieClip class.

Whichever approach you use, it's primarily the process of loading that differentiates MovieClipLoader from the other approaches. The results are the same. That means that there are some facts of life about working with loaded content that remain the same, too. For instance, you can't duplicate clips containing external content, and loaded movies can attach o nly from their own libraries.

After you load content into a movie with loadMovie() , you cannot duplicate that movie. There is one minor exception to this rule: If you execute duplicateMovieClip() in the same code block as loadMovie() , you can use duplicateMovieClip() to duplicate the clip with its original content (not the newly loaded content). For instance, the following example does work but does not duplicate the loaded content as you might expect:

 loadMovie("test.swf", _root.clip1); _root.clip1.duplicateMovieClip("dupetest", 1); 

Neither the rule nor the exception applies to loadMovieNum() , which does not load into a movie clip.

If you use loadMovie() to load content into myClip , you can no longer attach clips from myClip 's Library to myClip . You can attach content from the Library associated with the loaded content.

graphics/troubleshooting_icon.jpg

Ifs you're having trouble attaching a movie from the Library of a loaded SWF or accessing a variable, movie clip, or function in a loaded SWF, see the "Troubleshooting" section at the end of this chapter, page 535 .


Loading/Unloading SWFs and JPEGs: FP6 or Earlier

Here's an example of each of the three ways to load content into FP6 and earlier:

 loadMovieNum("test.swf", 1); // global function loadMovie("test.swf","myClip"); // global function myClip.loadMovie("test.swf"); // MovieClip method 

With loadMovieNum() , either you assign the SWF or JPEG to an existing level, or you can create a new level for it. With the loadMovie() method of the MovieClip class, the external file loads into an existing clip, so its level is that of the existing clip.

For more details on levels, see "Controlling the Visual Stacking Order of Movie Clips," later in this chapter, page 524 .


You can use the loadMovie() global function to load content into a movie clip , as shown in the preceding example. You can also use the loadMovie() global function to load into a level , using a string or a numeral to indicate the level:

 loadMovie("test.swf","_level1"); // string "_level1" loadMovie("test.swf", 1); // numeral 1 

You can't use a variable to indicate the level. For instance, the following code doesn't work:

 x = 15; loadMovie("test.swf", x); // DOESN'T LOAD !! 

Some reasonable-looking nonstring values cause the loadMovie() global function to load the content into level 0, replacing everything in your movie. For instance, the following is an innocent-looking implement of mass destruction:

 loadMovie("test.swf",_level1); // LOADS INTO LEVEL 0 !! 

External SWFs or JPEGs loaded with loadMovie() can be unloaded with unloadMovie() . You can use the global function for both loading and unloading, the MovieClip method for both, or the global function for one and the MovieClip method for the other. Here are some examples:

 loadMovie("test.swf", _root.clip1); // load "test.swf" into _root.clip1 _root.clip2.loadMovie("test.swf"); // load "test.swf" into _root.clip2 unloadMovie("_root.clip2"); // unload whatever is in _root.clip2 clip1.unloadMovie(); // unload whatever is in clip1 

Similarly, you can load with loadMovieNum() and unload with unloadMovieNum() :

 loadMovieNum("test.swf", 1); unloadMovieNum(1); 

unloadMovie() leaves an empty movie clip. For instance, even after you have executed the clip1.unloadMovie() statement, clip1 remains and you can attach content to it again:

 clip1.loadMovie("new.swf"); 

You can also just load the new content without ever having executed clip1.unloadMovie() . Loading new content into a clip automatically unloads the old content.

You can use unloadMovie() to unload the graphical content from a manually created clip, leaving an empty movie clip.

The unloadMovie() Global Function

It is generally best to use a string to specify a movie to unloadMovie() .

The unloadMovie() global function can also take as a parameter a reference to a movie clip, rather than a string. For instance, this statement is legal:

 unloadMovie(_root.clip2); // unload whatever is in _root.clip2 

However, if you provide a reference to a clip that does not exist, the unloadMovie() global function unloads the content from whatever Timeline the statement is on. It's safer to use only strings as parameters for the unloadMovie() global function.


Loading/Unloading SWFs and JPEGs in FP7: MovieClipLoader
graphics/new_icon.jpg

You can create a single MovieClipLoader object to handle all the loading and unloading in your movie, or you can create multiple MovieClipLoader objects.


MovieClipLoader has five events. In the order in which they are typically invoked, they are

  • MovieClipLoader.onLoadStart() ” Invoked when loading starts.

  • MovieClipLoader.onLoadError() ” Invoked if the clip cannot be loaded.

  • MovieClipLoader.onLoadProgress() ” Invoked as loading progresses.

  • MovieClipLoader.onLoadComplete() ” Invoked when the file has completely downloaded.

  • MovieClipLoader.onLoadInit() ” Invoked after the actions in the first frame of the clip execute.

In addition, MovieClipLoader has five methods:

  • MovieClipLoader.loadClip() ” Initiates the loading process.

  • MovieClipLoader.unloadClip() ” Removes movies/images loaded with MovieClipLoader.loadClip() or cancels a load operation in progress.

  • MovieClipLoader.addListener() ” Enables other objects to "tune in" on the process.

  • MovieClipLoader.removeListener() ” Removes a listener created with addListener() .

  • MovieClipLoader.getProgress() ” Allows you to explicitly request a progress report on downloaded file(s), instead of or in addition to onLoadProgress().

There are five basic steps or phases to using MovieClipLoader :

  1. Create one or more instances of the MovieClipLoader class, using the new keyword.

  2. Create the event handlers: onLoadStart , onLoadError , onLoadComplete , onLoadInit , and optionally onLoadProgress .

  3. Load clips using loadClip() , and monitor progress from onLoadStart to onLoadInit .

  4. Use the clips in your program.

  5. Unload clips using unLoadClip() .

These are demonstrated in sample movie MCL.fla on the CD.

NOTE

MovieClipLoader does not report bytes loaded if you load a local file ”only if you load a file from a Web site.


One nice feature of MovieClipLoader is that it is flexible and robust when it comes to how you specify the movie clip or level into which the content should be loaded. All these formats work:

 myMCL_obj.loadClip("http://www.myDomain.com/test1.swf","_root.myMC1"); myMCL_obj.loadClip("http://www.myDomain.com/test1.swf","_level0.myMC1"); myMCL_obj.loadClip("http://www.myDomain.com/test1.swf", 1);//loads into level 1 myMCL_obj.loadClip("http://www.myDomain.com/test1.swf", _level0.myMC1); myMCL_obj.loadClip("file:///C:/bigproject/images/testing.jpg", "_level0.myMC4"); 

Other advantages include

  • You find out immediately whether loadClip() executed successfully or not.

  • You can receive specific errors such as "URL not found."

  • Checking progress is easy, and you can use either a listener or an explicit request for a progress report.

You don't get any of these features with MovieClip.onLoad() and loadMovie() . For gory details, go to http://www. moock .org/blog/archives/000010.html.

Controlling the Visual Stacking Order of Movie Clips

Movie clips in a Flash document appear as if on clear pieces of acetate, so that where there is no graphical content at a higher stratum, elements on lower strata are visible. To control which movies appear in front of or behind other movies on the Stage, referred to as the visual stacking order , you need to understand how clips in a Flash document are arranged hierarchically.

The movie clip hierarchy combines two paradigms : the "sandwich" and the "tree." Specifically, a Flash document is organized into one or more sandwich-like levels . Within each level, clips are organized into a tree-like hierarchy, with clips vertically organized in a parent-child hierarchy , and the visual stacking order of siblings (child clips of the same parent) managed with depth numbers. Thus, the overall Flash document is a "sandwich of trees." If a movie has only one level, you deal with just one tree-like structure.

The _root identifier refers to the base of each tree. Each level has its own _root , or Main Timeline. Thus, two references to _root.myClip1 refer to two entirely different clips if the references occur on two different levels. For instance, one of the clips might be _level0.myClip1 , and the other might be _level1.myClip1 . References that begin with a level are unambiguous.

The initial hierarchical arrangement of movie clips is determined by five factors:

  • The chronological order in which you manually place multiple clips into any single timeline layer

  • The relative positions of timeline layers containing clips

  • Parent-child relationships among clips

  • Whether the clip was created with duplicateMovieClip() , attachMovie() , or createEmptyMovieClip()

  • On which levels clips reside

Using Levels for Visual Stacking

The overall Flash document hierarchy consists of one or more numbered levels , starting with _level0 . The level hierarchy is a simple "sandwich" structure, with a higher level number indicating a higher position in the hierarchy. For instance, _level2 is in the foreground compared to _level1 , and _level1 is in the foreground compared to _level0 .

Every document must have a _level0 , and beyond that it can have any number of levels up to a total of more than 16,000. There is no harm in having gaps in level numbers. For instance, you can have content on levels 0, 100, and 1,000, with nothing on other levels.

You assign ”and perhaps create ”levels beyond _level0 when you load an external SWF or JPEG using the loadMovieNum() or loadMovie() global function, or using MovieClipLoader 's loadClip method with a level number. Unless you deliberately create a new level using one of these three, your document will have only one level, _level0 .

Visual Stacking for Nonsiblings: The Parent-Child Hierarchy

Within each level, the visual stacking order is determined by two factors: the parent-child hierarchy and depth numbers.

In the parent-child hierarchy, every sibling is the founder of a "family line." When you look vertically within a family line, the visual stacking order of parents and children is simple: A child always appears in the foreground in comparison with its parent.

Each family line forms a unit for purposes of visual stacking order, just as it does for setting rotation, scale, or position. When you're working with two sibling movie clips, you don't have to think about the fact that each one may be composed of child clips and grandchild clips and so on. The way that visual stacking order works is consistent with other operations in Flash: If you rotate a clip, any family line within it is rotated , too. If you move a clip, the whole family line is moved. Similarly, if one clip is placed "behind" another clip in the visual stacking order, that placement applies to any family line within each of those clips, too. In short, when you're working with a clip, you can treat it as a unit.

Visual Stacking for Siblings: Depth Numbers

The other factor determining the visual stacking order within each level is depth numbering . Flash uses depth numbers to track several factors that together determine the visual stacking order of siblings. A sibling with a higher depth number appears in the foreground, in comparison with a sibling with a lower depth number.

The maximum number of depths that can be assigned in a single timeline is 16,384. Legal depth numbers range from “16,384 to 1,048,575.

NOTE

Flash 5 apparently put no lower limit on depth numbers, but a Flash 6 SWF can't go below “16,384. The Flash 6 Player will make exceptions for older SWFs.


In the past, developers often used very high depth numbers, in an attempt to make sure that they didn't accidentally use an existing depth number for a new clip, which would cause the new clip to displace an existing clip.

graphics/new_icon.jpg

Now, thanks to the new getInstanceAtDepth and getNextHighestDepth methods of the MovieClip class, the guessing games are over. You can use getInstanceAtDepth to test before you assign a depth number to a new movie clip. If you get back anything but undefined , you know there is already a clip there. getInstanceAtDepth also tells you the instance name of the clip, in case you need to make a decision based on that. Another approach is to use getNextHighestDepth to get the next unused depth number in sequence, and then use that.


These two methods are welcome complements to the existing getDepth , which allows you to determine the depth of any movie clip.

See Appendix A, "ActionScript Reference," page 909 , for a listing of MovieClip methods and properties, including getInstanceAtDepth and getNextHighestDepth ).


Together, these methods enable you to implement safe practices when assigning depth numbers, to make sure that you don't displace any existing content.

Automatic and Explicit Depth Numbering

Depth numbers may be assigned automatically or explicitly:

  • Flash automatically assigns depth numbers to movie clips that you manually place on the Stage at authoring time. The numbering depends on three factors: the chronological order in which you place multiple clips into any single Timeline layer, the relative positions of Timeline layers containing clips, and parent-child relationships among clips. Automatically assigned depth numbers are always negative, starting at “16,384 for _root and going up, possibly with gaps (for example, “16,383, “16,382, “16,380).

  • You must explicitly assign a depth number to each movie clip that you create using duplicateMovieClip() , attachMovie() , or createEmptyMovieClip() . Flash reserves numbers in the range from 1 to 16,384 for this purpose and does not automatically assign numbers in this range.

  • You can explicitly "swap" the depth numbers of two existing clips, or assign a currently unused depth number to a clip, using the swapDepths() method of the MovieClip class.

    For instance, the following line assigns myClip a depth number of 1. If a sibling already has a depth number of 1, the sibling is assigned myClip 's current depth number.

     myClip.swapDepths(1); 

    The following line gives otherClip 's depth number to myClip and vice versa:

     myClip.swapDepths(otherClip); 

If you explicitly assign only depth numbers greater than 0, automatically assigned numbers and explicitly assigned numbers will never conflict because automatically assigned numbers are never greater than 0.

Assigning _Root's Depth to Create New Backgrounds Dynamically

You can dynamically place a background behind all content, including author-time content, by giving a new clip the depth of the _root ( “16,384), like this:

 _root.attachMovie ("newBackground", "myBG", -16384); 

Automatic Depth Numbering

Flash follows just two key rules when automatically assigning depth numbers:

  • A sibling in a higher timeline layer gets a higher depth number than a sibling in a lower timeline layer.

    NOTE

    For automatically assigned depth numbers, "higher" means "less negative." For instance, “16,380 is higher than “16,382.


  • A new sibling gets a higher depth number than any existing sibling. This means, for instance, if you manually place multiple clips into a single timeline layer, Flash automatically puts the more recently placed clips higher in the visual stacking order.

The relative depth numbers of parents and children don't matter. Depth numbers determine only how clips "stack up" in relation to their siblings. Nonsiblings can even have the same depth number because the parent-child hierarchy determines their visual stacking order.

Applying Levels and Depths

With Timeline layers, depth numbering, the parent-child hierarchy, and the object-oriented framework to help you organize content, using levels as an organizing tool may seem unnecessary. Here are some reasons why you might prefer to use movie clips rather than levels as load targets:

  • A movie clip can have a meaningful name, making your program more readable. Levels always have the generic names _level0 , _level1 , and so on.

  • Movie clips offer more fine-grained control of the visual stacking order via layers, the parent-child hierarchy, and depth numbers. If you load into a movie clip, the _root of the loaded SWF becomes the clip into which it is loaded, displacing anything that was there previously but retaining the movie clip's depth number, layer position, and position in the parent-child hierarchy. (Similarly, a JPEG loaded into a movie clip effectively becomes the movie clip.)

  • You can't specify a depth, and you can't use depth to control what you displace, when you load into a level. The _root of an SWF loaded into a level using the loadMovieNum() or loadMovie() global function becomes the _root of the level into which it is loaded, visually displacing anything that was there previously.

On the other hand, levels can have some advantages:

  • Sometimes loading into a level provides the kind of "clean sweep" change that you want. For instance, if you load a movie into _level0 , all levels are unloaded, and the new movie becomes _level0 . If you then load movies into other levels, the movie in _level0 sets document properties such as frame rate, background color , and frame size for all levels. Loading into _level0 can thus provide some basic consistency for a team of developers, for instance.

  • Movie clip properties such as _visible and alpha can be applied to a level without affecting other levels. This capability provides an easy way to change properties for multiple clips, while still excluding other clips.

  • You can get a streaming sound to play across multiple scenes if you load the SWF containing the sound into a level.

  • For FP6 and earlier, if you're loading an SWF that uses the _root identifier in ActionScript statements, the loaded SWF has the best chance of working unchanged if loaded into a level because the _root of the loaded SWF becomes the _root of the level. Loading into a movie clip can cause many problems in this case. For instance, the SWF's _root variables could overwrite existing _root variables of the same name. In FP7, this problem is solved by the _lockroot property of the MovieClip class.

Using Init Objects to Give Properties to New Movie Clips

Both the attachMovie() and duplicateMovieClip() methods of the MovieClip class allow you to specify an init object when creating a movie clip. Flash automatically gives the new clip all the local properties of the init object. This feature is an advantage of the duplicateMovieClip() method over the duplicateMovieClip() global function.

In the following example, myClip is the clip you're attaching to, linkageID is the linkage ID of a clip in the Library, newInstance is the name of the new clip, 1 is the depth of the new clip, and initObj is the name of an object:

 myClip.attachMovie("linkageID", "newInstance", 1, initObj); 

The result is that the newly created movie clip, newInstance , will have all the local properties of initObj . newInstance will not have properties that initObj inherits from its class prototype object.

You can use a function literal for an init object, like this:

 myClip.attachMovie("linkageID", "newInstance", 1, {_x:300, _y:200 } ) ; 

Detecting Movie Clip Collisions

You can use the hitTest() method of the MovieClip class to determine whether any part of a movie clip overlaps either another movie clip or a particular point. The hitTest() method returns true if they coincide, otherwise false . Either or both of the clips, or any child clip, can have its _visible property set to false without affecting the hit test.

In the case of two movie clips, hitTest() checks whether their bounding boxes overlap. The bounding box is the smallest rectangle that contains all the graphics in the clip.

Determining a Movie Clip's Bounding Box

To find a movie clip's bounding box, use the getBounds() method of the MovieClip class. Here's an example:

 boundsObject = myClip.getBounds(coordinateSpace); 

boundsObject is an object with four properties ” xMin , xMax , yMin , and yMax ”that contain the left, right, top, and bottom coordinates, respectively, of myClip relative to the registration point of the Timeline named by coordinateSpace . Thus, in the example, boundsObject.yMin is the top of myClip .

If you don't supply a value for the coordinate space, the clip itself ( myClip in the example) is used. If you don't supply a value for the clip itself, the current Timeline is used. Thus, these two statements are equivalent:

 getBounds(); this.getBounds(this); 

If you need to find only one of the four properties, you can get it like this:

 topOfMyClip = myClip.getBounds().yMin; 

You can use MovieClip.getBounds() for collision detection. In comparison with hitTest() , getBounds() is more flexible, both because it allows you to test separately for each of the four bounds of the clip, and because you are free to adjust the test point. Rather than use yMin for testing, for example, you could use yMin-10 or yMin+10 .

Sample movie getbounds.fla on the CD illustrates the use of getBounds() .


The format for checking for a collision between two movie clips is as follows:

 myClip1.hitTest(myClip2) 

When checking for a collision between a movie clip and a point, you can use either the bounding box of the movie clip or just the areas that actually contain graphics. Set the Boolean "shape flag" parameter to false to use the bounding box or to true to use just the graphics. The format is

 target.hitTest(x, y, shapeFlag) 

For instance, you can use the "point-check" approach to check whether the mouse pointer is over a movie clip:

 myClip1.hitTest(_xmouse, _ymouse, true) 

The "point-check" approach can also be used to check whether a movie clip overlaps the registration point of another movie clip.

If you're having trouble using the "point-check" hitTest approach to check whether a movie clip overlaps the registration point of a movie created with createEmptyMovieClip() , see the "Troubleshooting" section at the end of this chapter, page 535 .


The hitTest() method is typically used with an if statement. In addition, you usually need to test for the collision repetitively. That means putting the test in an event handler. The following block of code checks in every frame whether any part of the graphics in myClip overlaps the point (100, 200) on the Timeline where the block of code resides.

 _root.onEnterFrame = function () {      if (myClip.hitTest(100, 200, true)) {           // do something      } }; 

The x and y values used in a "point-check" hit test are interpreted as x and y points on the main Stage. If you want to check whether one movie clip has collided with a point in another movie clip, you need to use the localToGlobal() method of the MovieClip class to translate the coordinates of the point into main Stage coordinates.

The localToGlobal() and globalToLocal() Movie Clip Methods

Local movie clip coordinates are measured from the movie clip's registration point. Global coordinates are measured from the upper-left corner of the main Stage. To convert from local to global coordinates, or vice versa, you first create an object with two properties, x and y :

 myObj = new Object(); myObj.x = 100; myObj.y = 200; 

Then you use this object as a parameter to the localToGlobal() or globalToLocal() method of the MovieClip class. The localToGlobal() method treats the x and y values as local coordinates and changes them to global ones. The globalToLocal() method treats the x and y values as global coordinates and changes them to local ones.

The methods do not return anything. Instead, they convert the actual x and y values in the object. For example, the following statement converts myObj.x and myObj.y from local coordinates within myClip to global coordinates:

 myClip.localToGlobal(myObj); 

Reducing Hit Test "Misses" with "Invisible Children"

If you check for a collision only once per frame, movie clips that are small in comparison with their relative speed can easily appear to "go through" each other without triggering a hit. For instance, suppose you have two movie clips, each 10 pixels square, moving toward one another at a rate of 20 pixels per frame. If the movie clips are 10 pixels apart in frame three, they will have "gone through" one another by frame four. No hit will be detected .

One way to reduce the number of "misses" is to create an invisible child clip (a clip with its _visible property set to false ) inside one or both of the clips involved in the hit test. If the children are bigger than the parents, the bounding boxes of the parents are those of their invisible children. If the parents are moving at a constant rate relative to one another, you should be able to find some size for the children that consistently triggers hits. If the relative speed of the parents is variable, you can scale the children up as the relative speed increases .

Creating the Illusion of a Perfect Hit

When an "invisible child" or a "hit distance" is larger than a visible graphic, your program may detect a collision before the visible graphics collide. If the movie clips are moving fast enough, the fact that the graphics do not actually "touch" may not be obvious to the eye. On the other hand, there is usually a limit to how large "invisible children" or "hit distances" can be before the illusion of the graphics " bumping into one another" is no longer convincing. You can address this problem by moving the movie clips into the desired positions immediately after detecting the collision.

Executing Multiple Hit Tests per Frame

An option that may work in some applications is to put the hit test in a mouse event handler, such as onMouseMove . Mouse event handlers can fire multiple times per frame.

Another possibility is to use the setInterval() global function to both move the movie clips and execute a hit test multiple times per frame. Performing a new hit test is useless, of course, if you haven't moved the clip since the last test. You should also use the updateAfterEvent() function to refresh the screen.

The setInterval() global function is covered in the next section, "Using setInterval() to Call a Function Repetitively," page 531 .


CAUTION

Executing hit tests multiple times per frame is processor-intensive and runs the risk of slowing down your application to an unacceptable degree.


Using setInterval() to Call a Function Repetitively

The setInterval() global function calls a function or method repetitively at regular intervals while a movie plays. You provide a parameter that specifies an interval in milliseconds . The two formats are as follows:

 setInterval(  function,interval  [,  arg1, arg2  , ...,  argn  ] ) setInterval(  object, methodName,interval  [,  arg1,arg2  , ...,  argn  ] ) 

For instance:

 setInterval(myFunc,100); setInterval(myObj, myMethod, 100); 

This function looks straightforward enough. In reality, however, as the following two examples illustrate , the relationship between the interval you specify and the actual interval of execution is anything but obvious.

  • If the specified interval is less than the frame length (the time required to play one frame), the function or method is called at some point after the interval expires . However, the actual interval is often 10 times the specified interval.

    For instance, a frame rate of 1 frame per second (fps) is equivalent to a frame length of 1000 milliseconds (1 second). If you specify an interval of 10 milliseconds at 1 fps, the actual interval may be 100 milliseconds ”10 times the interval you specified.

    In addition, the interval can vary depending on how processor-intensive the function or method is, what other processing is competing with it, and how powerful the computer is.

  • If you specify an interval that is greater than the frame length, the function executes on the next frame transition. For example, at 1 fps, if you specify 1500 ms (a frame and a half), the function executes every other frame.

Determining the Actual Interval

You can use the following function to determine the actual interval resulting from any given specified interval. Just change the interval value in the first line.

 interval = 100; clearVal = setInterval(measureInterval, interval); function measureInterval () {      if (restart == undefined) restart = 0;      else restart = start;      start = getTimer();      var elapsed = start - restart;      trace("interval:  "+ elapsed);      updateAfterEvent(); } 

Note the updateAfterEvent() function in the last line, which refreshes the screen.

Stopping or "Clearing" the Repeating Function Call

setInterval() returns an interval identifier that you can pass to the clearInterval() method to stop the function from executing. For instance, in the code in the previous section, the interval identifier is clearVal in the second line. Thus, the following line cancels the execution of measureInterval() :

 clearInterval(clearVal); 

CAUTION

setInterval() can degrade the performance of your movie. The more intervals that you run simultaneously , the more the performance of your movie is likely to suffer.


Dragging and Dropping Movie Clips

The capability to "pick up" a graphic on the screen, move it to another location, and "drop" it is useful in graphical user interfaces and also in games.

startDrag() and stopDrag()

You can implement drag-and-drop behavior by attaching two event handlers to a movie clip: an onPress event handler containing a startDrag() statement and an onRelease event handler containing a stopDrag() statement.

When you press the mouse button, the movie clip named in the startDrag() statement is "locked" to the mouse pointer and moves as the mouse pointer moves. When you release the mouse button, the stopDrag() statement in the onRelease event handler executes, and any currently draggable movie clip is no longer draggable.

Notice that the stopDrag() method does not apply just to the movie clip named in the stopDrag() statement. It disables dragging for any currently draggable clip. Thus, it is exactly equivalent to the stopDrag() global function. The movie clip name is like a comment, reminding you which clip you believe should currently be draggable.

The stopDrag() method takes no parameters. For instance, here's the stopDrag() portion of a typical drag-and drop implementation:

 myClip.onRelease = function () {      myClip.stopDrag(); }; 

Here's the startDrag() portion of a typical drag-and drop implementation:

 myClip.onPress = function () {      myClip.startDrag(); }; 

The optional parameters for startDrag() are discussed in the next section.

startDrag() Options

startDrag() has optional parameters to accomplish these two goals:

  • Locking the movie clip's registration point to the mouse pointer. This locking occurs if the optional lock parameter is true . If lock is false , the movie clip and the mouse pointer retain the spatial relationship they had when the user first pressed the mouse button. If you don't supply a lock argument, it is treated as false .

  • Constraining the area within which the movie clip can be dragged. The area is specified by four parameters: left, top, right, bottom . For instance, this statement constrains myClip to a rectangle starting at the upper-left corner of the stage and going 100 pixels to the right and 200 pixels down:

     myClip.startDrag(true, 0, 0, 100, 200); 

Flash developer Andy Halls spacelisten.fla on the CD provides an interesting example of drag-and-drop functionality in Flash MX.


Using the _droptarget Movie Clip Property

When a user drags and drops something, where the user drops it is often critical. Dropping a file into the trash triggers something quite different in a program than dropping it into a folder. The read-only _droptarget movie clip property gives you an easy way to test where a user has dropped, or may be about to drop, a movie clip.

If the registration point of a dragged clip is over any part of another clip (whether or not the mouse button has been released), the _droptarget property contains the path of that clip. If the dragged clip is not over any other clip, the _droptarget property is undefined .

The _droptarget property goes back to Flash 4, and it provides the path as a string in Flash 4 "slash notation," as shown in these examples:

 /myClip /myClip/child 

Working with Dynamic Masks

You can manually create a mask layer that reveals those portions of lower layers that are under graphics in the mask layer. Where the mask layer has no graphics, content in underlying layers is hidden.

For more information on setting up masking manually, see "Creating a Mask," page 356 , in Chapter 16, "Working with Vector Graphics and Bitmaps."


You can use ActionScript to animate masks. For instance, you can move, rotate, and scale masks.

The setMask() method of the MovieClip class allows you to designate one clip as a mask for another clip programmatically. For instance, in the following statement, myMaskedClip is the instance name of a movie clip to be masked, and myMask is the instance name of the masked movie clip:

 myMaskedClip.setMask (myMask); 

Mask and maskee form an exclusive "monogamous" pair: You can have only one mask per maskee and only one maskee per mask. As soon as you set a new mask on a clip, any previous mask stops functioning as a mask. Usually, this limitation is not severe because both the mask and the masked movie clips can have multiple frames, multiple layers, and multiple scripted child clips internally.

If you want to turn off masking for a particular masked clip without designating a new mask, use null for the mask clip parameter, as shown here:

 myMaskedClip.setMask (null); // myMaskedClip is no longer masked 

Here are some points to remember about dynamic masking:

  • Masks are always invisible, while they are masks . As soon as a mask clip is no longer being used as a mask, it becomes visible ”not usually the behavior you want. Therefore, before taking a mask out of service, you should probably make it invisible, like this:

     myMaskedClip.setMask (myMask); myMask._visible = false; myMaskedClip.setMask (null); 

  • You can use the drawing API to create masks. However, just as strokes (lines) have no effect in masks that you create manually, lines created with the drawing API are useless in masks. Only the filled portions of the clip have the capability to mask.

    For more on the drawing API, see drawing.htm on the CD accompanying this book.


  • graphics/new_icon.jpg In Flash MX, device fonts in a masked movie clip were displayed but not masked. They showed up, even though they were not under the mask. You had to embed fonts to cure this problem. In FP7, device fonts are masked properly. See the sample files (Help, Samples, Using Device Font Masking) for more information.

  • Setting a movie clip to mask itself accomplishes nothing, as in this example:

     mc.setMask(mc); // does nothing 

Peter Halls scratch.fla on the CD uses the drawing API and dynamic masking to create a scratch-and-win game.




Using Macromedia Studio MX 2004
Special Edition Using Macromedia Studio MX 2004
ISBN: 0789730421
EAN: 2147483647
Year: N/A
Pages: 339

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net