Showcase and discover digital art at yex

Follow Design Stacks

Subscribe to our free newsletter to get all our latest tutorials and articles delivered directly to your inbox!

Meet the Output panel

Meet the Output panel

Your first stop is the Output panel, which displays text that is output either by Flash or by you. To view the Output panel, open an existing Flash document or create a new one, and then select Window > Output or press the F2 key. (Doing so again will close this panel.) By default, the panel is empty; however, you may choose to fill it with informative messages at any point during the playback of your SWF file. In certain cases, such as ActionScript syntax errors, Flash will send messages to the Output panel on its own.

The Output panel is configurable through the Options menu in its upper right corner (see Figure 1). These options enable you to apply word wrap, copy, and clear the panel’s contents, find keywords or phrases, save to a text file, print, and more.

Options menu in the Output panel

Figure 1. Options menu in the Output panel

Note: The Filter Level option enables you to suppress output by selecting the None setting; the Verbose setting permits output and is the default. If you fail to see expected text in the Output panel, double-check your Filter Level setting. If you still don’t see output, select File > Publish Settings, click the Flash tab, and verify that the Omit trace actions check box is cleared.

There are basically two scenarios in which the Output panel displays text: Either Flash is warning you about an error in your ActionScript, or you have chosen to send yourself a message in order to see the inner workings of a SWF file.

Messages sent by you are only output from a SWF file, so in order to put the Output panel to work, you’ll have to generate one. One of the ways to do this is by selecting Control > Test Movie or by pressing Control+Enter (Windows) or Command+Return (Macintosh). You’ll learn how to output our own messages in a moment; first, let Flash have a go.

Messages sent by Flash may be output both from SWF files and FLA files. To see such a message, open a new Flash document and select Window > Actions or press F9 (Windows) or Option+F9 (Macintosh) to open the Actions panel.

Type the following purposefully incorrect ActionScript code into the Actions panel:

var example:Number = "this string should be a number"; 

Now click the Check Syntax button at the top of the Actions panel (see Figure 2). The Output panel will open automatically if it is not already open.

Check Syntax button in the Actions panel

Figure 2. Check Syntax button in the Actions panel

Flash sends an error message to the Output panel because of an erroneous attempt to set the value of a number variable to a string. This sort of information is terrific for correcting invalid ActionScript code before a file is published. If invalid ActionScript code makes it to the SWF file after all, similar error messages may appear during SWF file playback as errors are encountered.

Now, how can you send your own messages? And why would you want to? Well, that depends on your needs, of course. Perhaps your goal is to loop a Flash banner three times and then stop. You’ve created a variable to keep track of these loops, but for some reason the SWF file plays through only once. What could be wrong? Get ready to embark on your troubleshooting jaunt—with the global trace() function.

trace() function

The ActionScript trace() function is your direct conduit to the Output panel. This function is a no-frills, easy-to-use, and very helpful tool. It accepts only one parameter, which is basically whatever information you’d like to see.

Open loop_3_times.fla from the ZIP file that accompanies this article to look at an example of the above scenario. In this example, the SWF file is supposed to loop three times, but doesn’t loop at all.

Before you proceed, however, a historical note is in order. The ActionScript language has matured considerably in recent versions of Flash. The code presented in loop_3_times.fla is a legacy solution that works only in SWF files published for Flash Player 4, 5, or 6. (These version choices are available under File > Publish Settings, Flash tab.) If you want to take advantage of new Flash 8 features, you will obviously have to publish your SWF file for Flash Player 8. In so doing, you may find that older, seemingly correct code samples no longer behave. It’s good to be aware of this because it is simply a fact of life that plenty of third-party online Flash tutorials are outdated. Now, don’t get me wrong, they’re often still useful—but you’ll have an easier time bringing them up to speed when you understand the built-in debugging tools at your disposal.

Take a look at how the trace() function can show you what’s wrong with loop_3_times.fla. Open the Actions panel and click frame 25 of the Scripts layer:

loopCount++;
if (loopCount >= 3) {
stop();
}

A variable, loopCount, is incremented using the increment operator (++). If the loopCount value is greater than or equal to 3, the Timeline is stopped; otherwise, the Timeline naturally loops again from the beginning. Short and sweet. So why doesn’t this work in Flash Player 8?

Clearly, the success of this particular script hinges on the value of the loopCount variable. Presumably its value begins as 0. It increments to 1. Because 1 is not equal to or greater than 3, the SWF file repeats. Next time, the loopCount value increments to 2, and so on.

As it turns out, the value of loopCount does not begin at 0. The problem lies in the fact that the variable is never formally declared and initialized. You’re missing a line early on that reads var loopCount:Number = 0; to state specifically what your default value should be. In ActionScript 1.0 and 2.0, Flash automatically declares a variable for you if you do not. In Macromedia Flash MX and earlier, such automatically declared variables could be incremented. In Flash MX 2004 and later, in ActionScript 1.0 and 2.0, they cannot. For this reason, the value of loopCount immediately following the increment operator is a special number value called NaN (Not a Number), which happens to evaluate as equal to or greater than any other number in ActionScript.

