ActionScript Basics: Chapter 1 - ActionScript 3.0 Cookbook
Pages: 1, 2, 3, 4

The syntax of a for statement consists of five basic parts:

The for keyword
Every for statement must begin with a for keyword.
Initialization expression
Loop typically employs an index variable (a loop counter) that is initialized when the statement is first encountered. The initialization is performed only once regardless of how many times the loop is repeated.
Test expression
The loop should include a test expression that returns either true or false. The test expression is evaluated once each time through the loop. Generally, the test expression compares the index variable to another value, such as a maximum number of loop iterations. The overall expression must evaluate to true for the for statement's body to execute (contrast this with a do...while loop, which executes at least once, even if the test expression is false). On the other hand, if the test expression never becomes false, you'll create an infinite loop, resulting in a warning that the Flash Player is running slowly.
Update expression
The update expression usually updates the value of the variable used in the test expression so that, at some point, the conditional test becomes false and the loop ends. The update expression is executed once each time through the loop. An infinite loop is often caused by failing to update the appropriate variable in the update expression (usually the same variable used in the test expression).
Statement body
The statement body is a block of substatements enclosed within curly braces that is executed each time through the loop. If the test expression is never true, the for statement's body isn't executed.

The for keyword should come first, and it should be followed by the initialization, test, and update expressions enclosed within parentheses. Semicolons must separate the three expressions from one another (although the initialization, test, and update statements are optional, the semicolons are mandatory). The remainder of the for loop is made up of the statement body enclosed in curly braces:

for (initialization; test; update) {
  statement body
}

Here is an example of a for statement that outputs the numbers from 0 to 999:

for (var i:int = 0; i < 1000; i++) {
  trace(i);
}
trace ("That's the end.");

To understand the for statement, you can follow along with the process the ActionScript interpreter uses to process the command. In the preceding example, the for keyword tells the interpreter to perform the statements within the for loop as long as the conditional expression is true. The process is as follows:

  1. The initialization expression is executed only once, and it sets the variable i to 0.
  2. Next, the interpreter checks the test expression (i < 1000). Because i is 0, which is less than 1,000, the expression evaluates to true and the trace( ) action within the for statement body is executed.
  3. The ActionScript interpreter then executes the update statement, in this case i++, which increments i by 1.
  4. The interpreter then repeats the process from the top of the loop (but skipping the initialization step). So the interpreter again checks whether the test expression is true and, if so, executes the statement body again. It then executes the update statement again.

This process repeats until the test expression is no longer true. The last value displayed in the Output window is 999 because once i is incremented to 1,000, the test expression no longer evaluates to true and the loop comes to an end. Once the loop terminates, execution continues with whatever commands follow the loop.

Both the initialization and update expressions can include multiple actions separated by commas. You should not, however, use the var keyword more than once in the initialization expression. The following example simultaneously increments i, decrements j, and displays their values in the Output window:

for (var i:int = 0, j:int = 10; i < 10; i++, j--) {
  trace("i is " + i);
  trace("j is " + j);
}

The preceding example is not the same as two nested for statements (which is shown in the next code block.)

It is also common to use nested for statements. When you use a nested for statement, use a different index variable than that used in the outer for loop. By convention, the outermost for loop uses the variable i, and the nested for loop uses the variable j. For example:

for (var i:int = 1; i <= 3; i++) {
  for (var j:int = 1; j <= 2; j++) {
    trace(i + " X " + j + " = " + (i * j));
  }
}

The preceding example displays the following multiplication table in the Output window:

1 X 1 = 1
1 X 2 = 2
2 X 1 = 2
2 X 2 = 4
3 X 1 = 3
3 X 2 = 6

It is possible to nest multiple levels of for statements. By convention, each additional level of nesting uses the next alphabetical character as the index variable. Therefore, the third level of nested for statements typically use k as the index variable:

for (var i:int = 1; i <= 3; i++) {
  for (var j:int = 1; j <= 3; j++) {
    for (var k:int = 1; k <= 3; k++) {
      trace(i + " X " + j + " X " + k + " = " + (i * j * k));
    }
  }
}

Additionally, you can use for statements to loop backward or to update the variable in ways other than by simply adding or subtracting one:

