Chapter 6. Debugging

CONTENTS
  •  General Approaches to a Bug-Free Life
  •  Using the Debugger
  •  Summary

Bugs are a fact in any programming. Even if you could write code that by itself was bug-free, outside influences can cause your movie to fail. Such basic issues as the user's computer configuration, the operating system, the browser, and even the Flash player all have some bugs. You don't need any help creating bugs, of course, because if you're human, you'll create many on your own. Actually, you, not the underlying software, will likely be the source of most bugs. A bug can be a flaw in your flow of logic or an error in the syntax of an expression. Regardless, bugs will crop up with unpredictable results that are invariably undesirable.

Debugging, or finding bugs and fixing them, poses an interesting dilemma. The premise of debugging is that you (the one who created the bug in the first place) are supposed to uncover where the flaw in logic or syntax error appears. Naturally, this is quite difficult because it requires that you find a problem in a script you thought was perfectly logical and legitimate at the time you wrote it. Despite this difficulty, Flash has both practical methods and built-in features to help you squash bugs.

In this chapter, you will:

  • Interpret syntax errors that Flash provides.

  • Develop methods to identify and document bugs.

  • Learn conventional debugging techniques.

  • Use the Flash Debugger to watch properties and variables.

Compared to previous chapter's intensity and length, this chapter is pretty mild. You should find it a good reprise.

General Approaches to a Bug-Free Life

While programming Flash, your life will never be truly "bug-free." There are, however, many steps you can take to uncover bugs, fix them, and ensure that there are no obvious ones waiting to reveal themselves at the least opportune time. Most of these quality assurance techniques apply to any process not just Flash programming. We'll cover built-in Flash features later this chapter in the section, "Using the Debugger." First, let's look at a few general ways to find, define, fix, and prevent bugs.

Finding and Defining Bugs

The first and most important step in debugging is to find the bug. Some bugs will prove to be elusive, whereas others will be so significant that you won't be able to make the movie run. I classify bugs into three categories: those that prevent the movie from even starting, those that cause the movie to effectively stop running, and those with an error in logic that while still allowing the movie to run displays inaccurate results. The first type is the easiest to find; the third type is the most difficult.

Find the Bugs That Prevent Your Movie from Starting

When you select Control, Test Movie, Flash will export an .swf. If there are any syntax errors (or other critical problems) in your script, however, you will see a full listing of the problems in the Output window (see Figure 6.1). Although the offending script will be ignored (after all, Flash can't figure out what you meant), other scripts might still work. Regardless, the appearance of any syntax error is effectively a "show stopper." A script you wrote is not being interpreted and that is a problem that must be fixed.

Figure 6.1. When you test your movie, all syntax errors will appear in the Output window.

graphics/06fig01.gif

Errors that appear in the Output window usually look worse than they are. Sometimes something as simple as a missing semicolon at the end of a line will display a long-winded error message. Fortunately, that's easily fixed. Let me explain some of the more common errors you're likely to see and how to resolve them.

First, to digest an error message, you should understand the form shown in Figure 6.2. The exact location in your script where the error occurs is shown first. You can fix the problem only when you track down the script's scene, layer, frame, and line number luckily, it's all listed clearly. After the address, the generic Flash error message describes the nature of your problem. The text following the figure describes some of the more common errors. (You can find all the error messages in a table in Flash's ActionScript Reference Manual and help documents.) Finally, a quote or partial quote of your script is displayed on a new line.

Figure 6.2. Error messages are detailed, including where the error occurred and the nature of the problem.

graphics/06fig02.gif

Here are the errors that seem to appear most often.

Balancing Errors

As you learned in Chapter 5, "Programming Structures," anything you start (such as a quote or parenthetical statement) must be finished properly. You'll see one of the following errors if you follow this rule.

string literal was not properly terminated  ')' expected  ';' expected

Unexpected '}' encountered (This is usually caused by forgetting to include a semicolon at the end of a line.)

Statement block must be terminated by '}'

Right Church, Wrong Pew

These errors appear when you have script which might be perfectly legitimate but it's in the wrong place. For example, you can't use an "onClipEvent" anywhere except clip instances. If you use an "onClipEvent" on buttons or keyframes, you will get an error.

Statement must appear within on handler (This occurs when you place a script on a button without surrounding it with an "on" mouse event.)

Statement must appear within onClipEvent handler (This occurs when you place a script on a clip instance without surrounding it with an "onClipEvent.")

Mouse events are permitted only for button instances (This occurs when you try to use an "on" mouse event in a keyframe.)

Clip events are permitted only for movie clip instances (This occurs when you try to use an "onClipEvent" on something other than a clip instance for example, a button or keyframe.)

Catch All

Although several other specific error messages are available, if your problem doesn't clearly fit into one of those, the following catch-all message will appear:

Syntax error

Note

graphics/icon02.gif

Although the "right church, wrong pew" errors will appear as described, Flash MX has a new feature that enables you to make clips behave like buttons that is, include mouse events such as rollOver and rollOut. It's as simple as putting an "on" mouse event on a clip instance. There are many other ways you can extend buttons, clips, and text to go even further. Chapter 14, "Extending ActionScript," explains exactly how.

Fixing the problem is usually easy after you know the cause. Keep in mind that the Output window will often display several errors. Instead of trying to fix them all, repair them one at a time (starting at the top of the list). You'll find that fixing one problem often resolves others.

By the way, the same error messages sometimes appear in the Output window while you are authoring. This happens when you try to paste a bogus script (from your Clipboard) into an Actions panel set to Normal Mode. When the error is in your Clipboard, the "address" of the error is shown as "Clipboard Actions" rather than the familiar scene, layer, and frame format. Additionally, the Actions panel's options menu has a "Check Syntax" feature (Ctrl+T) that provides a similar listing of errors.

Any errors that reveal themselves in the Output window are critical, and you should deal with them before moving on to other tasks. Also, be sure to clear the Output window (or just close it) before each test movie so that you'll see only errors. (It's easy to become distracted by old errors that could have been fixed if they still appear in the Output window.)

Repeat and Isolate the Bug

The syntax errors that appear in the Output window might seem overwhelming, but such errors will in the long run prove to be the least of your problems. It's just that they're critical, so you have to deal with them first. Most of your bugs will just cause your movie to play incorrectly or display erroneous data. Because these errors appear only while you're testing your movie (maybe you click a button and nothing happens), you might believe they are intermittent. Very rarely is a bug truly intermittent and usually only when something outside your control (such as an Internet server or user's computer hardware) is the cause. Even though bugs might appear to be intermittent, to fix them you need to learn the cause, and to find the cause, you must be able to repeat the bug.

