Articles Archive
Articles Search
Director Wiki
 

I Miss Behaviors: Code Encapsulation in Flash

March 26, 2001
by Darrel Plant

In the first episode of Using Flash we looked at how to embed graphic objects (movie clips) within other objects to create complex hierarchical structures. The advantage on that technique definitely goes to Flash over Director. This week, we'll look at an area of multimedia programming that I'd have to score on the Director side of the fight card: the ugly way Flash deals with instance-specific code and properties.

In the interest of full disclosure, I should mention that I worked on writing the behavior libraries for Director 7 and 8, along with James Newton and some Macromedia employees. I did a bunch of the animation behaviors, the sprite transitions using imaging Lingo, and the sound behaviors, among others. I really like behaviors. Take the following with a grain of salt, if you wish, just remember that I'm right.

"Smart" Clips?

Behaviors were introduced to Director in version 6 to provide a method of drag-and-drop programming. Have a sprite that you want to move across the screen? Drop an animation behavior on it, set parameters for movement, and voilá, the sprite moves. In many cases, you can use the same behavior on any number of sprites, all at the same time or scattered throughout your movie.

A behavior script cast member contains the code, and the Score data for a specific sprite stores any specific parameter values that the developer of the behavior determines can be set at authoring time.

A generalized behavior script can use parameter values that are specific to an individual sprite to create a result that's different for each instance of the behavior.

