Chapter 4: Creating Instances Dynamically: The Button Menu


In this chapter, we're going to look at what are called dynamically instantiated movie clips . This will be the basis for deciding at run-time if we need to add new clips to the stage. We'll do this by using a special kind of event handler and some built-in functions. After we're done, we'll look at the development of another large project. It won't actually be a game, but what we're going to make will be useful to our games in the future, and its development will be more complex than the game for Chapter 3, "Programming Interactivity: Mouse Chaser ." After we're done with it, we won't have time for a second project in this chapter. However, please don't overlook this chapter's importance just because it doesn't contain a game. The concepts here will be used in every game through the rest of the book.

Dynamic Movie Clip Instances

So far, all our examples have involved the use of clip instances on the stage. We created these instances by dragging them there from the library. But what would happen if we wanted to create a game where there were 10 balls moving about the stage, and when the user clicked on a ball, it would divide into 2 new balls? In this case, we can't drag all the instances we need from the library because we only know about the first 10. After the user starts clicking, we'll need new instances, but we can't very well drag stuff onto the stage while the user is playing our game. Or can we?

Exporting a Library Symbol

Before we dive into the subject of how to create dynamic clip instances, we need to prepare a little bit. When you publish a Flash movie by default, only symbols that have been instantiated on the stage will be exported into the final .swf file. That means that if you have some movie clips in your library and they do not appear on the main timeline or in a timeline of some other movie clip that is instantiated on the stage, the art and script for that symbol will not exist in your final .swf file. This allows you to have a great deal of art in your library, but to be free from having to remove art you don't use from your library before you publish. Obviously, if there is art in your final .swf that is never used in your movie, it will do nothing but increase your file size . By default, Flash only exports symbols that it knows are going to be used in the final movie.

When we create a clip instance dynamically, we might want to use a clip that was not originally instantiated on the stage. In fact, our games in the future will be published with a completely blank stage. In that case, Flash would by default leave out all our library symbols, and we would have no resources to instantiate at run-time.

Fortunately, we are given the option to force Flash to export a symbol into the final .swf, even if it's not on the stage. The process is quite simple, so let's walk through it now.

Create a new movie, add a new symbol, name it ball , and draw a circle inside it. Then right-click on the ball clip in the library and select Linkage from the menu. You should see a dialog box named Linkage Properties that looks like Figure 4.1.

click to expand
Figure 4.1: The external linkage of a library symbol is used when a clip is created dynamically.

The Linkage Properties dialog box contains several check boxes, but the one we need is the first one: Export for ActionScript. Check this box and notice that the Identifier field, which was previously grayed out, is now active and has been filled out with the name of the symbol, ball . The dilalog box should now look like Figure 4.2. The Export in First Frame check box has also been checked. This is exactly what we want, so click the OK button. The ball symbol is then exported into the final .swf regardless of whether it's on the stage. We can now happily exclude it from the stage and create an instance of it dynamically with ActionScript.

click to expand
Figure 4.2: Checking Export for ActionScript lets you type an identifier.
Tip  

Another interesting feature in this window is the ability to tie a symbol in the library to a class file. That way, just the act of dragging a symbol on the stage or dynamically instantiating it causes it to compile with the code it needs to do its job in the Flash file.

Tip  

I always leave the identifier name the same as the library symbol name. This avoids confusion because when you actually create the dynamic instance, you will refer to the linkage identifier and not the library symbol name. By keeping both the identifier and the symbol name identical, this potential confusion is eliminated.

Creating a Dynamic Clip Instance

Flash gives us the ability to instantiate a movie clip using ActionScript. This is crucial to use as game programmers because we're going to need to create the vast majority of our instances interactively in response to the user. As usual, the only way to create interactivity with the user is to use code. There is a built-in function called attachMovie that will allow us to attach a new instance. Generally , we call it using the _root reference so that our new instances are attached to the main timeline, but sometimes we'll want to attach a new instance to an existing instance, in which case we call the attachMovie using a movie clip reference. The general form is as follows :

 MovieClip  myMovieClip  .attachMovie(  idName  ,  newName  ,  depth  [,  initObject  ]); 