Tracing variables

You can bring such knowledge to light with trace(). Add the following highlighted code to the existing ActionScript code:

loopCount++;
trace(loopCount);
if (loopCount >= 3) {
stop();
}

Test the SWF file to see that it still only plays through once—but the message NaN appears in the Output panel. This sort of discovery is often the first step toward a solution. Because the loopCount value is not an incremental number, you ought to declare the variable formally and initialize it to 0. You will do this only if the variable does not already exist (again, new code is highlighted):

if (loopCount == undefined) {
var loopCount:Number = 0;
}
loopCount++;
trace(loopCount);
if (loopCount >= 3) {
stop();
}

Tip: If you make several trace() statements in a row, you may find that you quickly lose track of what your output refers to. To remedy this, use the addition operator (+) to concatenate a string with your messages. In the code sample above, for example, you might amend the trace() statement as follows:

if (loopCount == undefined) {
var loopCount:Number = 0;
}
loopCount++;
trace("The value of loopCount is currently: " + loopCount);
if (loopCount >= 3) {
stop();
}

Thanks to this concatenation, the Output panel shows a more descriptive message than provided by a number on its own.

Tracing confirmations

The trace() function is useful with more than just variables. Any object that can be referenced by ActionScript can be traced. This includes movie clips, dynamic text fields, buttons, and even nonvisual objects like strings, arrays, dates, sounds, and more. If an object doesn’t implicitly contain textual content, trace() outputs a text description of it.

Often it’s helpful simply to trace a literal string in order to confirm that an object is behaving as expected. This bit of troubleshooting has come to the rescue in countless posts on the Flash support forums. Typically, a developer has coded a button to invoke the global getURL() function so that the button will open a new web page when clicked. For some reason, the button doesn’t work. Could this be due to incorrect use of getURL()? Possibly. But just as likely, the developer has merely mistyped the button’s instance name. Adding trace() to one of the button’s event handlers allows this possibility to be confirmed or denied.

Open button_events.fla and click frame 1 of the Scripts layer to see an example. Only one of these functions actually does something (line 8 in the script below); the rest are just traces. If this function fails for some reason, you’ll know that the problem lies with this function alone. You know it because the button’s events are clearly being handled otherwise, thanks to your troubleshooting messages:

myButton.onRollOver = function() {
trace(this + " has been rolled over");
};
myButton.onPress = function() {
trace(this + " has been pressed");
};
myButton.onRelease = function() {
myMovieClip._visible = !myMovieClip._visible;
trace(this + " has been released");
};
myButton.onRollOut = function() {
trace(this + " has been rolled out");
};

Test this SWF file and keep an eye on the Output panel as you move your mouse to the circular button, press and release, then move it away.

Tracing object properties

Even the most seemingly clear ActionScript can sometimes pull a few surprises. Consider the following example, in which a button manipulates the opacity of a movie clip. Open movieclip_alpha.fla and click frame 1 of the Scripts layer to follow along:

var changeAmount:Number = −10;
myButton.onRelease = function() {
myMovieClip._alpha += changeAmount;
if (myMovieClip._alpha == 50 || myMovieClip._alpha == 100) {
changeAmount *= −1;
}
};

Two symbols exist in this FLA file: a button with the instance name myButton and a movie clip with the instance name myMovieClip. A variable, changeAmount, is declared and its value is initialized to –10. The button uses this variable to update the MovieClip._alpha property of myMovieClip, whose original _alpha value is 100. Because the current value of changeAmount is –10, one would expect the opacity of myMovieClip to be reduced to 90 when the button is clicked, then 80, and so on. When the opacity reaches either 50 or 100, a multiplication converts the value of changeAmount to a negative number if it is positive, and vice versa. Repeated clicking alternately fades myMovieClip out and in, again and again. The only problem is that this is not what actually occurs. Test it and see! For some reason, repeated clicking of the button fades out myMovieClip altogether. So what’s going on?

Let’s lift the blindfold. Add the following highlighted code to the existing ActionScript:

var changeAmount:Number = −10;
myButton.onRelease = function() {
myMovieClip._alpha += changeAmount;
trace("the value of myMovieClip._alpha is: " + myMovieClip._alpha);
if (myMovieClip._alpha == 50 || myMovieClip._alpha == 100) {
changeAmount *= -1;
}
};

Test the SWF file again and keep an eye on the Output panel while you repeatedly click the button. Surprisingly enough, the value of myMovieClip._alpha does not actually change by increments of 10. This means it never hits exactly 50 and is consequently never converted to a positive number.