I know it sure helps when a client describes precisely how he caused a bug to appear. Often, a good description of the cause will enable me either to think of the solution on the spot or at least to have a good idea of which script I need to resolve. Spending time both to repeat a bug and to clearly specify the steps necessary to make that bug appear is time well spent. It's easy to fool yourself, though. The post hoc, ergo propter hoc ("after this, therefore because of this") fallacy can mess you up. For example, if you wash your car and then it rains, you could use post hoc, ergo propter hoc reasoning to mistakenly attribute washing your car to causing the rain to fall.

While tracking the cause of a bug, try to find the fewest steps necessary. For example, imagine that you're testing a movie you made and, after twice clicking a button you labeled "Help" followed by a click of the "Sound Off" button, you find that your "Search" doesn't work. You might be convinced that those were the steps that caused the problem, but you should start over and try just the last step (pressing the "Search" button). That could be the only problem! If the "Search" button works, start over and try again by just clicking "Sound Off" and then "Search." This is really just basic troubleshooting. However you do it, make sure that you can repeat the bug, because the next step is to clearly document the bug.

Document and Analyze the Bug

This is probably the stage that will require the most restraint. The natural tendency when you find a bug is to try to fix it. Don't! It's possible, I suppose, when the bug is acute and clear, to simply fix it. More often, when you prematurely attack a bug, you'll find yourself opening a can of worms. It's like starting to bake without a recipe or trying to assemble shelves without reading all the instructions first. You want to first have a clear plan of attack and know where you're headed and then you'll likely fix the bug accurately and efficiently. The way to achieve this is to document the bug.

Simply knowing the bug is half the work. Depending on whether you uncover the bug early or late in a project, you should usually attempt to fix the bug with the least disruption to the rest of the code. Obviously, if the bug is rampant or fundamentally intertwined in your program, you'll want to consider rebuilding from scratch. Consider that fixing one thing often causes other things to break. Therefore, when you narrow the bug, you'll be able to fix it better. Imagine you have a toothache. Surely, the dentist could simply remove the tooth and place a crown in its place, but this would probably be unnecessary. If the dentist found the precise location of a cavity, he could fill the cavity instead.

Also realize that with any problem, doing nothing is a viable alternative. This comes up all the time in medicine when performing an operation is too great a risk compared to doing nothing. The same is true with bugs. Not only should you implement only the necessary changes and nothing more, but you should always consider doing nothing as a suitable option. You should document and categorize every bug. Some bugs might be critical and prevent the movie from functioning. Other bugs might occasionally cause a display to blink for a second. Certainly I would always prefer to fix every bug, but you can viably opt to "punt" (just not fix a bug) if the bug isn't very important compared to a looming deadline or if fixing the bug will potentially break many other parts of the movie. I can't establish a set of guidelines for you to use, but if you first document and categorize each bug, you'll be able to set an appropriate course of action.

Fixing Bugs

An entire chapter on debugging and only now we talk about fixing bugs? Yep. I can't stress enough that after you know exactly what your objectives are, you can swiftly achieve them. It's true with converting detailed pseudo-code, and it's true when you fix bugs. Sincerely, that's all there is to it. Of course, I have a couple tips for how you should fix bugs, but the truth is that if you know what the bug is, you're 95 percent of the way to fixing it.

Have the Humility to Call Yourself a Fool

Most bugs are the result of a stupid mistake. You need to have the humility to realize that you could be overlooking something obvious. I once heard that the majority of the tech help calls to a sound card manufacturer were resolved simply by turning up the volume. Just because a bug is causing you a great deal of grief, it doesn't necessarily require a lot of work to fix. Later in this chapter, you'll learn how to use Flash's Debugger to focus in on exactly what your movie is doing. When you walk through the steps that reveal the bug, leave no stone unturned. That is, don't take for granted the "easy" scripts. They're just as likely to have an error that causes the bug as more complex scripts.

Try Being a Cynic

Not only do you need humility, but a good dose of cynicism will also help. The best quality assurance people often come across as cynical people. When applied to finding and fixing bugs, this is a good personality trait. Don't believe anything unless it can be proven. Even though part of your movie might appear to be working, a cynic will find a way to trip it up. For example, the movie might start with the user typing his name and pressing a "continue" button. When looking for a bug, you should try everything to break the movie! What happens when you type nothing and click "continue"? What about when you press the Enter key does it make a new line and move the first thing typed out of view? It's as though you're trying to make it break. Good programmers can do most of this themselves, but because of the need for this cynical attitude, it sometimes helps for someone else to test your creation.

This cynicism can help to do more than uncover bugs. When you're trying to fix a bug, step through the entire code and question the logic every step of the way. Don't just try to translate the code to English, however, because you'll likely just repeat the intention you had in the first place. Rather, read each line with cynicism as though someone were trying to trip you up and you have to find the flaw in the logic. If a line can be justified, move to the next line. If a block of code stands the "is it logical?" test, you can move on. It's difficult because you're effectively calling yourself a liar. Opposite to the United States' courts, your code is guilty (of bugs) until proven innocent.