The first argument, idName , is the name you used when you exported the clip. As I said before, in my code, this is always identical to the symbol name in the library. This argument can be summed up as "the export name of the library clip I want to instantiate." This argument is a string and not a reference. That's an important distinction to make. If you try to put a value here that is not in quotes, it better be a string variable, or the attachMovie call will fail and do nothing.

The second argument, newName , is the variable name you want this new instance to have. Later when you want to use script to refer to this newly attached clip, this is the name you will use.

The third argument, depth , indicates the depth you want the new clip to occupy within its host clip. The concept of depth deserves its own section, which we will get to soon. For now, simply realize that this variable determines which clips will appear to be on top of which, similar to the stacking order of layers .

Caution  

You can do interesting things by using a newName argument that has both a name and a numerical value concatenated . This strategy is demonstrated later in this chapter in the section "Dynamic Clip Names : eval" and again in Chapter 5, "Arrays: Match 'Em Up and Sliders ," in the discussion of arrays.

The fourth argument, initObject , allows you to use an object to populate the properties of the new clip. Notice that this last argument is optional. We won't be using this last argument for a while, so don't worry about it yet. After we talk about objects, initObject might be added to the mix.

The return type is listed as void in the ActionScript Dictionary, but the function actually returns a reference to the newly instantiated clip. This is shown in my general form with the return type of MovieClip .

Now let's look at some examples. Remove all instances from the stage and place the following script on frame 1 of the main timeline:

 _root.attachMovie("ball", "ball1", 1); 

The function is called on the _root reference, indicating that the instance should be attached to the main timeline. The first argument is a literal string "ball" , which is the name we gave to the ball symbol when we exported it in the linkage properties. The second argument is another literal string "ball1" , so in the future when we want to access the ball, we can use the reference name ball1 . The third argument is given a value of 1, and it indicates that this should be the deepest clip of all dynamically attached clips on the stage.

Caution  

If you try to attach a movie clip dynamically without exporting the symbol in the library, the attachMovie function call fails without error. If you think your script is correct but the new clip just isn't showing up on the stage, check to make sure you exported the symbol.

Now let's see an example where several clips are created at once. Add the following code to the end of the script in frame 1:

 attachMovie("ball", "ball2", 2); attachMovie("ball", "ball3", 3); 

Notice that this time the _root reference is left off. Because this script is attached to a frame in the main timeline, the locally scoped timeline is used, which in this case is the root (main) timeline.

If you test the movie now, you'll see only one clip. That's because all three clips are right on top of each other. The default position of a newly attached clip is (0,0). Let's move the clips around so that we can see them. Add the following code to the end of the script in frame 1:

 ball1._x += 100; ball1._y += 100; ball2._x += 160; ball2._y += 100; ball3._x += 220; ball3._y += 100; 

You should now see all three clips, as in Figure 4.3.


Figure 4.3: Three clips are created. The order in which they are stacked depends on their depth setting.

If the clips do not overlap, adjust the _x and _ y values in the preceding script so they do. Notice that ball3 is on top of ball2 , which is on top of ball1 . That's because of the values we gave to the depth argument, discussed fully in the next section.

Let's look at an example of attaching a movie clip to another movie clip. Remove the script on frame 1 and replace it with the following:

 attachMovie("ball"," ball1",1); ball1._x = 200; ball1._y = 200; ball1.attachMovie("ball", "ball2", 1); ball1.ball2._x += 20; 

There is a lot going on here, so let's look at it one piece at a time. First, a new ball is attached to the main timeline, and its name is set to ball1 . This new instance is then moved to (200,200) on the stage. After that, attachMovie is called using ball1 as the reference variable. This attaches a new ball called ball2 to it. There are several things to notice in this function call.

First, the name of the new ball instance is ball2 , but it could also have been ball1 . "But ball1 is already used as an instance name," you say. Yes, but that instance name is used on the main timeline to refer to the first ball instance we attached. Because we are attaching the second ball to the first one, the second ball will have a deeper level of scope than the first. For that reason, they can share variable names.