// Count backward from 10 to 1.
for (var i:int = 10; i > 0; i--) {
  trace(i);
}

// Display a sequence of square roots.
for (var i:Number = 50000; i > 2; i = Math.sqrt(i)) {
  trace(i);
}

In this case, the variable i winds up holding values other than integers, so it is best to declare it as a Number rather than an int.

You should not use a for statement to perform tasks over time.

Many programmers make the mistake of trying to use for statements to animate sprites; for example:

for (var i:int = 0; i < 20; i++) {
  _sprite.x += 10;
}

Whereas the preceding code moves the sprite 200 pixels to the right of its starting point, all the updates take place within the same frame. There are two problems with this. First, the Stage updates only once per frame, so only the last update is shown on the Stage (causing the sprite to jump 200 pixels suddenly rather than moving smoothly in 20 steps). Second, even if the Stage updates often, each iteration through the for loop takes only a few milliseconds, so the animation would happen too quickly. For actions that you want to take place over time, use the enterFrame event (see Recipe 1.5) or a timer (see Recipe 1.12).

Moreover, tight repeating loops should not be used to perform lengthy processes (anything that takes more than a fraction of a second). The Flash Player displays a warning whenever a single loop executes for more than 15 seconds. Using the other methods (just mentioned) avoids the warning message and allows Flash to perform other actions in addition to the repeated actions that are part of the loop.

See Also

Recipes 1.5 and 1.12. The for statement is used in many practical situations, and you can see examples in a great many of the recipes throughout this book. See Recipe 5.2 and Recipe 12.8 for some practical examples. Recipe 5.16 discusses for...in loops, which are used to enumerate the properties of an object or array.

Section 1.12: Repeating a Task over Time

Problem

You want to perform some action or actions over time.

Solution

Use the Timer class. Alternatively, listen for the enterFrame event of a sprite.

Discussion

The Timer class is new to ActionScript 3.0, and is recommended over the earlier setInterval( ) and setTimeout( ) functions. When you create an instance of the Timer class, it fires timer events at regular intervals. You can specify the delay between events and how many times you want the events to fire in the Timer constructor:

var timer:Timer = new Timer(delay, repeatCount);

You use addEventListener to set up a method to handle these events. After you create the timer and set up a listener, use its start( ) method to start it and stop( ) to stop it.

The Timer class is part of the flash.utils package, and there is also a TimerEvent class in the flash.events package, so those need to be imported:

package {
    import flash.display.Sprite;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    
    public class ExampleApplication extends Sprite {
        // Declare and initialize a variable to store the value
        // of the previous timer reading.
        private var _PreviousTime:Number = 0;
        
        public function ExampleApplication( ) {
            var tTimer:Timer = new Timer(500, 10);
            tTimer.addEventListener(TimerEvent.TIMER, onTimer);
            tTimer.start( );
        }
        
        private function onTimer(event:TimerEvent):void {
            // Output the difference between the current timer value and
            // its value from the last time the function was called.
            trace(flash.utils.getTimer( ) - _PreviousTime);
            _PreviousTime = flash.utils.getTimer( ); 
        }
    }
}

The getTimer( ) function (previously a top-level function), has been moved to the flash.utils package as well. This simply returns the number of milliseconds since the application started.

x

In the preceding example, even though the interval is theoretically 500 milliseconds in practice its accuracy and granularity depend on computer playback performance in relation to other tasks demanded of the processor. There are two implications to this:

  • Don't rely on timers to be extremely precise.
  • Don't rely on timer intervals to be smaller than approximately 10 milliseconds.

If you want to emulate the functionality of the setInterval( ) function, set the repeat count to zero. This causes the timer event to fire indefinitely. In this case, the stop( ) method is analogous to the clearInterval( ) function, and stops the timer from firing further events.

Similarly, if you want to duplicate the setTimeout( ) function, set the repeat count to one. The timer waits the specified amount of time, fires one event, and ends.

One of the neat things you can do with the Timer class is create animations that are independent of the movie's frame rate. With a timer you can call a method at any interval you want. Here is an example in which two timers are set--one for a square sprite (every 50 milliseconds)--and one for a circle sprite (every 100 milliseconds):

