8.7. Timeline and Clip Event Loops

All the
loops
we've looked at so far cause the
interpreter to repeatedly execute blocks of code. Most of your loops
will be of this "ActionScript-statement" type. But
it's also sometimes desirable to create a timeline
loop by looping Flash's playhead in the timeline. To
do so, attach a series of statements to any frame; on the next frame,
attach a gotoAndPlay( ) function whose
destination is the previous frame. When the movie plays, the playhead
will cycle between the two frames, causing the code on the first
frame to be executed repeatedly.

We can make a simple timeline loop by following these steps:

Start a new Flash movie.

On frame 1, attach the following statement:

trace("Hi there! Welcome to frame 1");

On frame 2, attach the following statements:

trace("This is frame 2");
gotoAndPlay(1);

Select Control Test
Movie.

When we test our movie, we see an endless stream of the following
text:

Hi there! Welcome to frame 1
This is frame 2
Hi there! Welcome to frame 1
This is frame 2

Timeline loops can do two things
ordinary loops cannot:

They can execute a block of code an infinite number of times without
causing an error.

They can execute a block of code
that requires a Stage update between loop iterations.

This second feature of timeline loops requires a little more
explanation. When any frame's script is executed, the movie
Stage is not updated visually until the end of the script. This means
that traditional loop statements cannot be used to perform repetitive
visual or audio tasks because the task results aren't rendered
between each loop iteration. Repositioning a movie clip, for example,
requires a Stage update, so we can't programmatically animate a
movie clip with a normal loop statement.

You might assume that the following code would visually slide the
ball movie clip horizontally across the Stage:

for (var i = 0; i < 50; i++) {
ball._x += 10;
}

Conceptually, the loop statement has the right approach -- it
repetitively updates the position of ball by small
amounts, which should give the illusion of movement. However, in
practice, the ball doesn't move each time
the _x position of ball is
changed because the Stage isn't updated. Instead, we'd
see the ball suddenly jump 500 pixels to the
right -- 10 pixels for each of the 50 loop iterations -- after
the script completes.

To allow the Stage to update after each execution of the
ball._x+=10; statement, we can use a timeline loop like
this:

// CODE ON FRAME 1
ball._x += 10;
// CODE ON FRAME 2
gotoAndPlay(1);

Because Flash updates the Stage between any two frames, the
ball will appear to animate. But the timeline loop
completely monopolizes the timeline it's on. While it's
running, we can't play any normal content on that timeline. A
better approach is to put our timeline loop into an empty, two-frame
movie clip. We'll get the benefit of a Stage update between
loop iterations without freezing a timeline we may need for other
animation.

8.7.1. Creating an Empty-Clip Timeline Loop

The following steps show how to create
an empty-clip timeline loop:

Start a new Flash
movie.

Create a movie clip symbol named ball that
contains a circle shape.

On the main Stage, rename layer Layer 1 to
ball.

On the ball layer, place an instance of the
ball symbol.

Name the instance of the ball clip,
ball.

Select
Insert New Symbol to create a blank movie clip
symbol.

Name the clip symbol
process.

On frame 1 of the process clip, attach the
following code:

_root.ball._x += 10;

On frame 2 of the process clip, add the following code:

gotoAndPlay(1);

Return to the main movie timeline and create a layer called
scripts.

On the scripts layer, place an instance of the
process symbol.

Name the instance
processMoveBall.

Select Control Test Movie.

The processMoveBall instance will now move
ball without interfering with the playback of the
main timeline upon which ball resides.

Note that step 12 isn't mandatory, but it gives us more control
over our loop. By giving our timeline-loop instance a name, we can
stop and start our loop by starting and stopping the playback of the
instance, like this:

processMoveBall.play( );
processMoveBall.stop( );

Note that in this example processMoveBall and
ball must both exist on the main timeline for as
long as the loop is supposed to work. If we wanted to make the code
more portable, we could use a relative reference to our
ball clip in process:

_ parent.ball._x += 10;

And if we wanted to control our ball from any timeline, we'd
use an absolute reference to ball:

_root.ball._x += 10;

WARNING

Timeline loops can't loop on a single frame. That is, if we
place a gotoAndPlay(5) function on frame 5 of a
movie, the function will be ignored. The Player realizes that the
playhead is already on frame 5 and simply does nothing.

8.7.2. Flash 5 Clip Event Loops

Timeline loops
are
effective
but not
necessarily elegant. In Flash 5, we can use an event handler on a
movie clip to achieve the same results as a timeline loop but with
more flexibility (just try to follow along with this example, or see
Chapter 10, "Events and Event Handlers", for details on movie clip event
handlers).

When placed on a movie clip, an
enterFrame event handler causes a block of code to
execute every time a frame passes in a movie. We can use an
enterFrame event handler on a single-frame empty
clip to repetitively execute a block of code while allowing for a
Stage update between each repetition (just as a timeline loop does).
Follow these steps to try it out:

Follow steps 1 through 7 from the previous section.

On the main Stage, create a new layer called
scripts.

On the scripts layer, place an instance of the
process clip.

Select the process instance and attach the
following code:

onClipEvent(enterFrame) {
_root.ball._x += 10;
}

Select Control Test Movie.

The ball instance should animate across the Stage.

Clip event loops free us from nesting our code inside a movie clip
and don't require a two-frame loop, as timeline loops do. All
the action of a clip event loop happens in a single event handler.
However, the clip event example we just saw has a potential drawback:
there's no way to programmatically start or stop the loop once
it's started. The only way to stop the loop is to physically
remove the process instance from the timeline with
a blank keyframe.

To create an event loop that can be arbitrarily started and stopped,
we have to create an empty clip that contains
another empty clip that bears an event loop. We
can then dynamically attach and remove the whole package whenever we
want to start or stop our loop. A little convoluted, yes, but the
results are quite flexible. Once again, follow the steps to try it
out:

On frame 1 of the process clip, drag an instance
of eventLoop onto the Stage.

Select the eventLoop instance, and attach the
following code:

onClipEvent(enterFrame) {
_ parent._ parent.ball._x += 10;
}

Return to the main movie timeline and attach the following code to
frame 1:

attachMovie("processMoveBall", "processMoveBall", 5000);

Whenever you want to stop the event loop, issue the following
statement:

_root.processMoveBall.removeMovieClip( );

Select Control Test Movie.

Once again, the ball instance should animate
across the Stage, but this time we can start and stop it whenever we
like by using the attachMovie( ) and
removeMovieClip( ) functions shown in steps 9
and 10.

There are examples of regular and controllable clip event loops
available from the online Code Depot.

8.7.2.1. Keeping event loops portable

Both of
the clip event loops we just saw included
a line of code that updates the position of the
ball instance on the Stage. For example:

Although this approach works, it's sloppy. By attaching
meaningful code to our clip event, we've decentralized our code
base, dispersing logic and behavior throughout our movie. In order to
keep our code accessible during authoring and better structured for
reuse, from within event loops, we should only
call functions. So, instead of actually moving the
ball clip in our example, we should call a
function that moves the ball clip, like this:

onClipEvent(enterFrame) {
_ parent._ parent.moveBall( );
}

The user-defined function moveBall( ) would be
defined on the same timeline we attach the
processMoveBall clip to, like this:

If our application is simple, we may wish to forego our empty
event-loop clip altogether. In some cases, we can quite legitimately
attach an event loop directly to the clip being manipulated. In our
ball example, we could avoid the need for separate
empty clips by attaching the following code directly to the
ball instance:

onClipEvent(enterFrame) {
_x += 10;
}

This approach is ultraconvenient, but it doesn't scale very
easily, and like our first example, it suffers from the inability to
start and stop the loop.

8.7.3. Frame Rate's Effect on Timeline and Clip Event Loops

Because timeline
and clip event
loops iterate once per frame, their execution frequency is tied to
the frame rate of a movie. If we're moving an object around the
screen with a timeline or an event loop, an increase in frame rate
can mean an increase in the speed of our animation.

When we programmed the movement of the ball clip
in our earlier examples, we implicitly specified the velocity of the
ball in relation to the frame rate. Our code says, "With each
frame that passes, move ball ten pixels to the
right":

When timing scripted animations, it's tempting to calculate the
distance to move an item in relation to the movie's frame rate.
So, if a movie plays 20 frames per second, and we want an item to
move 100 pixels per second, we're tempted to set the velocity
of the object to 5 pixels per frame (5 pixels * 20 frames per second
= 100 pixels per second). There are two serious flaws in this
approach:

By relying on the frame rate to determine the speed of an item, we
make it painful to change the frame rate. If we change the frame
rate, we have to recalculate our speed and edit our code accordingly.

The Flash Player does not necessarily play movies back at the frame
rate set in the Flash authoring tool; it often plays them slower. If
the computer running the movie cannot render frames fast enough to
keep up with the designated frame rate, the movie slows down. This
slowdown can even vary depending on the system load; if other
programs are running or if Flash is performing some
processor-intensive task, the frame rate may drop for only a short
period and then resume its normal pace.

You can test this out yourself using the time-tracker tool available
at:

In some cases, an animation that plays back at slightly different
speeds could be deemed acceptable. But when visual accuracy matters
or when we're concerned with the responsiveness of an action
game, it's much more appropriate to calculate the distance to
move an object relative to elapsed time instead of the frame rate.
Example 8-5 shows a quick-and-dirty sample of
time-based animation (i.e., the ball speed is
independent of the frame rate). The new movie would have three frames
and two layers, one layer with the ball instance
and the other with our scripts.

Note that our time-based movement might appear jerky if the frame
rate suddenly changes. We could smooth things out by using an
elapsed-time measurement that averages the time between a series of
frames instead of just two.