Ensuring That All Bugs Are Squashed

The joy you feel after fixing a bug is a feeling like no other. Don't get too happy, however. (Remember, you're a cynic.) I think the greatest error programmers make is that they don't retest after fixing a bug. Because it's quite possible that fixing one bug causes other bugs to appear, consider retesting everything after you implement a supposed fix. It's difficult because in the process of fixing a bug, you've developed a pattern of steps to re-create the bug. The tendency is simply to follow those exact steps to see whether the bug is still present. Consider that after fixing one bug, if another bug was created you'll probably have to follow a different set of steps to see it. Although it might be impossible to retest everything after every minor bug is fixed, you should at least consider testing at logical milestones. You can also send the file back to your trusty cynic. Realize that even if you tell the testy testing person to check only whether certain bugs are fixed, if she does a good job, she'll actually test everything not just the things that are supposed to be fixed.

Preventing Bugs

I suppose it's obvious that your life programming in Flash will be better if you simply prevent any bugs from the start. Even though this is wishful thinking, if not pure fantasy, there are several steps you can take while working in Flash that will reduce the likelihood of bugs.

Version Control

One form of documentation that's helpful while programming is version control. Although some people use sophisticated software packages, you can still take advantage of the benefits of tracking versions as you develop a Flash site. The simplest form of version control is to do a "Save As" periodically. For example, you can start each day by saving a copy of your movie with the date in the name plus a letter of the alphabet (starting with "a") such as "4_March_2002-a.fla." Every time you add a feature or squash a bug, save the movie with the next letter of the alphabet ("4_March_2002-b.fla"). If you're really organized, you'll keep a paper log that documents exactly which features were added or which bugs were fixed in each revision. That way, if you find that a major bug has appeared that wasn't in an older version, you can go back through the revisions to find the latest version without the problem. Then you can redo the same edits and test the site after each one. You'll uncover what caused the bug and your document will help you restore everything else.

Avoiding Bugs in the First Place

A related technique is to build new scripts and resolve bugs "offline." For example, if you intend to add a sophisticated feature to a Flash movie, you can try to work out the solution in a new separate file. After you get it working in the new file, you can add the script you developed to the main file. Sometimes working offline involves so much extra work just in laying the foundation that you might consider using the main file. However, before going wild trying different solutions, you should first save the main file, and then save as "working.fla." Do what it takes to find a solution, but when you do, go back to the original and carefully implement the scripts you developed (in "working.fla"). Even though this seems like extra work, realize that in the process of trying different solutions, you might make several attempts that don't work. Even though you might think you were careful to dispose of any failed attempts, there's a chance they're still present. By determining the solution "offline" and then implementing it "online," you're more likely to create a script that's more streamlined and less buggy. Compare this process to the way an artist will work on several "studies" before creating his masterpiece. (Remember that this technique is useful both for building scripts and for fixing broken scripts.)

The only other tip I can offer that tends to avoid bugs is to build your movies in pieces. No matter how large the task, it's always just a collection of subtasks. When you approach a large Flash project, try to break it down into pieces. Get each element to work before moving on to the next task. You can even modularize scripts by using functions (as you'll see in Chapter 8, "Functions"), and build building blocks of code by using objects (as you will in Chapter 12, "Objects," and Chapter 13, "Homemade Objects"). Regardless of the technical solution you apply, you should try to break down your task into pieces.

Using the Debugger

graphics/icon02.gif

When it comes to finding the bugs you've created, Flash does offer help in the form of the Debugger. This special window enables you to view variables or properties while a movie plays. You can also change most properties of any Movie Clip. In addition, Flash MX adds several options that make this a true "debugger." The name "Debugger" might be deceptive. It won't "de-bug" (that is, remove bugs from your movie). Later in this chapter you'll see the new options, including setting breakpoints (which are points in the script where the program will pause), the Step Over/Step In/Step Out buttons (which enable you to selectively skip parts of the script or to execute only one line at a time), and the Call Stack (which reveals "how you got there" when you encounter a breakpoint).

Despite the fact that the Debugger won't fix your problems, it's still very useful. We'll first look at how to use the Debugger, and then we'll consider strategies to apply its features.

Viewing and Setting Properties and Variables

Flash's Debugger enables you to view and set both properties and variables while the movie plays. To view the Debugger, select Control, Debug Movie (rather than Control, Test Movie). When the Debugger first appears, your movie will pause until you click the Continue button (see Figure 6.3). (It might seem like a pain to have to click Continue each time; however, as you'll learn later, you can navigate to any script to insert breakpoints while the movie is paused.)

Figure 6.3. The Debugger requires that you press Continue to begin debugging.

graphics/06fig03.gif

Once you have your movie playing in the Debugger, you'll see a hierarchy of your movie and all its Movie Clip instances in the white area at the top left. To view properties or variables, first select the Movie Clip instance that you want to analyze. Selecting _level0 will enable you to see properties of and variables in the main timeline. Similarly, selecting _global gives you access to variables stored in the new common area for global variables. (Global variables and local variables for that matter were covered in Chapter 4, "Basic Programming in Flash.") You will also see any unnamed clips, but Flash assigns seemingly arbitrary names to them. Regardless, each clip in the hierarchy appears with its full path, always starting with _level0 (except, of course, movies that you've loaded into other level numbers, which appear starting with "_levelx" where "x" is the level number). Despite how complex your hierarchy becomes, all you do is select the clip (or _level0 for the main timeline) you want to inspect and then click the Properties tab or the Variables tab.

