Linked Director Movies
October 6, 1999
by Rob Romanek
Okay, once again it's time to plan another Director project. You're looking at the storyboards. Chanting the programming mantra... compartmentalize, reuse, compartmentalize, reuse. You know, no matter what the size, a well-planned project will make your life much easier when the deadlines approach and bugs start cropping up.
Director offers many ways for you to become one with your mantra. Behaviours, filmloops, flash movies and movie in a window - the tools are there to be used. It's a question of deciding on the right mix..
The planets start to align, the project is coming into focus. You are at peace with yourself, engineering yet another successful project. But wait...you encounter an unforeseen obstacle. The storyboard calls for series of identical yet unique objects! Six analog clocks keeping track of individual time zones all the same style but uniquely colored, all editable on the fly, antialiasing with whatever background they are placed upon AND this sequence has to be shockwave compatible. What have you done to deserve this anguish? What could have so angered the gods of multimedia?
You take a cleansing breath and approach the problem systematically.
First a clock needs to be built
Looking at the storyboard, a vector-based solution seems best. This will help keep the file size small and allow the moving hands to antialias against the clock face.
Immediately a Flash movie jumps to mind. However some limitations occur to you, primarily support for the kind of coding required to run the motion of clock hands as well as allowing "on-the-fly" editing of the clock. You eliminate it as a possible solution. (Authors Note: My knowledge of Flash's coding tool set is limited and I have just barely had a peek at Flash 4 and its new expressions. So, there may well be a way to tackle this problem efficiently in Flash but I'll leave that for some Flash guru out there to figure out... and write about.)
Building the clock using Director's vector shapes is the next choice. This works well as an option. The clock is built from pieces, so colours can easily be customized on the fly. A vector member's colour properties can be changed or, by leaving the vector as white, you can colour the vector sprite on the stage. You opt for the latter approach since it allows one cast member to be used in many clocks.
You construct the clock behaviour cleverly. It's designed not only to track time and change it but to create clock objects as well. This last step is important in juggling any number of clocks and hands and time zones on the stage. Just for fun you throw in a rate setting so the clocks can be run at faster than real time in order to view changes more quickly.
Now, drop the behavior on any sprite that is a clock hand, choose a hand type or if the sprite contains a text member set it up as your time zone. Make sure all the pieces are associated with a clock object and you're off and running.
Proudly put your creation through its paces. Time, zone and face colour can all be changed on the fly.
Suddenly it dawns on you. Arrgh, the whole thing takes up 21 channels. Six clocks times 21 channels is 126 channels. Director does have 1000 channels to work with but who wants to manage 126 channels just to manipulate six clocks. What if the client decides the clocks should be moveable or stackable in different orders, or they want to add six more. There will be loc x, y and z's to worry about for 21 different channels every time one clock needs to be changed or added.
What is really needed is a single super-clock object. Not just the code that runs the clock, but code and graphics all wrapped up into one neat package. This super-clock object will be completely interactive yet occupy no more than a single sprite channel in the score.
Options, options, options
Will putting all 21 channels into a filmloop help solve this problem?
Well, the clock will now occupy only one channel and the antialiasing of the clock against any background will be retained. But, how will the hand rotation and time setting be controlled? The behaviour you built works by modifying a sprite's rotation angle and there isn't any way to access the sprites that have been trapped within a filmloop. If the hands are left out of the filmloop then there are only 3 channels left to manage, better than 21 but still not as good as one. Not to mention that customizing the clock becomes more difficult because it can't be done on the sprite level. Instead you would have to make a series of filmloops, each with unique cast members, then the cast members can be modified to create the custom effects. This could start to get messy again.
How about using a movie in a window (MIAW)?
MIAW is the first choice for many developers when building modular, compartmentalized, projects. MIAW's offer many of the requirements needed here. All the clock pieces remain in individual channels and can be controlled via standard coding methods. A clock MIAW would contain all code and graphics in a single package and not take up any channel space since it exists in RAM memory. Also, Director offers a well- supported communication method to allow custom changes to individual instances of the clock MIAW.
However, looking at the design spec again, you realize the MIAW option has two big shortcomings. First, the MIAW will not support the need to have the clock antialias against the variety of backgrounds that exist on the stage. Suddenly, you remember reading an article on DOUG about the undocumented irregular MIAW feature in D7.0.2 but just as quickly your hopes are dashed as you remember that the feature uses a 1-bit mask which eliminates any antialias possibility. Not to mention you can't pull off any sprite blending with the main stage like you could do using a filmloop. And, of course, there is the other small detail of having this work in the Shockwave format, and Shockwave does not support MIAWs.
Digging deep into your bag of Director tricks you take a good hard look at Director Movies within Director Movies. This technique comes in two flavours:
1) Imported director movies - These are essentially filmloops. An entire movie is imported into the current movie file, with the imported movie's media being placed within the current movie's cast library while its score is converted to a filmloop.
2) Linked Director Movies (LDM) - In this case, the movie remains external to the current movie and the cast member serves as a pointer to the file location of the linked movie. This is much like importing a Quicktime movie.
Well, the first approach is really just a filmloop, and you've already ruled that out, but, the second approach holds promise. Especially when while examining the linked movies properties you notice a toggle box that allows you to enable the linked movie's scripts.
(Author's note: While this feature is offered in D6, it is quite buggy. Yet another reason to upgrade to D7 if you haven't already done so.)
Excitedly you get to work
With the clock movie already built, you open a new movie and import the clock movie with 'link to external file' selected. The next step is to open the linked director movie (LDM) member properties and enable the scripts. Now drop the LDM onto the stage, set the frame to loop and run your movie. There you have it. Your clock is running like before - the code works great, full transparency features are retained and sprites are changeable on the fly - but, now instead of 21 channels, it takes up only one. (Well, this is almost true, mouseEnter and mouseLeave handlers don't exactly function properly in LDM's and a work around has to be created but to keep our story moving we'll deal with that later. See Behind-the-Scenes Issues below.)
Life is not yet golden, however. The messaging system the movie uses to change clock time is being run from within the LDM. What you really need to be able to do is control the time from the stage that is holding the LDM. This is especially true if once all six clocks are up and running they need to be synchronized. Well, you're in luck. You remember someone mentioning in Direct-L a while back a command you hadn't heard of before: "tell sprite". Sure enough, it does the trick. It functions just like the tell command for communicating with windows except here it talks to your LDM. Removing the messaging box from the clock movie and placing it on the stage, you use the tell sprite syntax to change the clock's time. So instead of runnning
setClockTime whichClock, "hh:mm"
the code becomes
tell sprite(theSpriteContainingTheClockMovie) to ¬ setClockTime whichClock, "hh:mm"
or if you set up a movie handler within the LDM to redirect calls to your one clock within the super-clock LDM the message can simply be
tell sprite(theSpriteContainingTheClockMovie) to ¬ setClockTime "hh:mm"
Note: Now that you're using LDMs, there is need for only one clock object within the clock movie. As a result the clock behaviours object managing feature becomes redundant, but you'll leave it in for now because it works.
With one clock up and running, it's time to start adding more. As it turns out, this is not as simple as dragging the one clock LDM in the cast library onto the stage many times. While this will create many copies of the clock on the stage, the copies won't really be independent instances of the clock LDM. Instead, a separate cast member must be created for each independent instance of the clock LDM that is required. The following multiple clock experiment shows how the two clocks on stage that originate from the same LDM are not true instances.
At first glance they may appear to be independent since each of these clocks can have their face colour changed. This is because the colourization is a sprite level characteristic, one that each clock manages independently. However, since they originate from the same cast member, the object that controls their time is shared between them along with the text member that contains the clock name. As a result changing the time or name of one clock changes the time or name of the others as well. So these really aren't independent instances.
The third clock in the experiment comes from a separate LDM cast member (but it still points to the same external file as the member from which the first two clocks originate). As such, it is a separate instance of the clock movie and runs its time independently.
You're almost finished. When the clock behaviour was first created, it had built into it the functionality of starting each clock object at its own time. Now, since you're creating instances of the clock movie, each instance starts with its time set to the value contained within the clock movie's clock behaviour (the internal clock object). So how can you get each clock to start at a different time based upon its instantiation on the stage? By using a behavior again, but this time it is applied to the sprite containing the clock LDM (at the level of the stage).
Fortunately, in D7, behaviours that don't contain any mouse events can be applied to the sprites that contain the LDM and have no effect on the LDM's scripts. (This is not the case in D6 where applying any behaviour disabled the LDM's scripts completely.) The existence of mouse events in the stage level behaviours don't actually affect the LDM's scripts: they trap the mouse event, not allowing the event to be passed to the LDM and acted upon accordingly. By dropping a behaviour onto the LDM containing sprite, specific instructions can be automatically sent to the LDM. However, make sure the LDM has been instantiated prior to sending any of these instructions, or the instructions are not registered. Because of this requirement the beginsprite event is not a good place to pass instructions. The beginsprite event takes place before the frame has been drawn and the LDM is not instantiated until it has been drawn on stage. The following diagram shows the order of events that occur in the stage movie and the LDM.
By the time the stage is executing the enterframe handler the LDM has been instantiated. By putting a flag in the behaviour, specific commands can be used to initiate the LDM.
property pFlag on beginSprite me pFlag=0 end on enterFrame me if pFlag = 0 then pFlag=1 tell sprite(me.spriteNum) initializationHandler end tell end if end
In this case the initializationHandler sets the clock time and time zone.
Since your initializationHandler is calling to the internal clock object within the LDM it is important to make sure that the LDM is on a frame where the internal clock object exists. Otherwise there will be no object to receive the setClock command. Ensuring the LDM starts on the right frame is easily managed by using the following startMovie handler within the LDM.
on startMovie go frame "clock" end
You're Done!
Use the message field to send specific command to the various clocks. To get you started follow the prebuilt code samples by selecting them then clicking on do it. Keep your eye on the top middle clock and you'll see some neat things happening. The clocks reside in channels 3 to 8
There you are, the proud owner of a super clock object. You'll be able to reuse this self- contained little beauty in all kinds of projects. And since all the instances in your main movie will point to the same external module, any changes to your code or graphics can be made in one location and will apply to all the instances.
What's most exciting about LDMs is their support of alpha channels and transparency relative to the stage and each other. Couple this with the fact that they can be incorporated into shockwave movies and you've just added an extremely powerful tool to your Director utility belt.
As always there are pitfalls and problems that can and will crop up. I've outlined some of the quirks in the following section but testing early and often is another mantra that needs to be chanted while working with LDMs. And since 'tell sprite' is not a feature that is officially supported by Macromedia it could change at any time. Instead let's hope that Macromedia is expanding the LDM feature set. Great enhancements would include features that mimic MIAWs - stuff like an global LDM list, to facilitate communication between LDMs, open, close and forget commands... Maybe some of these items will find their way into D8 but if your finding it hard to wait, keep your eyes open for a look at more advanced LDM communication techniques, including a LDM manager, to appear in the DOUG archives in the near future.
Behind-the-Scenes Issues
1) A big problem that wasn't addressed in the above scenario is a bug when using mouseEnter/Within/Leave handlers in LDMs. These events will be correctly registered by a sprite when they occur but any code occurring during these events will address the stage's cast libraries and not the LDM's. As a result if you swap members during one of these events with syntax such as:
on mouseEnter me sprite(me.spriteNum).member = member ¬ "rolloverState" end
Director looks in the stage's cast library for member "rolloverState" and if it doesn't exist, the result is a script error. This memory space confusion does not occur during other events such as mouseDown/Up, enter/exitFrame etc. To overcome this mouseEnter/Within/Leave problem, use a flag to register that the event has occurred and then check the flag during the exitFrame event and run the appropriate code.
property pEnter on mouseEnter me pEnter=1 end on exitFrame me if pEnter then pEnter=0 - - resets the property mEnter - - runs handler end if end on mEnter me sprite(me.spriteNum).member = member ¬ "rolloverState" end
2) The clock behaviour used in the LDM relies on the actorList in combination with the stepFrame command to advance the hands of the clock. While an LDM does keep its actorList separate from the stage's and other LDMs, the stepFrame handler is not called within LDMs. To compensate for this, every LDM which employs the stepFrame handler requires the following code in its frame scripts.
on enterFrame call(#stepFrame, the actorList) end
3) Why was a messaging system used to change the time zones for each clock rather than set up the time zone as editable text? Because LDMs don't support editable text. If a text member defaults to the editable state then when it appears on stage a fatal error will occur. Unfortunately simplying sending the new text via a messaging handler to the LDM will not result in an update on screen. The on screen text member has to be forceably redrawn by having the sprite associated with the text member point itself to another member, have the screen redraw and then repoint to the text member.
sprite(theSprite).memberNum=-1 updateStage sprite(theSprite).member=the text member
This refreshing issue holds true for any on-the-fly member modification. If a vectorshape is modified on the fly, by changing the vertexList, then using the above method will result in a flashing image as the memberNum Ð1 sets the sprite pointing to a 'blank' member. To get around this it would be best to create a buffer member which holds the member modification and refresh the stage by pointing to the buffer member then back again.
4) Speaking of flashing. The enterFrame-flagging trick used to set the starting parameters of the LDM is not the best way to get this done. That's because the screen gets a chance to draw the clock in its default state before setting the new parameters the jump between states is visible. Changeable parameters should really be established during the startMovie event. Then behaviours can consult these parameters before the sprites get drawn on screen. This level of communication, although beyond the scope of this article, can be accomplished and works to seamless draw the initial state of the LDM on stage.
5) System events such as mouseDown/Up will not trigger mouseDown/Up event handlers at the frame script or movie script level. They have to be specifically called in order to run.
6) An LDM will ignore go and play commands which have it jump to another movie.
7) Like MIAWs, LDMs can communicate with the stage by using the 'tell stage' syntax.
8) Quicktime movies will not play within an LDM. The solution is to have the Quicktime movie exist at the stage level and have the LDM communicate with it by using the 'tell stage' command to address the sprite containing the Quicktime movie.
9) Linked movies share the global space with the stage and other movies. Globals therefore serve as a useful tool for communicating between movies.
10) LDMs are very robust. Multiple LDMs can run safely at one time. They can even be nested within each other! Essentially the same performance issues hold true with LDMs as with filmloops. The more complicated the graphics, inks and sprite layering, the slower the performance.
Special thanks goes out to James Newton and his article on filmloops. It's a great resource and got the old creative juices flowing that started me on this journey. And thanks to Andrew Sinning for his invaluable casual mention of 'tell sprite' on DIRECT-L. It vastly simplified my original LDM communications and management system. I don't think anyone would have wanted to use LDMs or read an article that required the kind of hoops I was jumping through back then.
A sample movie is available for download in Mac or PC format
Copyright 1997-2024, Director Online. Article content copyright by respective authors.