Second, the depth of ball2 is set to 1 instead of 2. That's because the depth of ball2 is a depth within the first clip ( ball1 ). That means that ball2 won't interfere with the depth setting of ball1 , which was also 1, but relative to the main movie.

Both of these things have to do with the relative nature of movie clips in a hierarchy. Both their names and their depths are scoped locally, so they can be repeated as long as they are not repeated within the same scope.

Movie Clip Depth

We've skirted the issue of clip depth for the past several pages, and it's now time to take a hard look at it. The first thing to mention is that there is a complex set of rules that govern the order in which clips are stacked on the stage. The first rule says that there are two different types of depth: depth determined at author-time , and depth determined at run-time. We'll look at both types individually as well as see how they interact with each other.

Depth Order Rules at Author-Time

When clips are dragged from the library to the stage, they are said to be instantiated at author-time. The rules that govern their depth are as follows:

  • Clips that are dragged into the same layer appear in the order they are instantiated. Therefore, a newly instantiated clip will appear on top of an older one.

  • Layers in a timeline provide logical depth breaks. Therefore, all clips instantiated in a higher layer will appear on top of clips in lower layers, regardless of when they were instantiated.

  • Layers can be moved up and down relative to each other by dragging the layer name in the timeline above or below other layers in the same timeline. Moving the layers changes their relative depths.

  • All clips dragged into a timeline will appear below any clips created dynamically in the same timeline, regardless of the dynamic clip's depth argument in its attachMovie call.

Using these rules, you can change the ordering of instances you have dragged onto the stage. Now let's talk about the rules that govern dynamic clip depth.

Depth Order Rules at Run-Time

When clips are attached dynamically with an attachMovie (or duplicateMovie ) function call, they are said to be instantiated at run-time. The rules that govern their depth are as follows:

  • Clips that are attached to the same timeline dynamically are stacked in order of the depth argument given to them in their attachMovie calls. The higher their depth setting, the higher in the stacking order they will be.

  • Clips that are attached dynamically to different timelines are stacked in order of their parent clip depth, regardless of their individual depth setting. This is due to the relative nature of clip depth.

  • All clips that are created dynamically appear above all clips that were dragged into the same timeline, regardless of the dynamic clip's depth argument in its attachMovie call.

  • If a clip is attached to a timeline at the same depth as an existing clip, the existing clip will be automatically removed from the timeline to make room for the new clip. For this reason, it's important to make sure you only attach clips at a depth that no other clip exists at.

Caution  

Notice in the last dynamic rule that if a clip is attached at a depth that another clip exists at, the older clip is removed. That implies that only one clip can exist at any given depth. It's important to remember this, or you'll find your movie clip instances vanishing when other clips at the same depth replace them.

By combining these rules with the rules for author-time instantiated clips, you can stack clips in any fashion you choose.

It's possible to change the depth a clip is positioned at during runtime. The functionality to do this is discussed in Chapter 6, "Objects: Critter Attack ."

Note  

We'll be creating the vast majority of our clips dynamically, so you will usually be able to forget about author-time instantiation rules and concentrate only on the rules for dynamic clips.

Removing Dynamic Clips

Sure enough, as soon as you create a new alien or monster movie clip, the player will shoot it and you'll need to remove it. Fortunately, Flash allows us to remove our dynamically instantiated clips with the removeMovieClip function. This function is called upon a reference to the clip we want removed. The following is an example of first attaching a new clip to the main time-line and then removing it:

 attachMovie("ball"," ball1",1); ball1.removeMovieClip(); 
Caution  

It can become difficult to maintain an order of depths when certain types of clips must be above or below other types. With hundreds of clips to deal with, you must keep track of all your depths and make sure not to attach a new clip at a depth that will cause an existing clip to be removed. In the future, we will use empty movie clips to help organize our depths.

It's as simple as that. You just call the removeMovieClip function on a reference to whatever clip you want removed. Any clips that are attached to that clip will be removed as well.

Tip  

If you need to remove a clip that was created at author-time, you can do so by first changing its depth (explained in Chapter 6) and then calling removeMovieClip on it.