You can test out what's been covered so far by following these steps:

  1. Create a text field with some text in it and use the Properties panel to set the field to Dynamic Text. Also, specify a variable name in the panel's "var" field maybe someVar. Leave this text on the main timeline.

  2. Draw a box and convert it to a symbol (Movie Clip). Use the Properties panel to give it an instance name "boxClip" and use Color Styles drop-down to set the alpha to 50%.

  3. Create another Dynamic Text field with text in it and specify a memorable variable name. (I used "anotherVar.") Use the Brush tool to draw a rough circle around the text. Select both the text and the drawn circle and convert to symbol (Movie Clip). Give this instance the name "textClip."

  4. Finally, select Control, Debug Movie. The hierarchy should look like Figure 6.4. (You might need to resize and adjust the different panes inside the Debugger.)

    Figure 6.4. The hierarchy of our exercise includes the two clip instances, "boxClip" and "textClip."

    graphics/06fig04.gif

At this point, you can first select a clip and then click either the Properties tab or the Variables tab. For example, if you click the _level0.boxClip clip instance and then view its properties, you'll see _alpha listed as 50. Because the _alpha property is not grayed out, you can change it. Double-click the 50, type a new number, and then press Enter. You should see the clip change onscreen. Select the instance of _level0.textClip and change the _y property. When you press Enter, you should see the property change onscreen. Notice that both the _xmouse and _ymouse properties are grayed, meaning that you can't change them, but while you move your cursor around the movie, you'll see these properties update (see Figure 6.5).

Figure 6.5. Selecting an instance enables you to view properties, including the current _y.

graphics/06fig05.gif

Now try viewing and changing the custom variables. Select _level0 and click on the Variables tab. You should see the name and value of your variable someVar that's stored in the main timeline. If you want to change this variable's value, remember that because this variable is a string, you need to include the quotation marks or it won't work. (You can view the variable contained in your clip _level0.textClip but don't try to change the variable; you'll do that in the next section.)