Flash 5 introduced the Smart Clip, which at first glance gives much the same types of capabilities to movie clips in Flash as behaviors give to sprites in Director. You can set individualized parameter values for each instance of a Smart Clip (it's essentially a movie clip with externally-accessible parameters defined).

The problem with this approach is that the parameters are bound to specific movie clip symbols in the library. Once the number of parameters for a movie clip have been defined, only the names and values can be changed for each instance. In Director, the parameters are bound to instances of the behavior, not to a sprite itself. What's the difference?

The Smart Clip interface is designed to accomodate only one type of interaction per movie clip. Only one set of parameters can be accessed by the Clip Parameters dialog. Where Smart Clips do shine, though, is in their ability to create customized user interfaces for their parameter dialogs, right in Flash itrself. No need for the freakishly long property lists of the MUI Xtra or the getPropertyDescriptionList handler. You can even make multi-stage parameter dialogs that modify themselves based on the user's decision path.

The downside is that only one set of parameters and custom UI can be defined for each movie clip symbol. While the UIs can be reused for other movie clips, the properties must also be defined for the Smart Clip, and some amount of code must be duplicated. Properties and code don't travel with the UI, so they must be redefined for each Smart Clip that uses the interface.

Where's the Script, Kenneth?

One item that can trip up the unwary Director developer moving into Flash is the fact that Flash scripts are attached to either object instances or frames in an object timeline. There's no equivalent to Director, where most scripts exist as cast members, independent of other objects.

When a behavior script is attached to a Director sprite, the sprite's information in the Score is updated with a reference to the script, along with the values of any properties that can be set for the behavior. The script itself exists only once in the file, and many different sprites can use even a long, complex behavior script without affecting the size of the movie significantly.

Flash, on the other hand, attaches scripts to specific timelines or instances, and where you place a script can make a big difference in the size of the final file, not to mention in the development of a movie.

For an example, here's a bouncing ball movie. The ball bounces at a randomly-determined speed, and reverses direction if you click on it.:

The movie clip instance has a script attached to it that's a modest 649 characters:

onClipEvent (load) {

    dx = random (6) + 5;
    dy = random (6) + 5;
    if (random (2)) {dx *= -1}
    if (random (2)) {dy *= -1}

}

onClipEvent (enterFrame) {

    xnew = _x + dx;
    ynew = _y + dy;
    
    // test for top
    if (ynew < 15) {
        dy *= -1;
        ynew = 15 + (15 - ynew);
    }
    
    // test for left
    if (xnew < 15) {
        dx *= -1;
        xnew = 15 + (15 - xnew);
    }
    
    // test for bottom
    if (ynew > 285) {
        dy *= -1;
        ynew = 285 + (285 - ynew);
    }
    
    // test for left
    if (xnew > 385) {
        dx *= -1;
        xnew = 385 + (385 - xnew);
    }
    
    _x = xnew;
    _y = ynew;

}

onClipEvent (mouseDown) {

    if (hitTest (_root._xmouse, _root._ymouse, true)) {
        dx *= -1;
        dy *= -1;
    }

}

The load event initializes the speed in the x and y axes. Every enterFrame, the movie clip instance is moved to a new location, with tests made to determine if it's hide one of the sides. If it has, the direction is modified, then the instance's location values are actually modified. When the movie clip receives a mouseDown event, a test is made to determine whether the cursor is over the instance, and direction is reversed if true. The resulting SWF file is a only 749 bytes in size. (An equivalent Director movie with the same graphic placed as a Flash cast member was just about 6K.)

What if you wanted to use the same script on a lot of graphics at once? The simple way would be to copy the movie clip instance a bunch of times:

This movie has 53 copies of the bouncing ball. The script on each ball is identical, but while the ball itself is a symbol instance drawing its data from a single source, the script attached to each instance is a separate entity, with a duplicate of the script included in the file for each instance. Another downside of this approach is that any change to the basic script would then need to be copied to the other 52 copies of the script, although an external ActionScript file and the #include operator will fix that problem pretty fast.The SWF for this movie is almost 33K. By contrast, if the ball sprite in the Director movie is duplicated the same number of times, the Shockwave movie is just 7,095 bytes.

Why? Because behavior scripts in Director are separate objects (as are movie and parent scripts). Only one copy of the script is saved in the movie. How can we achieve the same type of size savings in Flash?

Code Encapsulation

One way to divorce the code from the individual instances would be to put the code into a frame of the root timeline. Using a gotoAndPlay action to repeat the same frame, then use a for loop to update the positions of each instance. This technique does cut the size of the movie, but you lose the ability to access the types of events sent to the onClipEvent handler, like load, enterFrame, and mouseUp.

To get around that, a single empty movie clip instance can be used to respond to mouse and key events, which are sent to all instances (unlike Director, which sends mouse events only to the behaviors attached to sprites the cursor intersects). The enterFrame event can also be used on an empty movie clip, and again, a for loop can update the positions of the balls.

This doesn't solve our problem for the load event, which is used to initialize the ball's movement parameters. The movement values for each ball can be stored in an array initialized in the empty movie clip's load event; this gives you the flexibility of the movie clip events, and the decreased size of the SWF file.

What it doesn't give you is much elegance. Rather than each ball operating on its own in an object-oriented manner, you now have a centralized controller for all of the balls. Instead of being able to add a ball by simply duplicating an existing object, counters need to be updated in your script to track the number of balls in existence.

There is a way to have our cake and eat it too. Placing an empty movie clip inside of the ball movie clip and attaching our previous script with some minor modifications to it gives us a single object which can be duplicated ad nauseum and which contains all of the code to move the ball and respond to clip events.

Because the script is encapsulated inside the movie clip used for the ball, the minimum amount of data is saved for each instance. The code itself exists only within the library item used for each of the balls. This version of the movie functions identically to the previous 33K example and is a whopping 1,431 bytes (the single-ball version is 817 bytes).

The changes needed to the code are simple. Since the movie clip that contains the code is attached to the timeline of the movie clip it affects, the script just has to refer to its parent for everything, which is easily done using the with action:

onClipEvent (load) {

     with (_parent) {
       dx = random (6) + 5;
        dy = random (6) + 5;
        if (random (2)) {dx *= -1}
        if (random (2)) {dy *= -1}
    }

}

onClipEvent (enterFrame) {

    with (_parent) {

        xnew = _x + dx;
        ynew = _y + dy;
    
        // test for top
        if (ynew < 15) {
            dy *= -1;
            ynew = 15 + (15 - ynew);
        }
    
        // test for left
        if (xnew < 15) {
            dx *= -1;
            xnew = 15 + (15 - xnew);
        }
    
        // test for bottom
        if (ynew > 285) {
            dy *= -1;
            ynew = 285 + (285 - ynew);
        }
    
        // test for left
        if (xnew > 385) {
            dx *= -1;
            xnew = 385 + (385 - xnew);
        }
    
        _x = xnew;
        _y = ynew;
    }

}

onClipEvent (mouseDown) {

    with (_parent) {

        if (hitTest (_root._xmouse, _root._ymouse, true)) {
            dx *= -1;
            dy *= -1;
        }
    }

}

A couple of minor changes go a long way in trimming this movie down to size. When virtually the same script is attached to duplicated objects rather than embedded in them, in this case the resulting SWF is nearly 2300% of the other's size. While that number represents just under 30K for this example, the mind boggles at the quantity of wasted bytes flying around on the Web due to long ActionScripts attached willy-nilly to multiple instances. In a large movie with more complex scripts (say, a menu system), it begins to add up. An added benefit is that there's only one place to edit the code, now.

Portability & Multiple Behaviors

Regrettably, while this technique does solve some problems, it doesn't address the issue of code portability that is one of the hallmarks of Director's behaviors. You can't just use any piece of code any where you want in a Flash movie without making a duplicate of it. The Flash scripting interface isn't quite there yet. Yes, you can create custom objects in ActionScript, which is one approach to code portability that Director's had since about 1993, but the type of drag-and-drop coding behaviors offer is an approach to reusability that will hopefully come to Flash one day.

The other feature missing from Flash is the ability to incorporate multiple scripts into a movie clip while avoiding duplication. It's actually possible to do something like this in an ActionScript:

onClipEvent (load) {trace (1)};
onClipEvent (load) {trace (2)};
onClipEvent (load) {trace (3)};

The result in the Output window will be the number 1, 2, and 3 on separate lines. So you can actually copy and paste scripts from one object to another or use #include to add external scripts that contain multiple handlers for the same events. The trouble here (apart from code duplication in the SWF) is that you need to be very careful that each added script doesn't step on another's persistent variables (not a problem with behaviors). You're still left with the problem that the parameter interface for a Smart Clip isn't geared toward a dynamic system where the function of a movie clip can change from instance to instance..

Why? You've got to wonder sometimes whether the left hand's talking to the right hand at Macromedia. After all, behaviors have been used in multimedia authoring tools since mTropolis was released in the mid-'90s. The Director engineers weren't above appropriating the concept. It's difficult to know why the Flash team didn't pick up on it. Almost as difficult as why there wasn't any cooperation to make Flash 5's XML Sockets talk to the Shockwave Multiuser Server, which was the only server product in Macromedia's stable for the four years up until the recent Allaire merger. But that's another story.

Source files for four Flash 5 movies are included in downloads for Macs and Windows. The archive includes single ball and multiple ball versions of scripts attached to movie clips and scripts embedded in movie clips, along with a Director source and Shockwave file for comparison.

Darrel Plant is Technical Editor of Director Online. He is the Publisher at Moshofsky/Plant Creative Services in Portland, Oregon, and the author of or contributor to a number of books on Macromedia Director and Flash, including Special Edition Using Flash 5,, Flash 5 Bible, and Director 8.5 Studio..

Copyright 1997-2024, Director Online. Article content copyright by respective authors.