Duplicating Dynamic Clips

Many times you will create a new clip instance and then spend several lines of code setting its initial property values. Sometimes it's more convenient to tell Flash, "See that ball clip on the stage that has its properties already set up? I want another ball just like that one." Fortunately, Flash understands this. If you have already attached one clip and set its properties, and you now want an exact duplicate clip (with the exception of its name), you can do so by calling the duplicateMovieClip function. This function is invoked on a reference to the clip that you want duplicated . The general form of the function is as follows:

 MovieClip  myMovieClip  .duplicateMovieClip(  newName  ,  depth  [,  initObject  ]); 

The arguments are identical to the attachMovie function except that the first argument has been removed. In duplicateMovieClip , Flash already knows what clip you want to use as your template; you called the function on a reference to it. The timeline that the clip is created on will be the same timeline that its duplicate clip has. All that's left to do is set the new clip's reference name and its depth.

The following is an example of the duplicateMovieClip function's use:

 attachMovie("ball"," ball1",1); ball1._x = 200; ball1._y = 200; ball1._alpha = 50; ball1._rotation = 10; ball1.duplicateMovieClip("ball2", 2); ball2._x += 20; ball2._y += 20; 
Note  

Please note that any event handlers that are attached to the instance are duplicated as well. In addition, a duplicated movie clip always begins playing at frame 1, regardless of which frame the source clip was at when duplicated.

As you can see in Figure 4.4, ball1 is duplicated after being attached and having some of its properties set. The duplicate clip is named ball2 , and then its position properties are shifted slightly. This is necessary because ball1 's position will be used for the duplicate clip, essentially placing ball2 directly on top of ball1 . The rest of ball2 's properties are identical to ball1 's.


Figure 4.4: After you have set a clip's properties, you can duplicate it and save yourself the trouble of setting up many clips the same way.
Tip  

In the future, we will use more sophisticated methods to create movie clips with preset property values. These techniques include both initialization objects handed to the initObject argument of attachMovie, as well as prototype classes from which multiple instances are created.

Creating Empty Clips

Another trick that Flash gives us is the ability to create a new movie clip that is completely empty. What is the purpose of creating an empty clip? There are several advantages to doing so. One example involves creating an empty clip and then attaching several other clips to it. After this composite clip has been assembled , you can move the entire thing by changing the position of the composite clip, as opposed to moving each piece separately. Flash also gives us the ability to load pictures at run-time into an empty clip, which can drastically reduce loading times for movies that have a large number of photos. Finally, Flash gives us a set of drawing tools that we can use to draw lines and fills in an empty movie clip. We'll talk about the drawing tools in Chapter 6. These are all good reasons to use empty clips, and we'll be taking advantage of them in the future.

Creating an empty clip requires the createEmptyMovieClip function, which has the following general form:

 MovieClip  myMovieClip  .createEmptyMovieClip(  instanceName  ,  depth  ); 
Tip  

Using empty clips to group other clips reduces the amount of effort necessary to maintain the depth settings of all your clips. For example, when you have hundreds of clips on the stage at one time and thousands over the life of the game, you must be careful to assign depths to newly attached clips that are unused. By consolidating similar clip types within one empty clip, you can better organize your depths.

The function returns a reference to the newly created empty clip, is called upon a reference to the timeline that the clip should be attached to, and requires two arguments. The first argument is a name for the new clip, and the second argument is depth. These arguments are the same as the second and third arguments in attachMovie . The following is a script to create an empty clip, attach five ball clips to it, stagger them, and then reduce the _alpha value and position of the entire compound clip:

 createEmptyMovieClip("myBalls",1); for(i=1; i<6; ++i){     myBalls.attachMovie("ball", "ball" + i, i);     eval("myBalls.ball"+i)._x += i*100; } myBalls._alpha = 50; 

Take a look at Figure 4.5 to see what it looks like.

click to expand
Figure 4.5: When clips are attached to a parent “clip and the parent clip has a property changed, all child clips are displayed with the changes applied to them as well.