To review: The basic process is to select the clip you want to inspect and then select either the Properties or the Variables tab. In practice, the idea is that you might expect to see a clip's _alpha change and you should be able to (while the movie plays) see this property change. Or, if there's a variable in a clip (which isn't always shown to the user in a text field), you might want to monitor the current value that is, make sure that it's changing when you expect it to. You'll see how this will be useful in the section, "Using Dynamic Text to Watch Variables or Expressions," later in this chapter.

Watching Variables

Selecting individual clip instances is a necessary step before viewing their properties or variables because the Debugger can show you only one clip's _x property at a time (because different clips might have different values for this property). It was easy in our case because we just had two clips plus the main timeline. In a real project, it can be a lot more complex. Figure 6.6 shows how complex a real project can become.

Figure 6.6. A real project can become quite complex with countless clip instances.

graphics/06fig06.gif

So, instead of weaving through a complex hierarchy, the Debugger enables you to "watch" the variables of certain clips. All you need to do is select the variable that you want to monitor and either right-click it (control+click on Macintosh) and then select Watch, or select Add Watch from the Debugger's options menu. You'll see a blue dot next to the variable. Also, when you select the Watch tab, your variable will be visible at all times (regardless of which clip you happen to select in the top-left pane of the Debugger). Under the Watch tab, you can also select "add watch" (or simply "add"), and then type the path and variable name you want to watch. It's a little tricky because you have to begin each variable name with an explicit address (that is, timeline) for that variable, starting with _level0. I think it's easiest to first find the variable you want to watch (under the Variables tab), and then add it from there.

If you want to test it out, just create two buttons and two Movie Clip instances. Name one Movie Clip instance box1 and the other box2. Then attach a script on one button that reads box1.count++ and have the other button's script read box2.count++. (Both scripts will need to be within a mouse event, such as on (release){ }.) Now when you select Control, Debug Movie, even though you the user won't "see" each clip's count variable, you'll be able to with the Debugger. In addition, you can monitor both count variables if you first use the Debugger to find box1, select count and then select Add Watch from the options menu. Do the same for box2 and you won't need to keep finding box1 or box2 to view their respective count variables.

Keep in mind that the Debugger is a runtime-only feature. That is, you can make changes while your movie plays, but if you re-export your movie (by selecting Control, Test Movie), all variables will reinitialize regardless of the changes you made in the Debugger the last time it ran.

Stepping Through Code

graphics/icon02.gif

The feature that makes Flash MX's Debugger the "real deal" is its capability to step through code. It's almost as though you can make the frame rate come to a halt while you step through one line of code at a time. As you step, you can watch variables and see the sequence of events in slow motion. The way it works is you first set a breakpoint on any line of code you want to analyze. While debugging, if that line of code is encountered, the Debugger pauses the movie and displays the line of code in context in the right-side pane (see Figure 6.7).

Figure 6.7. The Debugger pauses when it encounters a line of code on which the author set a breakpoint (line 12).

graphics/06fig07.gif

Once your movie is paused on a breakpoint, several additional pieces of data may be revealed. You'll always see the line of code in question on the right side. If your breakpoint is inside a function, you'll see additional variables listed under the Locals tab. These variables are specific to the function in which you've just paused. For example, some functions may operate on a temporary variable just while inside the function. Or, if your breakpoint is inside a loop, the iterant variable could be local; this way, you can reveal its current value. (Chapter 8 discusses functions in detail.) Finally, if you're paused inside a function and you arrived at that function by way of another function, the Call Stack pane will be filled with a history or "how you got here" information. Figure 6.8 shows information from the upcoming example.

Figure 6.8. The Debugger can display variables local to the current function and the Call Stack (like a recent history).

graphics/06fig08.gif

Even though we haven't discussed functions yet, you can type the following code into the first keyframe of a movie as an introduction to the Locals tab and Call Stack pane:

function tellOneFriend(){     andTheyTell("a", "b");  }  function andTheyTell(){     for(var i=0; i<arguments.length; i++){         andSoOn(arguments[i]);      }  }  function andSoOn(which){     trace(which);  }  tellOneFriend();

Before you debug this movie, set a breakpoint on the line that executes the trace command. First, click that line in the script, and then either right-click or click the Debug options menu (the stethoscope icon) and select Set Breakpoint. Now debug the movie. After pressing the green Continue button in the Debugger, you should see a display like Figure 6.8. By the way, any time you debug a movie, before you click the Continue button, you can insert temporary breakpoints by first navigating to a script (through the Debugger drop-down list at the top right) and then right-clicking or clicking the Toggle Breakpoint button (the red dot).

Let's walk through the Debugger for the example in Figure 6.8. First, notice the entries in the Call Stack pane. In a way, the order might appear to be backward. The most recent function in the sequence (andSoOn(which)), which is really the current function) is listed on top. That's where we are paused, but right before that, the function andTheyTell() was triggered and before that, the tellOneFriend() function. It all started with tellOneFriend(), which you can see at the bottom of the stack. Even though it might seem backward, consider the result if you placed the daily newspaper on a stack every night. You'd find the most recent paper on top, and then the previous day's paper underneath. Anyway, the Call Stack is useful because it answers, "How did I get here?"

As you'll learn, an advantage of functions is that multiple places in your code can invoke the same function. Maybe when you page forward you have an additional function that plays a sound. You may want to invoke the same sound-play function when you page back. If you set a breakpoint and the Debugger pauses, you can look at the Call Stack to determine the sequence that got you to where you are. Unfortunately, only homemade functions appear in the Call Stack not internal events. That is, you won't see on (press) at the very bottom of the Call Stack even if that was the "thing that started it all." Nevertheless, the Call Stack is interesting.

In the example that you're currently debugging, select the Locals tab. You'll see which (and its value of "a"). The point is that at this frozen moment in time, the which variable has a value of "a". But which really isn't a variable, but rather a name given to the parameter received. It's effectively a local variable because as soon as the andSoOn() function is done, it will vanish out of memory but while you're debugging, it may be useful to know its value. Anyway, go ahead and click the Continue button again. The Debugger appears to have not changed even though when you're paused and then click Continue, you'd expect the movie to continue on its way. It did! Actually, you should see the Output window with "a" (from your trace command). What happened? You continued, but Flash encountered your breakpoint again. The andTheyTell() function includes a loop that repeatedly calls the andSoOn() function where you have a breakpoint. Notice that this time around the value for which is "b". So, if there were any question as to which loop iteration you're in, the Locals tab helped you figure it out. You can also try this with a breakpoint in the for loop and use the Locals tab to see the value of i (the iterant variable).

Finally, once you're paused on a breakpoint, you can do more than just view the Call Stack and local variables. There are other buttons besides Continue, including Step Over, Step In, and Step Out. Basically, these give you the option (when stopped at a breakpoint) to step through one line of code at a time. Normally, when a breakpoint is encountered, a little yellow arrow points to the line of code in question. Clicking Continue will make the movie play until the next breakpoint is reached. However, if you click the Step In button (think "baby step"), the arrow moves to the very next line of code, at which point you can keep clicking Step In and monitor how variables or properties are changing. Step Out is slightly different, because when you're paused within a homemade function, it will step right outside the currently enclosed function. Step Over is similar, but it just jumps over the very next line of code. In any event, your code isn't really skipped. (Every line will always execute no matter what.) However, the little yellow line can jump a little bit further ahead and pause if you want.

In practice, I find myself clicking Step In first, but then if I find that I'm stepping into a repetitive loop, I might use Step Over or even Step Out. The differences are really subtle, but the general idea is the same: you can step through one line at a time and check out what's happening.

Remote Debugging

One of the coolest features of the Debugger is that you can debug movies playing in a browser. This means that even after you upload a finished movie, it's possible to debug it remotely. When you see the steps involved for this to work, you might think it's a pain, but the steps are necessary for good reason. For example, you might not want others to be able to debug your movies.

To debug a movie remotely, the following conditions must be met:

  • The original .fla needs to have its Publish Settings set to Debugging Permitted. Optionally, you can also specify a password (see Figure 6.9).

    Figure 6.9. The movie's Publish Settings must allow for debugging.

    graphics/06fig09.gif

  • You must store an .swd file adjacent to the .swf on your server. (You'll create an .swd file automatically any time you test, debug, or publish a movie with the Debugging Permitted option selected in Publish Settings.)

  • Finally, the Debugger must have the options menu set to Enable Remote Debugging (see Figure 6.10). (This makes Flash act like a server, so you might need to adjust firewall settings.)

    Figure 6.10. Because you'll be debugging while Flash is running, you must set the Debugger to Enable Remote Debugging.

    graphics/06fig10.gif

To debug a movie, first open Flash, start a new movie, and then select Window, Debugger. From the Debugger's options menu, select Allow Remote Debugging. Then just launch your browser and right-click a movie that has been set to permit debugging when it was published. If you're running the correct version of the Flash player (the Debug version), you can select Debugger from the menu that appears. (Users who have downloaded the free player from Macromedia won't see this option.) First you need to specify where your version of Flash MX is running (that is, the one with its Debugger set to enable remote debugging). Normally, you'll just accept the default Localhost option. Then you can return to Flash, enter the password (or simply click OK if you have no password), and you'll be able to debug the movie like normal.

One frustrating fact is that as with any application when you make Flash active (by clicking on the Debugger, for example), the entire Flash application comes to the front and may cover your web browser that contains the move being debugged. You'll just need to resize the Flash application to take up less space, and then arrange it so that it doesn't cover your browser (see Figure 6.11).

Figure 6.11. Although Flash comes to the front when it's the active application, you can arrange windows so that it won't cover up too much of your browser.

graphics/06fig11.gif

Remote debugging has been vastly improved in Flash MX; if you found it funky before, you really should consider trying it again.

Strategies of Debugging

After you've found, documented, and determined to fix a bug, the approach you take can involve many different avenues. You can't just select Debug Movie and expect the solution to fall into your lap. You have to be deliberate in your quest. If you need to see whether a variable or property is changing as you expect, the Debugger is great. If you've really narrowed down the problem, it might even be appropriate to set breakpoints so that you can step through the script. However, you often need to first determine that the script you believe is in error is being encountered at all, and then you must determine that it's producing the expected results. There are several ways to check your scripts.

The trace Command

Sometimes you'll have a script that for the life of you appears to be perfect. But that script has to be encountered in order to execute. Often, the script isn't even being reached. Perhaps it's contained within an if statement whose condition is false. Or, you keep checking a script that's inside a Movie Clip that isn't used on the Stage. There are many ways for this to happen, so you need a quick way to confirm that a script is reached. The trace command is the perfect solution.

When you're testing a movie (or debugging, for that matter), any time Flash encounters trace("whatever"), the Output window will pop up (if it's not already open) and display "whatever." If you place trace("hi") inside a loop that repeats 10 times, hi will appear 10 times in a row. So, if you have a block of script that appears to be ineffectual, just throw a trace("hi mom") right before the script. It's sort of a sanity check to see whether a script is even being used. For example, assume that you have the following script attached to a button:

on (release) {     if (age<12&&age>21) {         age++;      }  }

Now say that you've tested the movie with the Debugger and noticed that age doesn't increment (as you expect) when the button is pressed. So, you can insert trace("I'm incrementin' here") at the beginning of the if statement, as follows:

on (release) {       if (age<12&&age>21) {             trace("I'm incrementin' here");              age++;        }  }

When you test or debug the movie again, if the script is reached, an Output window pops up and displays, "I'm incrementin' here." To your surprise (or maybe not), the Output window doesn't appear. At this point you know that it doesn't matter whether the age++ statement is legitimate, because it's not even being encountered. What's interesting about the trace statement is that you can actually put an expression in the parentheses (rather than a hard-wired string). For example, if the statement trace("Age is "+age) is reached, the Output window will show the result of the expression "Age is "+age. The problem at hand, however, is that our trace statement is never reached. Try the following script instead:

on (release) {   trace("Age is "+age);    if (age<12&&age>21) {         age++;        }  }

This will display the value of the expression "Age is "+age (that is, "Age is " followed by the value of age). This way, the message that appears in the Output window will be much more informational. (Remember that the + operator concatenates when either operand is a string.) You could keep trying different expressions, such as:

trace("age: " + age + " part a: " + (age<12) );  trace("age: " + age + " part b: " + (age>21) );

Eventually, you should narrow down the crux of the bug (that age cannot be both less than 12 and greater than 21 and, therefore, our if statement's condition is never true and we never reach the part that reads age++). Granted, this bug was rather obvious, but the idea was to show the process of using the trace command. trace is a great way to quickly see that a script is being reached as well as displaying the results of an expression right in the Output window. You can use trace quite liberally and your audience will never see the Output window after you publish the movie. (And, if you want to turn it off while you're testing, there's an option in the Flash tab under Publish Settings to "Omit Trace actions.")

Using Dynamic Text to Watch Variables or Expressions

The Debugger is great for watching variables and properties change, but you need to make an effort to first activate the Debugger, select Debug Movie, find the clip whose variables or properties you want to watch, and then set up a watch. Often, you'll have a specific variable that you need to watch quite a lot and it will become a hassle to always use the Debugger. Also, the Debugger enables you to watch only a single property or variable. What if you want to "watch" an expression? You'll need to devise another way to monitor the values that interest you.

You can quickly monitor any variable just by creating a Dynamic Text field. (Remember, you'll likely have many variables that won't be visible to the user this is a case where you want to temporarily view these variables.) If you want to "watch" a variable named score in a Dynamic Text field, you just associate that variable name in the Properties panel. Of course, the text needs to be placed in the timeline or clip that you want to monitor. If you want, you can even precede the name of the variable with the path to that variable such as _root.score or _root.someClip.score depending in which timeline the variable resides. Keep in mind that creating a Dynamic Text field will initialize the variable as a string (even if you type a number into the text field). You might need to make sure that the variable gets converted to 0 or some other number immediately after such a field is displayed onscreen. Because the Dynamic Text field will try to make a variable a string, you can place a script in the first frame that reads score=Number(score) to reassign the variable to a number data type. The weird part is that a Dynamic Text field will properly display the value of a number. (Therefore, you never need to change it back to a string.)

This technique of watching variables in text fields works great. If you keep all such variables in a separate layer, it's easy to effectively remove them before publishing by simply changing the layer to Guide. This way, you can just as quickly restore all the variables that you were watching by resetting the layer to Normal.

In addition to convenience, Dynamic Text fields have another benefit over the Debugger. Namely, you can use them to constantly monitor the current value of an expression not just a variable. Unfortunately, you can't just type an expression in the Text Options panel. Rather, you can use this simple workaround:

  1. Create a Dynamic Text field associated with a variable named result.

  2. Use the Arrow Tool to select the text field and then convert it to a Movie Clip symbol (by pressing F8).

  3. Attach a script to the instance of your Movie Clip now on the Stage that keeps setting result on the enterFrame event. For example, try this script:

    onClipEvent (enterFrame) {     result=_root._ymouse;  }

Although this example to monitor the _y property of the mouse isn't performing anything you couldn't do with the Debugger, consider that you can set the result variable to anything you want. A much longer expression could show the result of an expression that calculates the user's current average, using variables called sum and questionsAnswered:

onClipEvent (enterFrame) {     result=_root.sum/_root.questionsAnswered;  }

Again, this is a rather simple expression but it doesn't have to be. Keep in mind that because this script is attached to a clip, it behaves as if it were in the clip. You need to pay special attention to the target paths for variable names. If you can keep that straight, this is a great way to monitor complex expressions. Because this is just for testing purposes, you can ignore the fact that using the enterFrame event might put a heavy load on the computer processor.

Here's another example. On a button, attach the following script:

on (release) {     lastClick=getTimer();  }

Then on the Movie Clip that contains the Dynamic Text field displaying the variable result, attach this script:

onClipEvent (enterFrame) {     result=Math.floor((getTimer()-_root.lastClick)/1000);  }

You'll constantly monitor how long (in seconds) it has been since the user clicked the button. In the release event, our custom variable lastClick is assigned the value of getTimer(), which is the precise number of milliseconds since the movie started. Then in the enterFrame clip event (which executes repeatedly), we assign the variable result the value of getTimer() (that is, the current time) minus lastClick (the start time). It doesn't matter when you started; you can always determine the elapsed time by finding the difference between the current time (getTimer()) and the start (lastClick). Finally, we divide by 1000 (which converts milliseconds for which each second has 1000 into seconds). Finally, the whole expression is inside a Math.floor(), which returns the integer portion of the expression.

graphics/icon02.gif

Because I can't restrain myself and because it fits so well here I have to tell you about a new Flash MX feature: watch. In a nutshell, the watch method enables you to identify a single variable that, when changed, causes another function to be triggered. That is, you can "watch" the variable. In the example above where we wanted to see the time constantly change, it was necessary to use the enterFrame event even though this forced the computer to work harder. If you're just watching for one variable to change and want to trigger a result, there's another way. In practice, this means you can actually display the result of an expression without using the enterFrame event unnecessarily.

Although you'll find the most complete coverage of the watch method in Chapter 14, "Extending ActionScipt," here's something to give you a taste of its power. Consider an Input Text field for price. Any time price changes, you want to display the actualPrice with 8 percent sales tax added. You certainly could set up an enterFrame script like we did earlier, but you don't really want to monitor the value of price constantly only when it changes. This is a perfect situation for watch. You can trigger a function any time that price changes.

Assuming you have an Input Text field associated with price and a Dynamic Text field with the variable actualPrice, type the following code into a keyframe:

watch('price',function(id,oldVal,newVal){actualPrice=newVal*1.08});

It really is amazing. I won't explain watch fully now; Chapter 14 covers it in detail. However, you can use the general form above to redisplay the value of an expression any time a specified variable's value changes. Just replace 'price' with the variable you want to watch, and then the part inside the curly braces can perform an assignment to the onscreen variable (in this case, actualPrice). It's weird, but the variable appears as 'price' (that is, like a string) when inside the watch method. Just realize that within the expression, use newVal in place of the trigger variable. That is, you don't have access to price inside the formula (as in actualPrice=price*1.08) but instead have to use newVal because that's the parameter name given in the function. I know that doesn't explain everything, but it really is easy to adapt for the purpose of seeing an expression onscreen.

Quality Assurance and Productivity Scripts

It might be discouraging to find a bug, but when you're aware of a bug (and what causes it) you can at least take the time to address the bug. ("The devil you know is better than the devil you don't.") Compare that to bugs you don't find. They're worse because the user might be the first to uncover them. Ideally, you can provide some level of assurance that the quality of your movie is high that is, has few if any bugs. As you'll see, it's possible to write scripts that provide a level of quality assurance.

A quality assurance script is a script that serves no purpose except to prove the program is working. For example, imagine you're making a reference site with information about the Olympics. Assume that your interface includes a column of 10 buttons for 10 different years and a row of buttons for 10 different events, plus three buttons ("photos,""stats," and "winners"). The user could click any year and any event to see photographs, statistics, and medal winners for each combination. The 100 combinations of year/event, multiplied by the three bits of content means a total of 300 discrete pieces of information. Although you don't have to step through 300 combinations to test that it's basically working, you do need to proof all 300 pages of content. Instead of stepping through every combination by hand (and probably missing a few due to human error), you can write a script that does this for you! I would use a couple of temporary buttons ("reset" and "next"), plus a Dynamic Text field that constantly displays the combination of year/event/media-type (for "photos,""stats," or "winners"). To proof all the content, you just click "reset," and then keep clicking "next." If you find a page that has bogus information or doesn't work, just note the data in the Dynamic Text field. Figure 6.12 shows what I have in mind.

Figure 6.12. The multitude of options in this matrix calls for a quality assurance script that steps through all 300 combinations.

graphics/06fig12.gif

To construct this script requires using homemade functions, which we haven't discussed yet. (We'll get to them in Chapter 8.) By using functions, you can access the same function from several buttons, including the "next" button. Without explaining the code, imagine that all the content is in a clip called years. The years clip contains 10 frames, one for each Olympic year. Every frame in years has a unique master clip (with content from a different year), but all clip instances are named events. Finally, each events clip contains 10 frames for the 10 different events. On each frame of each events clip there's a clip called media that contains three frames one each for photos, stats, and winners. Every clip in this movie has a stop() script in the first frame. Figure 6.13 shows a portion of the hierarchy in the Movie Explorer.

Figure 6.13. The Movie Explorer shows the basic architecture used for the Olympics example.

graphics/06fig13.gif

Here's the script that can be placed in the main movie's first keyframe:

reset();  function reset(){     curYear=1;      curEvent=1;      curMedia=1;      goUpdate();  }  function goNext(){     curMedia++;      if (curMedia>3){         curMedia=1;          curEvent++;      }      if (curEvent>10){         curEvent=1;          curYear++;      }      if (curYear>10){         reset();          trace("You're done");      }      goUpdate();  }  function goUpdate(){     _root.years.gotoAndStop(curYear);      _root.years.events.gotoAndStop(curEvent);      _root.years.events.media.gotoAndStop(curMedia);  }

The reset() function (which is executed at the very start) simply sets the variables (containing current year, event, and media) to 1. Then, it calls the function goUpdate(). This is the same function that is called when the user clicks any button (year, event, or media) but after the variable for curYear, curEvent,or curMedia is set. For example, if the user clicks year 2000, curYear is set to 10 and the goUpdate() function is called. If the user clicks the Freestyle event, curEvent is set to 1 and the goUpdate() function is called. When the Next button is pressed, the quality assurance function (goNext()) tries to go to the next media element. If doing so puts curMedia higher than 3 (for three types of media), it's set back to 1 and curEvent is incremented. If curEvent is greater than 10 (the total number of events), it's set back to 1 and curYear is incremented. Basically, the goNext() function starts in the first year and steps through all the media in the first event, and then through all the media of the next event. When it's done with the events in one year, it moves on to the next year.

Even though you might not understand every detail of this script, the concept should start to make sense. You can write a script that serves no other purpose than to aid in the testing process.

It's easy for me to say, "Oh, just write a quality assurance script." Obviously, this will take additional effort, which might seem like time away from your main goal. In fact, the time investment necessary isn't that great, and if it means that you can save time on another stage of the production (proofing, in this case), it's likely worth the investment. In this not-so-contrived example, it turns out this script could have another use. Maybe you think it might be nice to provide the user with an "auto slide show mode" that steps him through all the combinations. You'll already have the script. I don't want to suggest that every quality assurance script will turn out to have other value, but even if it doesn't, it's probably still worth the investment.

Another reason why you might want to write an extra script is to aid in the production of your project. Unlike a quality assurance script, which might have practical use in the final movie (the way the "next" button turned out to have a secondary use as a slide show), a productivity type of script will never be useful in the final movie but it can still be very valuable. Quite often the process of assembling a project can be tedious. It's possible to invest a little bit of time writing a "productivity script" that will pay back in time saved later.

I had a great opportunity to write a productivity script when I programmed part of the 2000 edition of the site www.m3snowboards.com. In one section, individual letters would scatter and assemble themselves to spell the name of each snowboarder on the team. (Figure 6.14 shows a sequence of the movie.) Each letter was a separate movie clip. Without explaining how I made each letter move, it's obvious that I needed to know the x and y coordinates of each letter in the final arrangement for each snowboarder. With all the different team members and the multiple use of several letters (that is, there weren't just 26 letters letters such as "s" were used many times), the total number of coordinates was more than 1,400!

Figure 6.14. Because each letter arranges itself, I needed to gather the coordinates for each snowboarder's name. (From www.m3snowboards.com screen shots courtesy of Paris France Inc., copyright MLY Snowboards.)

graphics/06fig14.gif

You can see just half of one page (of five pages) of data in Figure 6.15. At first, I thought that someone would simply need to position the letters in the arrangement he wanted and then one by one use the Info panel to ascertain the x and y positions. Then that person (I sure didn't want to be the one) would simply type these numbers into the text file (the result for which is shown in Figure 6.15) and that would be loaded using Flash's loadVariables command. Calculating the 1,400 individual coordinates by hand would not only be tedious, but fraught with potential errors. Instead, I wrote a productivity script that would make the process relatively simple.

Figure 6.15. 1,400 individual coordinates is a lot of data to type by hand the production script meant we didn't have to.

graphics/06fig15.gif

I made a separate Flash movie that enabled the production author to individually align by hand or with the Align panel each letter visually. Then the production author would test the movie and press a button in the Flash movie that ran my script. My script would loop through every letter and ascertain the x and y property of each one. This data would be formatted the way the external text file needed to be (URL encoded, as you'll learn in Chapter 16, "Interfacing with External Data"). Finally, the script would use the trace statement to display all this data in the Output window. The production author simply copied this text and pasted it into a text file.

The process was repeated for each snowboarder but it was a simple process: Just line up all the letters while authoring in Flash, test the movie, press the button, and then copy and paste the contents of the Output window. Multiply that process by each team member and you can well imagine that it is way less time (and more accurate) than using the Info panel to determine the coordinates of 1,400 letters! It might have taken an hour or two to write the script, but that investment was well worth it.

Summary

This chapter introduced you to many debugging techniques. If you've ever done any kind of troubleshooting, this chapter was not all new. Even though the focus was on applying such skills to fixing or preventing bugs in Flash, it's not much different than fixing or preventing problems with your VCR. In any case, there are no hard-and-fast rules, but rather a general approach you must take.

Finding and documenting bugs is always the first step. You learned how to use the Debugger, but you must first know what you're looking for before you can fix it. I suggested that you could try to develop personality attributes (such as cynicism and humility) that make finding or testing for bugs easier. Ultimately, you'll develop skills that will reduce bugs in the first place. By writing quality assurance scripts and productivity scripts, you'll be well on your way.

By the way, you'll learn more details about the topics in this chapter in Workshop Chapter 14, "Offline Production," and Workshop Chapter 18, "Fixing Broken Scripts." Of course, you'll get plenty of practice fixing bugs, because they're just a fact of programming. Perhaps realizing this will make the debugging process part of the entire production rather than something extra that you hadn't expected to do.

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