Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning ActionScript 2.0 2006

.pdf
Скачиваний:
101
Добавлен:
17.08.2013
Размер:
12.47 Mб
Скачать

Chapter 5

/*

if (hasBackground == true)

{

baseMovieClip.moveTo(textFieldX-3, textFieldY-3); baseMovieClip.beginFill(0xAAAAAA); baseMovieClip.lineStyle(1, 0x333333);

baseMovieClip.lineTo(textFieldX-3, textFieldY+17); baseMovieClip.lineTo(textFieldX+17, textFieldY+17); baseMovieClip.lineTo(textFieldX+17, textFieldY-3); baseMovieClip.lineTo(textFieldX-3, textFieldY-3);

}

*/

Formatting Code

Many styles for formatting code exist, and choosing is usually a matter of personal preference, yet some coding styles are definitely more readable than others. The most important attribute about formatting code is to use white space. Code crammed together to take as little space as possible makes reading and understanding it difficult. Placing blank lines between sections of code allows related sections of code to be distinguishable, much like paragraphs of text, and inserting spaces before and after operators helps split up a line into word-like chunks.

The following styles have been found by the authors to be effective ways of formatting code:

Put start and end braces on their own lines. This applies to functions, if statements, for statements, and all other statements that use braces. The extra line between the top statement and the brace body makes it clear where the statement body begins:

function myFunction()

{

// Function code goes here

}

if (something == true)

{

// If statement code goes here

}

else

{

// Else statement code goes here

}

for (var i=0; i < 20; i++)

{

// Loop content goes here

}