package {
    import flash.display.Sprite;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    
    public class ExampleApplication extends Sprite {
        private var _square:Sprite;
        private var _circle:Sprite;
        
        
        public function ExampleApplication( ) {
            // Create the two sprites and draw their shapes
            _square = new Sprite( );
            _square.graphics.beginFill(0xff0000);
            _square.graphics.drawRect(0, 0, 100, 100);
            _square.graphics.endFill( );
            addChild(_square);
            _square.x = 100;
            _square.y = 50;
            
            _circle = new Sprite( );
            _circle.graphics.beginFill(0x0000ff);
            _circle.graphics.drawCircle(50, 50, 50);
            _circle.graphics.endFill( );
            addChild(_circle);
            _circle.x = 100;
            _circle.y = 200;
            
            // Create the two timers and start them
            var squareTimer:Timer = new Timer(50, 0);
            squareTimer.addEventListener(TimerEvent.TIMER, onSquareTimer);
            squareTimer.start( );
            
            var circleTimer:Timer = new Timer(100, 0);
            circleTimer.addEventListener(TimerEvent.TIMER, onCircleTimer);
            circleTimer.start( );
        }
        
        // Define the two handler methods
        private function onSquareTimer(event:TimerEvent):void {
            _square.x++;
        }
        
        private function onCircleTimer(event:TimerEvent):void {
            _circle.x++;
        }
    }
}

It is also possible to use the enterFrame event of a sprite to have some action (or actions) repeat over time. The Timer technique offers some advantages over the enterFrame event method, most notably that it allows you to create intervals that differ from the frame rate of the .swf. With enterFrame, the handling method is called at the frame rate.

With that said, there are still times when using enterFrame is appropriate. For example, you may want something to occur at the frame rate of the .swf. One such scenario is when you want to reverse the playback of the frames in a movie clip.

Section 1.13: Creating Reusable Code

Problem

You want to perform a series of actions at various times without duplicating code unnecessarily throughout your movie.

Solution

Create a method and then call (i.e., invoke) it by name whenever you need to execute those actions. When a function is a member of a class, it is often called a method.

Here is how to create a method of a class:

               accessModifier function functionName ( ):ReturnDataType {
  // Statements go here.
}

To call (i.e., execute) the named method, refer to it by name, such as:

  
               functionName( );

Discussion

Grouping statements into a method allows you to define the method once but execute it as many times as you'd like. This is useful when you need to perform similar actions at various times without duplicating the same code in multiple places. Keeping your code centralized in methods makes it easier to understand (because you can write the method once and then ignore the details when using it) and easier to maintain (because you can make changes in one place rather than in multiple places).

Like class variables, methods can be declared with access modifiers. These determine which other classes are able to call the methods. The available access modifiers are:

private
Can only be accessed from within the class itself.
protected
Can be accessed by the class or any subclass. This is instance-based. In other words, an instance of a class can access its own protected members or those of its superclasses. It cannot access protected members on other instances of the same class.
internal
Can be accessed by the class or any class within the same package.
public
Can be accessed by any class.

The definition of private has changed since ActionScript 2.0, where it allowed access by subclasses. If you do not specify an access modifier explicitly, the method takes on the default internal access.

The following class defines a drawLine method and calls it 10 times, rather than repeating the three lines of drawing code for each line:

package {
    import flash.display.Sprite;

    public class ExampleApplication extends Sprite
    {
        public function ExampleApplication( ) {
            for(var i:int=0;i<10;i++) {
                drawLine( );
            }
        }
    
        private function drawLine( ):void {
            graphics.lineStyle(1, Math.random( ) * 0xffffff, 1);
            graphics.moveTo(Math.random( ) * 400, Math.random( ) * 400);
            graphics.lineTo(Math.random( ) * 400, Math.random( ) * 400);
        }
    }
}

Another important method type is a static method. Static methods aren't available as a member of an instance of that class, but instead are called directly from the class itself. For example, in a class named ExampleApplication, you could define a static method as follows:

public static function showMessage( ):void {
    trace("Hello world");
}

You could then call that method like so:

ExampleApplication.showMessage( );

Some classes contain nothing but static methods. The Math class is an example. Note that you don't have to create a new Math object to use its methods; you simply call the methods as properties of the class itself, such as Math.random( ), Math.round( ), and so on.

Pages: 1, 2, 3, 4

Next Pagearrow