The answer to this perplexity is that the MovieClip._alpha property actually refers to 256 levels of transparency, but can be adjusted only in terms of percentage. Without the trace() function, you might have been stumped by this, but one solution already presents itself. Because the anticipated 50-mark is actually closer to 49.21875, you can change the pair of equality operators (==) to “less than or equal to” (<=) and “greater than or equal to” (>=), respectively. In this way, you can account for the decimal places—again, thanks to trace():

var changeAmount:Number = -10;
myButton.onRelease = function() {
myMovieClip._alpha += changeAmount;
trace("the value of myMovieClip._alpha is: " + myMovieClip._alpha);
if (myMovieClip._alpha <= 50 || myMovieClip._alpha >= 100) {
changeAmount *= -1;
}
};

Tracing object properties recursively

While objects can be a convenient way to organize data—consider the Array and XML classes—they can also become overwhelmingly complex. In fact, with XML especially, you may not even be aware at first what an object’s properties are. This makes the prospect of tracing any particular property a daunting one.

In cases like this, a combination of the trace() function, the for..in statement, and recursion becomes a powerful tool. A recursive function is one that invokes itself until certain conditions are met, at which point it stops. Unless you’re familiar with this concept, it may seem a little strange but it’s a great way to step through nested hierarchies to find every last branch.

After making sure that trace_xml.fla and xml_demo.xml are in the same folder, open trace_xml.fla and test the SWF file to see a recursive trace of all the nodes of the dynamically loaded XML file. The custom traceXML() function is what pulls this off. A step-by-step discussion of how it works is beyond the scope of this article, but you’re welcome to paste this function into your own projects in order to view the contents of unfamiliar XML objects.

When the function is defined, simply invoke it and pass in an XML object’s XML.firstChild property as the parameter:

function traceXML(node:XMLNode, tab:String):Void {
if (tab == null) tab = "";
if (node.nodeName != null) {
var prop:String;
var str:String = "<" + node.nodeName;
var child:XMLNode = node.firstChild;
for (prop in node.attributes) {
str += " " + prop + "="" + node.attributes[prop] + """;
}
if (child) {
trace(tab + str + ">");
while (child) {
traceXML(child, tab + " ");
child = child.nextSibling;
}
trace(tab + "</" + node.nodeName + ">");
} else {
trace(tab + str + > />>);
}
} else {
trace(tab + node.nodeValue);
}
}
traceXML(yourXml.firstChild);

Special thanks to Branden Hall, who gave me kind permission to put an ActionScript 2.0 gloss on his original printXML() function.

Open trace_movieclip.fla to see a similar approach to tracing movie clip hierarchies:

function traceMovieClip(target:MovieClip):Void {
var clip:String;
for (clip in target) {
if (target[clip] instanceof MovieClip) {
trace(target[clip]);
traceMovieClip(target[clip]);
}
}
}
traceMovieClip(yourMovieClipInstance);

Test the SWF file to see a recursive trace of all the movie clips nested inside the main Timeline. Some have been placed on the Stage by hand; others are created through ActionScript. The traceMovieClip() function accepts any movie clip reference as a parameter. In the sample file, the parameter’s value is the global this property, which in this context refers to the main Timeline. Change this to mc1parent (one of the dynamically created movie clips) to see how the output changes.

Open trace_object.fla to see the same concept used to trace hierarchical properties of a generic Object instance—even when those properties include an array that contains another object!

Tracing “nothing at all”

Sometimes the trace() function outputs undefined, which means you’ve hit a variable that has not yet had a value assigned to it. Learning this can be very useful, especially if you expect the variable to have a value. Often it may lead you straight to the solution (“Hey, I forgot to initialize my variable here…”). The trace() function may also output null, which means you’ve hit a variable that earlier contained a value but currently does not—potentially just as useful to know.

Note: Both ActionScript 1.0 and 2.0 create a variable on the spot if a referenced one doesn’t exist, so make sure to watch for spelling mistakes. You might be checking on a variable named cost but tracing the typo cosst. If so, a cosst variable will be created whose value is undefined.

typeof operator

You may want to know more about a given object than its current value. The typeof operator returns an object’s data type in lowercase. Use this operator in conjunction with the trace() function to find out, for example, if the property you thought was a number is actually a string, or some other inappropriate data type.

Open typeof_sum.fla and click frame 1 of the Scripts layer to follow along. The ActionScript seems straightforward enough. Two input text fields enable the user to supply a couple of numbers. Clicking the button adds the values of these numbers and places their sum into a third dynamic text field:

myButton.onRelease = function() {
tfSum.text = tfNumber1.text + tfNumber2.text;
};

