Workshop Chapter 18. Fixing Broken Scripts

CONTENTS
  •  Card Flip
  •  Card Snap
  •  Circle Move
  •  Multiple Choice
  •  Rotating Box
  •  Word Float
  •  Yellow Box
  •  Click and Hold
  •  Objects
  •  Move Multiple
  •  Summary

In Chapter 6, "Debugging," you learned some of the ways to debug movies. In this workshop chapter, you're given the chance to put those skills to work. Your task is to download the "broken" Flash files (from www.phillipkerman.com/actionscripting/) and fix the errors that I have purposely included in them. None of these files will work without adjustment. Most of the fixes are simple after you track them down.

The files you'll work with in this workshop chapter either don't work at all or they work incorrectly. For each file, you'll find a description of what's supposed to happen in the following sections. Then, if you choose to peek, you'll find a clue to the problem. Finally, I provide the solution and a short explanation. The difficulty of the problems in these files ranges from simple to quite difficult. (It's actually hard to gauge how difficult it is to find and fix a bug.)

One thing to remember in your real projects is that most bugs require only a simple fix. Sometimes the fix is very involved, but usually, it's just a minor oversight you made while programming. In this workshop chapter, the "harder" bugs to fix usually require a little more digging to find. For example, you might have to edit the script in a keyframe nested inside another clip. For every bug you find, you should feel really good about your debugging skills. If nothing else, you'll be rewarded with a movie that functions.

Card Flip