Several things are going on here that you should take notice of. To begin with, I'm creating an empty clip called myBalls at a depth of 1. Because there is no reference before the call, Flash uses the local timeline ”in this case, the root timeline.

Tip  

for loops are generally indexed starting at 0 because arrays are indexed starting at 0. You'll learn about arrays in Chapter 5.

From there, a for loop iterates five times with an index from 1 “5. This breaks the traditional practice of indexing a for loop starting at 0, but it works out better inside the body of the for loop where the index i is used for both the ball instance names and their depth.

Inside the for loop body is an attachMovie called on myBalls . This creates a new ball instance attached to the empty clip, names it using the literal string "ball" plus the loop index (so that all names are different), and sets the depth with the loop variable.

The second line in the loop body uses a call to a function you haven't seen yet: eval . This function is explained soon, but for now, I'll simply tell you that eval is used to dynamically construct a variable reference at run-time. In this case, eval does this for the reference to the ball that was just created and moves it over by 40 times a factor of i . This staggers the clips by placing each newly made clip 40 pixels further than the last.

Finally, after the loop body, the alpha value of the formally empty clip is reduced. Notice that all the balls appear to be half transparent. Likewise, when you change any of the properties of the parent clip (the formally empty one in this case), all attached clips are affected. Be careful, though: The actual values of the child clips are unchanged. Even the _x and _y properties are unchanged. That's because even the position properties are relative to the parent clip.

Caution  

The idea of child clip properties being relative to their parent's properties is critical. The _alpha property, for example, actually represents the percentage of opacity relative to its parent opacity. If the parent is at 50 and the child is at 100, the child has 100 percent of its parent's 50-percent opacity (half visible). Likewise, if the parent is at 50 and the child is at 50, then the child will have 50 percent of its parent's 50 percent, giving the child an actual opacity on the stage of 25 percent (nearly invisible).

You'll see numerous examples of empty clips as we move forward. As I said before, empty clips give us some distinct advantages when stacking clips in games where there are massive amounts of clips during the game's life.

Dynamic Clip Names: eval

The eval function's purpose is to allow you to create a variable name at run-time. That way, the variable name can be constructed at run-time, allowing you to dynamically create names to go with your dynamically created variables .

The way I think about eval is to say that it is really a "string to reference" conversion function. You hand eval a string, and it returns to you a reference to the object with that string's name. I know this seems confusing at first; it's that way for all of us. But stick with me, and it will become clear as we go.

The general form of eval is as follows:

 reference eval(  expression  ); 
Note  

In Chapter 5, we will be talking about arrays, which will allow us a more elegant way to dynamically create a name than eval gives us. For that reason, you won't be seeing eval much after this chapter. This, of course, does not mean you're wasting your time learning it. eval is a function shared by JavaScript that occurs fairly regularly in the programming world.

The function takes one argument, an expression, and this expression should resolve to a string. The function returns a reference to an object named by what was given as the string argument. So you concoct a string using literals, variables, and string concatenation and hand it to eval . What is returned to you is a reference to the dynamically created object that you gave that name to.

It can be difficult to understand why we need this function (or something like it). When we dynamically create an instance in Flash, the name we give to the clip might not be known to us at run-time. You've seen me do this in the previous example when I created a movie with attachMovie("ball", "ball" + i, 1) , where the ball name was created using the i from the loop index. We can't use that same technique to refer to a property of the ball later because we can't use a literal string with dot notation. Instead, we are allowed to give a literal string to eval , combined with some variables and whatnot, to construct our name dynamically. We assign the return value of eval to some local variable; then we can refer to properties of it. This is what we did in the example from the previous section when eval was used to resolve the reference myBalls.ball1._x .

To reiterate, when we create clips dynamically, we are forced to create their names dynamically. The problem is that we cannot later refer to those dynamic names using dot notation. Instead, we have to use a function such as eval to generate our reference at run-time.




Macromedia Flash MX 2004 Game Programming
Macromedia Flash MX 2004 Game Programming (Premier Press Game Development)
ISBN: 1592000363
EAN: 2147483647
Year: 2004
Pages: 161

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