Dynamic Creation of Graphics

As data drives the content of this game, we want it also to start driving the game's graphic format. The most rigid feature, at the moment, is the predefined set of four AnswerOptions . The best way to make it more flexible is to spawn AnswerOption instances as they are needed.

Flash has a mechanism for saving prototypes of MovieClips and then instantiating them from within a running application. (The prototypes can even reside on a remote server and be accessed by URL!)

To make a symbol available for instantiation, right-click on its name in the Library panel. The Linkage panel appears. It seems foolish to change the name at this point, but Flash invites you to anyway (Figure 3.7). Resist.

Figure 3.7. Exporting a Symbol


With this symbol available, we can dynamically generate the display by instantiating as many AnswerOptions as the data requires ”not always four. The attachMovie method allows us to spawn the new buttons (Figure 3.8).

Figure 3.8. Data-Driven Graphics


The code executes as each round begins. It supplements Flash's automatic creation of graphic elements with our custom code.

 for( var i=0; i< r.a.length; i++){     this.attachMovie("AnswerOption","answer"+i,++_root.maxdepth+5555);     setProperty( "answer"+i, _y,  50*i);     eval("answer"+i).text=   r.a[i].text;     eval("answer"+i).score=  r.a[i].score;     eval("answer"+i).comment=r.a[i].comment;     }   stop(); 

For each option in the array of answers, we punch out another copy of the AnswerOption object (button, text treatments , sounds, and a little code).

AttachMovie is a method of any MovieClip. Here we have round call its own method and attach the new instances as its own direct children. We supply three arguments:

  • The name of the prototype symbol. This is the name we published from the Library using the Linkage panel.

  • The names of the instances : answer0, answer1, answer2 , and so on

  • The depth. We hold our nose and write in this global.



This is a weakness in ActionScript. The scripter is expected to know the layout of the object levels at runtime.

Depth is an internal parameter that determines display list priority, among other things. However, like array indices, depth values must be unique. And worse , unlike array indices they must be unique throughout the Flash session, not just within a limited namespace.

No matter how many Flash MovieClips are running, two objects cannot share the same depth. The second object to be assigned will silently annihilate the first.

Yet the programmer cannot read the depth of an object, as he can the other properties. Nor can the programmer inspect system internals through some sort of read-only access functions, perhaps nextAvailableDepth() or isDepthUsed() . Even a desperate programmer willing to experiment is out of luck: the depth of an object cannot be determined at authoring time. It can vary tremendously based on the context of the object's use and the history of any session.

Common practice (and ours) is to pick a large number arbitrarily and assign depths consecutively from that point. Our counter is a global in the root. Any code that uses this counter anywhere increments it, which prevents our depth assignments from colliding . We also add a large constant to avoid the danger that an uninitialized depth counter could cause the destruction of the root or other important objects, which presumably reside at low depths.

But other assignments, from other people's code and from Flash's own operation, can still collide with our levels.

Every error that can happen eventually will. And sooner than we think. The only thing predictable about the resulting bug is that it will be very hard to identify.

As we create the answer instances, we set the _y property (the relative vertical positioning) on each ”as a function of i ” so they stack up properly. We also plug the appropriate values into the text, score , and comment fields.

In a more sophisticated version, we would hope to find a more thoughtful consideration of r.a.length ”the number of answer options for this question. For example, we might program _y as a more complex function of i that squeezes and stretches the spacing. But now we are happy that we can create answers at will and that they look pretty good with two, three, four, or five choices.

But creation is only half the job. We also need to be able to destroy the answers when we are through with them. We have done this casually so far ”almost unconsciously. In the static versions of round , all four answer options and the question text are in the layer q&a . This layer has contents only on frame 1 of the movie. When we enter that frame, Flash creates all the graphic elements. When we move out of the frame to Win or Lose , Flash destroys the objects and cleans up (Figure 3.9).

Figure 3.9. Automatic Creation and Destruction


But now that we have created our own graphic structures, they are not located on the timeline. Who will destroy them? We will, of course. It would help if Flash offered an onExit frame event. It doesn't: All frame code implicitly handles the onEnter frame event. We have to split the Start state into two keyframes. The first is entering the state. It creates and displays all the structures and waits for user action. The second keyframe cleans up memory and the screen and decides what to do next . (If this sounds like the waiting and thinking steps of the AnswerOption earlier, it should. As that functionality is assumed by its parent, AnswerOption gets simpler.)

In fact, it becomes very simple (see Figure 3.10). It records its own name as the chosen answer in its parent, the round.

Figure 3.10. Simplified Answer Option



It then tells the round to play, which takes it to the exit frame of its Start state.


The code on this frame falls into two sections. The first removes the MovieClips from memory. To be easy on the memory manager, they are removed in LIFO order. (Last in, first out sometimes minimizes memory fragmentation.) RemoveMovieClip is a method of instances that have been created dynamically.

 for( var i=r.a.length-1; i>=0; i--){   eval("answer"+i).removeMovieClip();   } 

If we are on this frame, the player has clicked on one of the options, and the option has stored its instance name in our variable chosenAnswer . (This instance name is something like "answer2".) Skip past the six letters in "answer" and convert the rest to a number. This is the option chosen ”it is an index into the array of answers.

 chosen=Number( chosenanswer.substring( "answer".length)); 
Figure 3.11. Destroying Graphics


We use it to display the proper comment and to direct the transition to either the Win or the Lose state.

 comment="<p align=center>"+r.a[chosen].comment+"</p>"; if( r.a[chosen].score>0 ){     gotoAndPlay ("Win");        } else{     r.a.splice( chosen, 1);     gotoAndPlay ("Lose");     } 

Finally, as a nice detail in the case of a wrong answer, we use splice() ”a method available to all array objects ”to delete this used-up option from the array. When the first frame plays again and round generates its display, it will list one fewer option. The copy of this array in the base movie is unaffected; we are altering only the data in round 's private copy.

Flash and XML[c] A Developer[ap]s Guide
Flash and XML[c] A Developer[ap]s Guide
ISBN: 201729202
Year: 2005
Pages: 160

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