The problem is that these numbers don’t add up correctly. The default 2 plus 2, for example, shows up as 22. Why? As it turns out, the TextField.text property always returns a string and the addition operator (+) concatenates strings. What the user thinks are numbers aren’t numbers at all: the words 2 and 2 combine to make the word 22, just like cat and astrophe combine to make the word catastrophe. A traced typeof operation makes this clear. Change the preceding ActionScript code to read as follows (new code highlighted):

myButton.onRelease = function() {
trace("the value "+tfNumber1.text+" is a "+typeof(tfNumber1.text));
trace("the value "+tfNumber2.text+" is a "+typeof(tfNumber2.text));
tfSum.text = tfNumber1.text + tfNumber2.text;

};

When the button is clicked, the Output panel shows that the global parseInt() function is a good idea here because parseInt() converts strings into numbers:

myButton.onRelease = function() {
tfSum.text = parseInt(tfNumber1.text) + parseInt(tfNumber2.text);
};

Be aware that the typeof operator has its limitations. There are two kinds of data types in ActionScript: primitive and composite. The typeof operator is really only useful with primitives, which are among the elemental units of a language. In ActionScript, these include string, number, Boolean, null, and undefined. Composite data types represent a unique combination of these. The typeof operator simply returns object when tested against a composite.

While this assessment is certainly true, it is not especially specific. Open typeof_primitive_composite.fla to test the following ActionScript in a SWF file:

// Primitives
// Boolean
var myBoolean:Boolean = true;
trace("typeof Boolean: " + typeof(myBoolean));
// output: typeof Boolean: boolean
// Number
var myNumber:Number = 5;
trace("typeof Number: " + typeof(myNumber));

// output: typeof Number: number
// String
var myString:String = "sample";
trace("typeof String: " + typeof(myString));
// output: typeof String: string

// Composites
// Array
var myArray:Array = new Array();
trace("typeof Array: " + typeof(myArray));
// output: typeof Array: object
// Date
var myDate:Date = new Date();
trace("typeof Date: " + typeof(myDate));
// output: typeof Date: object
// Sound
var mySound:Sound = new Sound();
trace("typeof Sound: " + typeof(mySound));
// output: typeof Sound: object

Note: Variables that represent primitives may either be declared directly or as the result of a constructor. It is worth noting that typeof does not return the specific primitive for variables whose values are set by a constructor; instead, it returns object:

// String (constructor)
var myString:String = new String("sample");
trace("typeof String (constructor): " + typeof(myString));
// output: typeof String: object

instanceof operator

The instanceof operator makes up for the shortcoming of typeof because it tests whether an object is an instance of a given constructor. In terms of debugging, this means you may use instanceof as a finer-toothed comb than typeof. Notice the different manner in which this operator is employed. The tested expression must be arranged in the format objectToCheck instanceof ClassName, which returns either true or false. Open instanceof.fla to test the following ActionScript code in a SWF file:

// Array
var myArray:Array = new Array();
trace("instanceof Array: " + (myArray instanceof Array));
// output: instanceof Array: true
// Date
var myDate:Date = new Date();
trace("instanceof Date: " + (myDate instanceof Date));
// output: instanceof Date: true
// Sound
var mySound:Sound = new Sound();
trace("instanceof Sound: " + (mySound instanceof Sound));
// output: instanceof Sound: true

Note: Tested against a primitive, instanceof returns true only when a variable’s value is compared to the right class and set by a constructor:

var myString:String = "This goat is nibbling my hat.";
trace("instanceof String: " + (myString instanceof String));
// output: instanceof String: false
var myString:String = new String("After all, it is a straw hat.");
trace("instanceof String (constructor): "
+ (myString instanceof String));
// output: instanceof String (constructor): true

Of course, everything comes at a price. The disadvantage of instanceof is that it forces you to decide on your own what data type an object might be and then check whether you’re right, while typeof simply tells you the answer, albeit in less detail.

Object and variable dumps

Sooner or later, you may be faced with a misbehaving FLA file built by someone else. Until you take the time to locate every last shred of ActionScript, you may not even know where to begin inserting any troubleshooting trace() functions. In a situation like this, you may simply want a dump of all the current values of a SWF file’s objects and variables. Consider this an ad hoc “table of contents” for a given snapshot of a SWF file.

Open dump.fla for a file that’s prepopulated with a few objects. Test this SWF file and select Debug > List Objects or Debug > List Variables from the File menu of the SWF file itself (not the File menu in the Flash authoring program). Regardless of the occurrence (or absence) of trace() in your code, these commands send a list of the chosen data to the Output panel. Notice that List Objects displays the movie clips, text field, and button but omits the variables declared in frame 1 of the main Timeline. List Variables shows all of these, including detailed property information on the text field and button.

Depending on the complexity of your document, either one of these dumps might be huge, so you may want to send contents to the printer or save to a text file for easier inspection.

Note: Using List Objects or List Variables clears the Output panel of any existing trace() output.

Comments