You can configure Flash to auto-format braces to this style by selecting Flash Preferences (Macintosh) or Edit Preferences (Windows), choosing the Auto Format item, and then checking the first three checkboxes: Insert { On Line After If, For, Switch, While, Etc.; Insert { On Line After Function, Class, And Interface Keywords; and Don’t Cuddle } And Else. These settings apply any time you select Tools Auto Format while editing an ActionScript file or when you click the auto-format button in the Actions panel.

118

Getting Started with Coding

Use spaces between operators and operands:

//Without spaces

var totalCroutonWeight:Number=numCroutons*(croutonWeight-crumbleFactor);

// With spaces

var totalCroutonWeight:Number = numCroutons * (croutonWeight - crumbleFactor);

Avoid the shorthand if..then..else notation, which can quickly lead to some grotesque, impossible-to-read code lines. In the following code example, you can see that the second version is much clearer, even though it takes up more lines of code:

var inputValue = “”; var localValue;

// Avoid:

localValue = (inputValue == “”) ? “defaultValue” : inputValue;

// Use instead:

if (inputValue == “”)

{

localValue = “defaultValue”;

}

else

{

localValue = inputValue;

}

Understanding Variable Scope

Every variable takes up some amount of memory and exists for a particular length of time. Variables that are declared within a function — and are therefore local in scope — last only for the duration of the function. As a result, the impact of local variables on the overall memory footprint of the project is relatively small. Timeline variables and global variables last longer. If the project runs all code in a single frame, the timeline variables last for the duration of the project, and an accumulation of timeline and global variables increases the memory footprint of the application.

Memory usage is important, but more significant is the impact of timeline and global variables on your ability to find and fix problems. Every global variable used in a function works against the principle of having well-defined input and output for each function. There’s certainly a need for global and timeline variables, but there are some strategies for limiting their impact on your code, which the following sections explain.

Declaring All Global and Timeline Variables in One Place

Do not scatter global and timeline variable declarations throughout your code. Reserve a specific section at the beginning for variables. Definitely don’t declare global variables from within a function, although it is a good idea to create a startup function that can assign starting values to global variables, such as the following example shows:

// Declare timeline variables var currentScore:Number;

var startingNumLives:Number;

119

Chapter 5

var numLives:Number; var shieldLevel:Number; var ammoLeft:Number;

// Initialize application init();

function init():Void

{

currentScore = 0; startingNumLives = 9; numLives = 9; shieldLevel = 50; ammoLeft = 200;

}

Creating an Object Just for Data

Instead of having several variables on the timeline, it may make sense to group variables into a container object. This lessens the chance of having those variables conflict with properties from built-in classes, and it also helps organize related variables into a single grouping. The disadvantage of doing this is that you cannot use strong typing for the object properties of the container object. The following code shows how startup code is grouped into a function called init(), which is the only function actually called from the main script file. It can call additional functions as needed for more complex projects.

//Declare timeline variables var playerStats:Object;

//Initialize application init();

function init():Void

{

playerStats = new Object(); playerStats.currentScore = 0; playerStats.startingNumLives = 9; playerStats.numLives = 9; playerStats.shieldLevel = 50; playerStats.ammoLeft = 200;

}

Creating Access Functions

In general, global and timeline variables should not be accessed directly. If the global data implementation changes, then every function that accesses or changes the data needs to be modified as well. Instead, make the changes through intermediary access functions that are responsible for manipulating global or timeline variables. The following code shows how the currentScore, numLives, and shieldLevel properties in the playerStats timeline variable are accessed through the addToScore(), decrementNumLives(), and powerUpShield() functions:

//Declare timeline variables var playerStats:Object;

//Initialize application

120

Getting Started with Coding

init();

function init():Void

{

playerStats = new Object(); playerStats.currentScore = 0; playerStats.startingNumLives = 9; playerStats.numLives = 9; playerStats.shieldLevel = 50; playerStats.ammoLeft = 200;

}

function addToScore(addAmount:Number):Void

{

playerStats.currentScore += addAmount;

}

function decrementNumLives():Void

{

playerStats.numLives -= 1;

}

function powerUpShield(addAmount:Number):Void

{

playerStats.shieldLevel += amount;

}

// etc...

Using access functions instead of directly changing global or timeline variables provides the additional benefit of being able to respond to changes in variables that may need to be reflected by the screen state.

An even better way of manipulating important global and timeline variables is through the creation of custom classes.

Managing Data with Custom Classes

The best way to deal with global data is through the creation of one or more custom classes. The process of creating a custom class is explored in Chapter 27, so here it’s just discussed at a high level.

A custom class is the object-oriented approach to managing global data. You recall from the object-oriented programming introduction that an object is the combination of data and the methods that work with that data. Placing the access functions within the custom class along with the data, instead of placing them directly on the timeline, means that the only global reference that’s needed is the one to the object itself. Here’s an example that shows how the player stats can be manipulated through methods called from the custom playerStatsClass instead of through functions sitting on the main timeline:

// Declare timeline variables

var playerStats:PlayerStatsClass = new PlayerStatsClass();

// Data can now be modified through object methods and properties playerStats.addToScore(50);

playerStats.powerUpShield(20);

trace(playerStats.getScore());

121

Chapter 5

The addToScore(), powerUpShield(), and getScore() methods provide ways to read from and write to the variables stored internally by the class.

In this example none of the player stats is directly accessed; the implementation of the data is hidden, so it can easily be changed without affecting other code. (Creating a second instance of PlayerStatsClass easily accommodates a two-player scenario.)

Accessing Variables in Another Timeline

It is generally not a good idea to scatter code or variables throughout the timeline, yet there are times when variables need to be set in individual movie clips. You can easily do this without actually having to click a movie clip on the stage and setting the variable in the Actions panel. The following line of code placed on the main timeline creates a variable called screenId within a movie clip on the main timeline called screenHolder, and it assigns the string value “intro1” to that variable:

screenHolder.screenId = “intro1”;

Retrieving the variable is done similarly:

trace(screenHolder.screenId);

To set a variable within a nested movie clip, use the dot syntax to traverse the path of nested clips and set the variable:

applicationHolder.screenHolder.screenId = “intro1”;

trace(applicationHolder.screenHolder.screenId);

Two ways exist for code placed directly within a movie clip to access variables within the parent. The first is to use the _parent property:

//Retrieve value of screenId in screenHolder from within

//a movie clip nested inside screenHolder

_parent.screenId = “intro1”; trace(_parent.screenId);

// Outputs: intro1

The second is to use an absolute reference, starting from the base of the project and traversing through each nested movie clip:

//Retrieve value of screenId in screenHolder from anywhere _level0.applicationHolder.screenHolder.screenId = “intro1”; trace(_level0.applicationHolder.screenHolder);

//Outputs: intro1

Using the _parent syntax is generally preferred because it means that a group of movie clips can be moved around and the references will be preserved. It also means that if the project is later loaded into another project, the references will still work.

122

Getting Started with Coding

Try It Out

Access Timeline Variable from Another Movie Clip

In this exercise you access variables located in different movie clips from other movie clips.

1.Create a new Macromedia Flash document by selecting File New and choosing Flash Document from the New Document panel.

2.Rename the current timeline layer to Scripts. Click the first frame in the timeline, open the Actions panel (Window Development Panels Actions), and type in the following ActionScript code:

var baseClipTestVar:String = “base clip data”; movieClip1.movieClip1TestVar = “clip 1 data”; movieClip1.movieClip2.movieClip2TestVar = “clip 2 data”;

trace(“**************”); trace(“Accessing variables from base:”);

trace(“movieClip1.movieClip1TestVar: “ + movieClip1.movieClip1TestVar); trace(“movieClip1.movieClip2.movieClip2TestVar: “ + movieClip1.movieClip2.movieClip2TestVar);

3.Select File Save As and name the file tryItOut_accessTimelineVars.fla, choose an appropriate directory, and save it.

4.Create a new layer using the Create Layer button in the toolbar underneath the timeline. Name the layer Movie Clip 1.

5.Use the rectangle tool to draw a rectangle onscreen. Choose the Selection tool (filled arrow icon in the toolbar) and drag across the shape to select the whole shape.

6.With the shape selected, choose Modify Convert To Symbol. Give the symbol a name, such as Movie Clip 1, make sure that the Movie Clip radio button is selected, and click OK.

7.With the shape still selected, go to the Properties panel (Window Properties) and type movieClip1 into the text field on the top-left (see Figure 5-5).

8.Double-click the shape to go into edit mode for the new movie clip.

9.Rename the only layer in the timeline to Shape. Add two more layers, name them Script and Movie Clip 2, and drag them so that they are on top of the Shape layer.

10.Click the empty frame in the timeline on the Script layer, and type the following code into the Actions panel:

trace(“**************”);

trace(“Accessing variables from movieClip1:”); trace(“_parent.baseClipTestVar: “ + _parent.baseClipTestVar); trace(“_level0.baseClipTestVar: “ + _level0.baseClipTestVar); trace(“movieClip2.movieClip2TestVar: “ + movieClip2.movieClip2TestVar);

11.Click the empty frame in the timeline on the Movie Clip 1 layer. Select the rectangle tool, and choose any different color from the Color Picker in the toolbar. Draw a new rectangle within the first rectangle.

12.Choose the Selection tool and drag across the shape to select the whole shape.

123

Chapter 5

Figure 5-5

13.With the shape selected, choose Modify Convert To Symbol. Give the symbol a name, such as Movie Clip 2, make sure that the Movie Clip radio button is selected, and click OK.

14.With the shape still selected, go to the Properties panel (Window Properties) and type movieClip2 into the text field on the top-left (see Figure 5-6).

Figure 5-6

124

Getting Started with Coding

15.Double-click the shape to go into edit mode for the new movie clip.

16.Rename the only layer in the timeline to Shape. Add one more layer called Script.

17.Click the empty frame in the timeline on the Script layer, and type the following code into the Actions panel:

trace(“**************”);

trace(“Accessing variables from movieClip2:”); trace(“_parent._parent.baseClipTestVar: “ + _parent._parent.baseClipTestVar); trace(“_parent.movieClip1TestVar: “ + _parent.movieClip1TestVar); trace(“_level0.baseClipTestVar: “ + _level0.baseClipTestVar); trace(“_level0.movieClip1.movieClip1TestVar: “ + ;

_level0.movieClip1.movieClip1TestVar);

18.Save the file and select Control Test Movie.

How It Works

The nested movie clips created in this example query the variables from other movie clips using both relative and absolute references.

The first segment of code on the base timeline does the actual setting of data to the root timeline, to the first movie clip, and to the nested movie clip:

var baseClipTestVar:String = “base clip data”; movieClip1.movieClip1TestVar = “clip 1 data”; movieClip1.movieClip2.movieClip2TestVar = “clip 2 data”;

The second segment of code accesses the variables relative to the base timeline:

trace(“**************”); trace(“Accessing variables from base:”);

trace(“movieClip1.movieClip1TestVar: “ + movieClip1.movieClip1TestVar); trace(“movieClip1.movieClip2.movieClip2TestVar: “ + ;

movieClip1.movieClip2.movieClip2TestVar);

Within the script for the first movie clip, the code accesses the variable at the base timeline by using the relative _parent property, and then by using the absolute _level0 property. It then accesses the variable in the nested movie clip through dot syntax relative to movieClip1:

trace(“**************”);

trace(“Accessing variables from movieClip1:”); trace(“_parent.baseClipTestVar: “ + _parent.baseClipTestVar); trace(“_level0.baseClipTestVar: “ + _level0.baseClipTestVar); trace(“movieClip2.movieClip2TestVar: “ + movieClip2.movieClip2TestVar);

Within the script for the second movie clip, the variable on the root timeline and the variable on the parent movie clip are both accessed through relative paths. Finally, those same two variables are accessed through absolute paths:

trace(“**************”);

trace(“Accessing variables from movieClip2:”); trace(“_parent._parent.baseClipTestVar: “ + _parent._parent.baseClipTestVar); trace(“_parent.movieClip1TestVar: “ + _parent.movieClip1TestVar);

125

Chapter 5

trace(“_level0.baseClipTestVar: “ + _level0.baseClipTestVar); trace(“_level0.movieClip1.movieClip1TestVar: “ + ;

_level0.movieClip1.movieClip1TestVar);

The output for this example should look like the following:

**************

Accessing variables from base: movieClip1.movieClip1TestVar: clip 1 data movieClip1.movieClip2.movieClip2TestVar: clip 2 data

**************

Accessing variables from movieClip1: _parent.baseClipTestVar: base clip data _level0.baseClipTestVar: base clip data movieClip2.movieClip2TestVar: clip 2 data

**************

Accessing variables from movieClip2: _parent._parent.baseClipTestVar: base clip data _parent.movieClip1TestVar: clip 1 data _level0.baseClipTestVar: base clip data _level0.movieClip1.movieClip1TestVar: clip 1 data

One of the reasons why you might want to dynamically assign a variable to a movie clip is because you have a number of almost identical buttons that each need to be distinguished from one another when they call a single common click handler. You may come across this scenario when creating clickable image thumbnails, a custom calendar with clickable dates, or a custom menu. The next Try It Out exercise explores how to handle clicks on different buttons with a single button handler function.

Try It Out

Assign an ID to a Dynamically Created Movie Clip

This example shows how to dynamically assign an ID to a number of dynamic movie clips, and to then reference that ID when a button is clicked.

1.Create a new Macromedia Flash document by selecting File New and choosing Flash Document from the New Document panel.

2.Click the first frame in the timeline, open the Actions panel (Window Development Panels Actions), and type in the following ActionScript code:

#include “tryItOut_assignID.as”

3.Select File Save As, name the file tryItOut_assignID.fla, choose an appropriate directory, and click Save.

4.Create a new script file by selecting File New and choosing ActionScript File from the New Document panel.

5.Select File Save As and ensure it is showing the same directory containing the .fla file. Name the file tryItOut_assignID.as and click Save.

6.Type the following code into the new ActionScript file:

126

Getting Started with Coding

var baseMovieClip:MovieClip = _level0; var tempMovieClipHolder:MovieClip;

for (var i:Number = 0; i < 5; i++)

{

//Create a movie clip baseMovieClip.createEmptyMovieClip(“buttonClip” + i, ;

baseMovieClip.getNextHighestDepth()); tempMovieClipHolder = baseMovieClip[“buttonClip” + i];

//Assign an ID to a timeline variable tied to the button tempMovieClipHolder.buttonID = i;

//Draw something inside the button so it can be seen and clicked on tempMovieClipHolder.lineStyle(1, 0x333333); tempMovieClipHolder.beginFill(0x6666AA); tempMovieClipHolder.moveTo(0, 0);

tempMovieClipHolder.lineTo(50, 0); tempMovieClipHolder.lineTo(50, 25); tempMovieClipHolder.lineTo(0, 25); tempMovieClipHolder.lineTo(0, 0);

//Position the button

tempMovieClipHolder._y = 10; tempMovieClipHolder._x = i * 60 + 10;

//Make the button clickable. Pass along the ID that was assigned

//to the button’s timeline

tempMovieClipHolder.onRelease = function()

{

handleButtonClick(this.buttonID);

}

}

function handleButtonClick(buttonID:Number)

{

trace(“Clicked on button number: “ + buttonID);

}

7.Save the file, return to the .fla, and select Control Test Movie.

How It Works

When a number of buttons are dynamically created onscreen, and each performs a similar function, something must be embedded in each button to allow it to be uniquely identified. The actual process of doing this is very simple, and most of the code in this example is there just to create, fill, and position the buttons.

First, a couple of timeline variables are declared:

var baseMovieClip:MovieClip = _level0; var tempMovieClipHolder:MovieClip;

127