The file ("01_card_flip.fla") is just the start of a card-game movie that could evolve to be three-card monte. Although this file will take many steps to finish, the current version is supposed to allow the user to click on any card. The card then animates by reducing its _yscale until it reaches 0. Then it's supposed to increase its _yscale until it reaches the normal 100 again, where it stops. Don't worry about showing the face of the card we have bigger problems! The current version appears to scale down alright, but then it seems to scale up forever! All the code is in a keyframe inside the Card Movie Clip (so that it's automatically duplicated for each instance). See whether you can find and fix the bug.

Hint

The error is probably different than it appears. If you edit the master version of the Card symbol and place an odd graphic shape inside the rectangle shape (see Figure W18.1), you'll see that the next time you test the movie, the error is not that the _yscale increases too much, but rather that it starts to decrease and keeps decreasing below 0 (where it will appear upside down). Don't worry about _yscale going above 100; make sure that it never goes below 0, and you'll be on the right track.

Figure W18.1. Placing a splotch on one part of the card provides a clue to the exact problem.

graphics/34fig01.gif

Solution

The problem can be found in the two if statements contained inside the onEnterFrame callback:

if (this._yscale==0){   this.direction=12;  }  if (this._yscale==100){   this.onEnterFrame=null;  }

The variable direction starts at -12 and is supposed to turn to 12 when the _yscale reaches 0. However, the if statements check to see whether _yscale is equal to 0 or 100. The problem is that if _yscale jumps by 12 for each enterFrame event (while it's animating), it will jump right past 0. Change the first condition from this._yscale==0 to this._yscale<0. Test the movie, and you'll see that the other if statement must be fixed, too. Change its condition to this._yscale>100. Finally, you'll want to make one more touch. In addition to clearing the onEnterFrame callback (within the if(this._yscale>100) statement), add one line that reads this._yscale=100. This ensures that when _yscale goes past 100, it is set back to 100. Here is the final script:

this.onPress=function(){   this.direction=-12;    this.onEnterFrame=function(){     this._yscale+=this.direction;      if (this._yscale<0){       this.direction=12;      }      if (this._yscale>100){       this._yscale=100;        this.onEnterFrame=null;      }    }  }

By the way, another solution would be to change both values for direction (12 and -12) to a number that divides evenly into 100 perhaps 10. I think that's a poor solution for this workshop, however, because it will change the "speed" of the animation. Besides, even if did work, it's always safer to use a conditional like < or <= than == (when appropriate).

Card Snap

In this broken file ("02_card_snap.fla"), I've added quite a bit to the preceding Card Flip file (although we're not playing three-card monte yet). We're successfully assigning a unique random number to each "card" instance's cardNum variable. I even threw in a Dynamic Text field just for testing purposes. (We don't want the user to know the value of cardNum for each card.) The randomization works, as does the animation, but we want the card to reveal its face in the middle of the animation. The instant the _yscale reaches its minimum (index 2 of the tweenLocs array), we want to make the instance called card jump to a frame within its 11-frame movie. We think most of it is built right, but we never see the card's value!

Hint

Code appears in both the first frame of the movie and in the first frame of the symbol named Clip w-Card. Because the randomization is working fine, you don't need to bother with the code in the main timeline.

The solution involves an easily misunderstood fact about the keyword this. Namely, when you say this inside a clip, you're addressing the clip; when you say this inside a function, you're addressing the function; and when you say this inside a callback, you're addressing the clip to which the callback is attached. If all that makes sense, you should look for places in the code that don't use this but should.

Solution

It's much easier to fix this file than it is to understand why the fix works. All you need to do is make sure the ActionScript editor preferences are set to display line numbers and then change line 21 from how it currently reads:

gotoAndStop (destination+1);

to instead read

this.gotoAndStop (destination+1);

Including this addresses the card instance (the one with all the card faces). The way it was (without this), the code was addressing the timeline of the symbol Clip w-Card (which had only one frame). I've said in the past that if a clip address doesn't begin with _root, _parent, or this, you can always add this (because Flash will anyway). What's confusing in this case is that the this that Flash inferred is the timeline where the code is placed not the timeline of the card instance. The lesson here (besides that the keyword this can be confusing) is that it's best to be explicit.

Circle Move

This is a pretty easy one, but it's likely to trip up even the most experienced ActionScripter. In the file ("03_circle_move.fla"), you'll find three instances of the same Movie Clip but only two bounce back and forth when you test the movie. Naturally, all the code is inside the master symbol so that it's not duplicated. That makes it doubly frustrating because you would expect the same code to behave the same. That is, when you fix the script, you'll have to fix it in only one place. For some unknown reason, the green circle never bounces back the way the red and blue instances do. What's so different about the green clip? How can you fix it so that all the circles bounce back and forth?

Hint

Drag another instance of the Circle symbol and chances are very good that it will be broken too. Here's another clue: Review the solution for the "Card Flip" broken script, earlier in this workshop chapter.

Solution

The reason both the red and green instances bounce back and forth is because I carefully placed their x coordinates at 210 and 100, respectively. Because they're moving by 10 or -10 each enterFrame, both will eventually reach precisely 550 and 0, which are the two extremes. We don't really want to check whether the _x position equals 550 or 0; we care only whether it's above 550 or below 0. The if statement should use a greater-than and less-than comparison operator. The following is the finished version:

this.direction=10;  this.onEnterFrame=function(){   this._x+=this.direction;    if(this._x>550){     this.direction=this.direction*-1;    }    if(this._x<0){     this.direction=this.direction*-1;    }  }

It's true that I was being deceptive by artificially making the red and blue circle work when, in fact, almost any clip would fail with this script. However, you'll quite often write a script that appears to work for your conditions and only for your conditions. As soon as you take a script that works fine under certain conditions to another situation, errors often pop up. So, in fact, this kind of thing will probably happen to you eventually. However, if your tests are exhaustive, you should find such bugs.

Multiple Choice

The file ("04_multiple_choice.fla") is a great example of a dynamic multiple-choice quiz. You just assign the variables question, answer1, answer2, answer3, and correctNum, and the text is displayed onscreen. This code could be expanded to load questions from a server or a text file. You can have a lot of questions and even report the score at the end of a quiz. If only it worked! It's a shame it doesn't because we even have a highlight for the button that the user clicks, and a way to color the Dynamic Text field for feedback using the new TextFormat object. The only problem is that any answer is considered correct. Here's a free hint: Salem is the capital of Oregon. Please fix my quiz.

Hint

The entire problem is one missing character that should be included somewhere in the attempt() function. Okay, the problem is found in the if statement's condition.

Solution

This one gets me all the time! The if statement condition should use the comparison operator ==, not the assignment operator =. The way it is now, whatNum is assigned to currectNum every time. I don't know whether the bug would have been easier to solve if I had placed the code that moves the highlight at the end. If I did that, the highlight would always move to the correct choice, not to the button the user clicked.

Rotating Box

Don't ask me what the practical use of this movie is maybe a Wheel of Fortune game? If you open and then test the file "05_rotating_box.fla," you'll find that you can set the box in motion by clicking either arrow button. When you click the Stop button, the next time the _rotation reaches a point that's evenly divisible by 90, the box should stop (see Figure W18.2). I added some visual feedback for when the user clicks the Stop button much like how a public bus might have a lighted sign that says "Stop requested" so that riders don't keep requesting the bus to make the next stop. That's just a little touch that makes this movie more usable. The problem seems to be related to "seltzer" I can't seem to make the box stop on it. Actually, if you start by pressing the left button, you won't be able to stop it on "apples" either. Obviously, when you investigate the solution, don't get hung up on the "seltzer" and "apples" choices. Try to determine why the box isn't working generally.

Figure W18.2. Despite having little or no practical value, the goal of this movie is to enable the user to stop the box in one of four rotations.

graphics/34fig02.gif

Hint

Set the movie's frame rate down to 2 fps (Ctrl+J) and debug the movie. Then select the box instance and Properties tab of the Debugger so that you can monitor the box's _rotation property. This should answer why the long-winded condition in the if statement (_rotation==0||_rotation==90||_rotation==180||_rotation==270) isn't working. Here's another clue: The solution requires a different condition in the if statement. Consider the modulus operator (%) and re-read the phrase "evenly divisible by 90" in the preceding movie description.

Solution

Like most solutions, the one I'm going to give you isn't the only one. All you need to do is to change the condition in the if statement to this._rotation%90==0. In pseudo-code, this condition says, "The remainder of 90 divided into _rotation is equal to 0." Here's the finished version of the enterFrame event:

box.onEnterFrame=function(){   this.stopLight._visible=stopRequested;    this._rotation+=direction;    if (stopRequested){     if (this._rotation%90==0){       direction=0;        stopRequested=0;      }    }  }

Word Float

I'm not really sure where I'm headed with this project, but the "06_word_float.fla" file doesn't seem to let me get any further than where I am. All I want is for the five instances (word_1, word_2, word_3, word_4, and word_5) to contain the five individual words in my arrayOfWords array variable. The entire script is in frame 1, but it doesn't seem to work right. Please help!

Hint

This bug is almost too easy for a hint, but here's one: The first index in an array is 0.

Solution

The easiest solution is to change the instance names from word_1 through word_5 to word_0 through word_4. When setting the text property of the fields being addressed dynamically (_root["word_"+w].text), the w iteration variable is going from 4 down to 0. (Remember that for-in loops effectively count backward, which doesn't have much to do with the problem but is an interesting tidbit.)

If, for whatever reason, you don't want to rename the instances (maybe you're using those names elsewhere or it's too much work to change them), there is another solution. Unfortunately, changing the statement that assigns the text property to read _root["word_"+w].text=arrayOfWords[w-1] won't work. The w iteration variable is not exactly usable as-is. Instead, you could initialize a variable such as counter and then increment it within the loop. See if you can find the error in the following code:

counter=0;  for (w in arrayOfWords){   counter++;    this["word_"+counter].text=arrayOfWords[w];  }

You'll see the problem if you test it: counter is going from 1 to 5, and w is going in the opposite direction from 4 to 0. Here's one of many possible alternatives that work:

counter=arrayOfWords.length+1;  for (w in arrayOfWords){   counter--;    this["word_"+counter].text=arrayOfWords[w];  }

And here's still another version using the TextField object that works without any text fields onscreen:

for (w in arrayOfWords){   _root.createTextField("word_"+w,w+1,0,0,50,50);    targ=_root["word_"+w];    targ.text=arrayOfWords[w];    targ._x=100+(w*50);    targ._y=100+(w*50);  }

Yellow Box

Here's another practically useless project as it is, but wait until you see it run! Right now, if you open and test the file "07_yellow_box.fla," nothing happens. When you fix the syntax errors, however, the yellow box will rotate constantly and, as you move the mouse, it will change its _xscale and _yscale based on where the mouse goes. Please make it work. There's only a tiny bit of code.

Hint

Make sure that the Syntax Coloring option is selected from the ActionScript Editor tab of the Preferences dialog box(see Figure W18.3).

Figure W18.3. If the Syntax Coloring option is selected, you'll have an easier time fixing the bug.

graphics/34fig03.gif

Solution

The solution is pretty easy. The _xmouse property appears incorrectly as xmouse. (In addition, the underscore is missing from _ymouse and _rotation.) Notice, however, that unless the _rotation property appears as this._rotation, the entire Stage rotates rather than just the box clip the way I had wanted. (Try it both with and without this to see the difference.) This exercise just underscores the importance of using correct syntax.

Click and Hold

The file "08_click_and_hold.fla" should let the user either click once or click and hold to move the box left and right. It used to work fine when there was just a forward and backward button (which reacted to single clicks). After I added the click-and-hold feature and moved some code around, however, the single-click feature stopped working. Although there are a number of ways to fix the single-click buttons, try to do so without making a new function. That is, I don't want another version of the moveBox() function.

Hint

Again, the fix in this file is related to the keyword this. That is, notice that moveBox() is triggered both as a regular function and as an onEnterFrame callback. Try placing the script trace(this); somewhere inside the moveBox() function for a clue.

Solution

Because using the keyword this inside moveBox() function will address the main timeline (when triggered normally) and the box clip (when triggered as a callback), we need to use something more specific. Just change the moveBox() function to read as follows:

function moveBox(){   box._x+=box.direction;  }

Objects

Don't worry, the broken script in the "09_objects.fla" file doesn't have much to do with objects. Instead, it deals with a common problem that arises when using objects. In this movie, I want a representation for each wheel of each vehicular homemade object that gets instantiated. In the first frame, you can see that I'm trying to put instances of my vehicle object in the variables bicycle, car, unicycle, and truck. Then I want to use attachMovie() to both place an instance of the Vehicle Shell symbol for each vehicle and, inside each vehicle, place an instance of the Wheel symbol for each wheel in that vehicle.

Just to see how the movie is working (or not working), I created a generic object (clickObj) onto which I assigned an onClick callback function. That generic object gets passed as the fourth parameter of attachMovie for each wheel (line 25 of the code). The problem is that I seem to get only one wheel for each vehicle but I want to see as many wheels as necessary (see Figure W18.4). The thing that kills me is that when I debug the movie, all my objects are instantiated properly (as Figure W18.4 shows). Please make the wheels appear to spread out horizontally. Consider peeking at the hint because you'll still be left with a nice challenge; even when you know where the script has to be adjusted, there's still a lot of work to do the adjusting.

Figure W18.4. When the file is corrected, you should see a circle for every wheel in every object.

graphics/34fig04.gif

Hint

Right after you execute the attachMovie() method for each wheel inside the for loop (for (var w=1; w<allObjects[i].wheels+1; w++)), you must reposition the newly created wheel by setting its _x property. Right now, all the clips are indeed appearing, but they all have the same x coordinate, so they're stacked. It's the same basic thing that happens in scripts that set the _y property of each vehicle or object_x clip. Study this line of code for an idea of what you have to do:

this["object_"+objectCount]._y=30*objectCount;

We want to set the _x property of the newly created wheel_x instance. With dynamic referencing, however, it's not so simple.

Here's another hint: The pseudo-code for the statement you have to write is: "Set the x of the new wheel inside the current vehicle to 20 pixels more than the last one," or "set the x of the correct wheel instance ("wheel"+wheelCount) inside the correct object (object_+objectCount) to 20 times wheelCount."

Solution

Right underneath line 25 (which begins

this["object_"+objectCount].attachMovie( ), place the following statement:

this["object_"+objectCount]["wheel_"+wheelCount]._x=30*wheelCount

Pretty ugly, eh? Re-read the pseudo-code provided in the "Hints" section, if necessary.

Move Multiple

How can so little code have so many errors? The "10_move_multiple.fla" file has two buttons that simply call my move() function, which is supposed to move each circle 10 pixels in the direction requested. I'm sure I'm targeting the correct circle_ clip because I re-read the "Dynamic Referencing" section of Chapter 7, "The Movie Clip Object." However, I can't even test the movie without the Output window rearing its ugly head. Make it go away and make the circles move.

Hint

After you fix the syntax error by putting semicolons in place of the commas in the for loop, you have two more fixes to make! I'll give you one: We're supposed to be changing the _x property of each circle. Add ._x after this["circle_"+i] and you'll only have one more fix to make.

Solution

First, change the commas to semicolons in the for loop. (I mistakenly use commas all the time.) Next, notice that although we're targeting the correct circle_ clip, we're not specifying which property we want to change. It's likely that we want to change the _x property. Finally, the loop doesn't loop enough times. More specifically, it loops only when i is less than 4. We want it to keep looping while i is less than or equal to 4. There are a few other fixes you could make to solve this problem, but changing < (less than) to <= (less than or equal to) will do it. Here's the finished version of the move() function:

function move(direction){   total=4;    for(i=1;i<=total;i++){     this["circle_"+i]._x += direction*10;    }  }

Summary

That was fun wasn't it? I sure had fun breaking the scripts. Fixing them should be quite rewarding and very educational. I tried to expose some of the common problems that programmers have with ActionScript. Errors in the following categories are common: addressing (both for variables and properties), syntax, logic flow, and array indexes. There are countless ways to solve any problem, of course, but you must realize that there are even more ways to do it incorrectly! ActionScript is not easy to program, but with deliberation and patience, you have proven that you can do it!

CONTENTS


ActionScripting in MacromediaR FlashT MX
ActionScripting in MacromediaR FlashT MX
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 41

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