Hammers Within Hammers: Dynamic Hierarchical Structures in Flash
March 6, 2001
by Darrel Plant
The Earth Was Without Form, and Void
For Director developers who are used to the Score-sprite-behavior structure of their favorite program, the transition to Flash can be a little confusing. Things just don't seem to work the same way, Stuff goes in the wrong place. And a whole bag of tricks and workarounds is out the door.
That's not to say that it's a bad thing. Flash movies are constructed in a way that may make the it seem like the Bizarro Director. But there are definite parallels between the programs that you can use to your advantage, as long as you keep the differences in mind.
To Divide the Day From Night
One major difference between the two programs is in the sequencing of time. Both applications use a grid-based system divided into frames on the columns of the horizontal axis. Rows on the vertical axis of the grid control the stacking order of objects (in Director, the foremost object is in the bottom row of the grid; in Flash, the top row of the grid is in front).
Layers vs. sprite channels
A row in Director (better known as a sprite channel) can have only one cast member object occupying it at a time. Yes, more than one cast member can be included in a film loop, but the loop counts as a single cast member when placed in the Score. A Flash timeline layer can include theoretically hundreds of groups, symbols (the rough equivalent of cast members), scripts, and text objects, as well as an artwork level underneath the objects.
Multiple Timelines
The biggest variance with Director is in the fact that Flash has multiple timelines. Each movie clip, graphic, and button symbol has a completely unique sequencing system, including its own internal layering. All of these timelines are simultaneously stepping forward as the movie plays.
Bring Forth Abundantly the Moving Creature
The Flash equivalent to Director's casts are its libraries. A Flash library is a repository of objects which can be reused throughout the movie without the asset data being duplicated in the file, When a library object, or symbol, is used in a timeline, a reference to the symbol is stored in the data associated with a keyframe, indicating scaling, position, etc.
Graphic Symbols
Two types of symbols are relatively simple. Graphic symbols will show the same thing every time the movie is played, depending on which frame is displayed. They can only be modified by changes applied to the instance of the symbol in the timeline, or changes in their own timelines; they can't be modified with ActionScript. In many ways, they're similar to Director's film loops.
Button Symbols
Button symbols are interactive, but in a limited manner. They also have a timeline, but it only has the four frames that control the Up, Over, Down, and Hit states of the button. Scripts can be attached to instances of button symbols to react to the cursor and mouse, but a button can't be controlled by ActionScript.
Movie Clip Symbols
The movie clip is where Flash really comes into its own. A movie clip is like a little Flash movie unto itself. It has its own timeline. It can execute ActionScripts within its timeline. Behavior-like scripts can be attached to instances of a movie clip to react to cursor, mouse, and key events. Most important of all, any script in the movie -- whether it's in the primary (or root) timeline, a movie clip timeline, a button, etc. -- can affect the movie clip instance.
The Tree of the Knowledge of Good and Evil
The structure of a Director movie's sprites is a flat hierarchy. All of the sprites are on the same level, in that they don't have any connection with each other. Each sprite is equal to any other sprite.
In Flash, though, a movie clip's timeline can actually contain other movie clips, creating a hierarchy more like a tree. Instead of referring to an object simply by its sprite number, you use an address more like a URL or object references in JavaScript, C++, or other object-oriented languages. The addresses can be relative to the object a script is attached to, or they can be absolute references based on the movie's root timeline.
This allows you to do some things that are just impossible in Director. If you want to build your own dialog boxes in Director, you've got to allocate sprite channels for the pieces of the artwork, the text blocks, buttons, etc., keep those sprite channels clear throughout the Score, and move all of the elements simultaneously when the user moves the window.
In Flash, you can create a movie clip that encapsulates the window elements. The elements (borders, text, buttons, etc.) are relative to the window movie clip, and all you need to do when moving the window is to set the coordinates of the window instance -- all of the elements move along with it. That's why I'm using Flash movies for dialog boxes in my Director applications whenever possible.
Then Your Eyes Shall Be Opened
Let's take a look at an example of a Flash movie that uses a multilevel object structure. This movie has three levels of spinning hammers (originally they were going to be wheels, which would have made the title of the article more to the point, but they obscured the motion). Each hammer spins around a point on its own handle at its own individual speed. The top-level hammer has several inner hammers that rotate around the top-level hammer's center point, in addition to revolving on their own axis. And each inner hammer has several subhammers. It's sort of like the sun, planets, and moons, if they were giant hammers. (If the random configuration of the hammers makes it difficult to see what's happening, just click on the movie and they'll move to new positions.)
This movie uses a couple of Flash tricks that don't have Director equivalents. First is the aforementioned method of encapsulating movie clips within movie clips. The second is that this encapsulation is done at run-time, by modifying the contents of an instance.
In Director, each instance/sprite of a cast member references the original cast member data. You can have sprites with different attributes, but there's no way to substantially change the underlying cast member data without having that change reflected in other sprites that reference the same cast member.
Flash is different, though. Once a symbol has been instantiated into a timeline, it becomes an entity unto itself, and it can be modified in significant ways without altering other objects referencing the same symbol data.
As you've probably guessed, all of the hammers are derived from the same symbol. Let's take a look at the simple portions of this movie first, by examining the symbols.
Spinning Hammer
The hammer movie clip is just a single-frame movie with a couple of rectangles in it. The hammer-spinning movie clip has two layers and two frames. The only frame that's ever seen is the first frame, containing an instance of the hammer movie clip. The script in frame 2 of the movie clip's timeline executes, but the frame itself is never seen:
_rotation -= rotspeed;
gotoAndPlay (1);
The reason the second frame of the movie clip is never seen is that frame scripts in Flash are executed during what is equivalent to the Director prepareFrame event, before any updating of the objects in the frame. The first action uses the decrement assignment operator (-=), and is an alternative to _rotation = _rotation - rotspeed.
To use the symbol the way we're going to, the Symbol Linkage needs to be set by selecting the symbol in the Library window, then choosing Linkage from the window's menu. If a symbol isn't used in Flash, it doesn't get exported. Creating a symbol linkage forces the exporter to include the symbol, and assign it an Identifier that can be accessed by ActionScript.
Restart
The restart movie clip is only used to detect a mouse click and reinitialize the positions of the hammers. Its timeline is empty (no artwork) and the script on the only instance (in frame 1 of the root timeline) is short:
onClipEvent (mouseUp) {
_root.hammer1.removeMovieClip();
_root.hammer ()
}
The onClipEvent (mouseUp) looks familiar to anyone who's written a Director mouseUp handler. There's a bit of a difference. Flash clip events respond to any mouseUp event, not just those executed over the instance. The empty clip responds just as if I'd put a button into the movie covering the whole Stage. The two actions don't mean much at this point, but the first clears all of the hammers from the Stage by removing the top-level instance, and the second calls the function that creates a new set of randomly-placed hammers.
Genesis
The first frame of the root timeline contains nothing but a script and an instance of the restart movie clip.
Again, Flash works in a similar fashion to Director, but not exactly the same. In this case, the function hammer is defined within the frame script, as you'd expect in a Director behavior. Unlike Director, it becomes a global function within the root timeline, and could be called from anywhere in the movie, not just frame 1.
The stop action stops only the root timeline. Any other timelines (i.e. movie clips) in the movie will continue to play (a source of much merriment to folks who have been trying to pause Flash movies from within Director).
The hammer function is where the hierarchy of hammers is created. The first four lines establish the top-level movie clip. The line with the attachMovie method inserts the symbol with the linkage identifier hammer into the root timeline as an instance named hammer1. The instance (_root.hammer1) is then assigned to a position at the middle of the Stage, and given a rotation speed (stored in the rotspeed variable) ranging from -5 to 5 (the random (n) function in Flash returns an integer in the range 0 to n-1). The rotspeed variable is local to the timeline of the instance.
hammer ();
stop ();
function hammer () {
_root.attachMovie( "hammer", "hammer1", 1 );
_root.hammer1._x = 275;
_root.hammer1._y = 200;
_root.hammer1.rotspeed = random (11) - 5;
for (i = 1; i < 6; i++) {
_root.hammer1.attachMovie ("hammer", "hammer" + i, i);
innerhammer = _root.hammer1["hammer" + i];
innerhammer._x = random (350) - 175;
innerhammer._y = random (350) - 175;
innerhammer._xscale = 30;
innerhammer._yscale = 30;
innerhammer.rotspeed = random (21) - 10;
for (j = 1; j < 4; j++) {
innerhammer.attachMovie ("hammer", "hammer" + j, j);
subhammer = innerhammer["hammer" + j];
subhammer._x = random (350) - 175;
subhammer._y = random (350) - 175;
subhammer._xscale = 30;
subhammer._yscale = 30;
subhammer.rotspeed = random (31) + 15;
}
}
}
The two for loops are very similar in structure. The outer loop attaches five innerhammer instances of the hammer to the _root.hammer1 instance, giving them the names hammer1 to hammer5. Because they're attached to the top-level instance hammer1, the full path to an innerhammer is something like _root.hammer1.hammer3 (for the third instance attached in the loop).
Each innerhammer instance is given a random position within the top-level instance. Its coordinates are relative to the center of the top-level instance, not the Stage. The innerhammer instance is scaled and given its own rotation speed. Note that as in Lingo, you can create an object reference to a calculated object reference by using it within brackets instead of in dot notation: _root.hammer1.hammer3 and _root.hammer1["hammer" +"3"] are interchangeable. The scale of the hammer is set to 30% of its default size.
Three subhammer instances are created within each of the innerhammer instances, and the same basic code is used to position and scale each of them. These values are relative to the coordinate system of the innerhammer instances. The scaling of the subhammers is 9% of the original size (30% of 30%).
Once the hierarchy is set up, the only scripts executing are those in the spinning hammers, updating _rotation properties. Since the position of a hammer is dependent on its parent instance's coordinate system, the top-level hammer rotates the whole system, and each hammer's position is dependent on the combined rotation of its parent instance and any grandparents it may have.
Exodus
This is a fairly simple example of a hierarchical structure that would be -- frankly -- more difficult to program in Director. The updating mechanism alone in Director would require far more work to keep the rotations straight. Not that it can't be done and done well, but it would involve a bit more work than I put into the Flash version. While this example is not inherently useful in and of itself, I think you can see that there are some definite possibilities for complex systems, particularly when you know what can be done well in Flash and combine its power with the things that Director does well.
Hopefully, this has been enlightening for those of you coming from the world of Director who are getting into Flash, and possibly even for those Flash folks who have been trying to get a grasp on some aspects of nested timelines. Stay tuned for more Using Flash articles here at Director Online!
A sample Flash 5 movie is available for download in Mac or Windows format.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.