Interactive filmLoops
July 2, 1998
by James Newton
Introduction
A filmLoop is a neat way of creating a looping animation. But it can be much more than that. With a little Lingo, and the advice in this article, you can turn a filmLoop into a self-contained interactive element. I've included a Shockwave movie with an interactive filmLoop.
The movie uses only one sprite.
If you don't believe me, locate the .dcr in your browser cache, create a new movie in Director's authoring environment, and open the .dcr as a MIAW. A button will appear that lets you transfer the graphic cast members and the score to your empty movie. Since you won't be able to recuperate the scripts, you'll just have to read this article to find out how the Lingo was done. Here's the outline of my article:
- A quick review of how filmLoops are built and edited in the Score Window
- How to create a filmLoop through Lingo using score generation. You'll discover how to build filmLoops seamlessly on-the-fly, even in projector.
- How to modify a filmLoop dynamically, by altering certain properties of the constituent cast members.
- Workarounds for incorporating editable text into a filmLoop.
- Shockwave and filmLoops: things you can't do in Shockwave... and some extraordinary things that you can.
All through the article you'll find scripts, tips and practical exercises to give you hands-on experience.
What you may already know
What is a filmLoop?A filmLoop is a template. It is a cast member object which simply contains pointers to other cast members, plus information concerning the way they appear on the stage. This means that a filmLoop cast member can be very much smaller than the media it "contains". It can even contain pointers to other filmLoops. You may find it helpful to think of a filmLoop as a list:
filmLoop = [ #dataConcerningInternalElements: [ #image2: [ #layer1: [#castMember: castmember, #rect: rect, #ink: ink, ...], #layer2: ... ], #image2: [ #layer1 ..., #layer2 ... ], #image3 ..., ... ], #dataConcerningtheFilmloopItself: [ #rect: rect, #loc: loc, ... ] ](Words in italics should be replaced by the appropriate variable type).
This list is obviously not exhaustive, but it gives you the idea. When the playback head finds a filmLoop in a sprite channel, it uses the data stored in the filmLoop to draw the elements correctly on the stage.
I have used the symbols #image and #layer. These properties are somewhat equivalent to "frame" and "sprite", and the list above is similar in many ways to Director's "score". Indeed, a filmLoop is simply a poor man's score: it cannot contain any scripts, palettes, transitions, or any of the other information displayed in the upper panel of the Score Window. This means that creating and editing a filmLoop, either manually or with Lingo, is little different from manipulating the score.
Creating and editing a filmLoop in the score
To create a filmLoop manually, you just select part of the score, copy the selection, and paste it into a cast member. To edit a filmLoop, you simply reverse the process: copy a filmLoop cast member and paste it into an empty area in the score. If you drag-and-drop a filmLoop onto the score, it appears as group of elements in a single sprite. If you copy-and-paste it, it will appear as an "exploded view", so that you can modify its constituent members and their properties (#loc, #ink, "#layer", and so on), through standard score manipulations.
You do not modify the filmLoop cast member itself while you work on the score. To make the changes permanent, you must now select the exploded view of the filmLoop, copy it, and paste it back into the original filmLoop member.
Note that, while "frame" and "sprite" are absolute, #image and #layer are relative. Suppose you create a filmLoop from elements in sprites channels 1 and 2, and in frames 1 through 10. Suppose you now click on sprite 40 on frame 15 and paste your filmLoop into the score. The editable exploded view will now occupy sprite 40 and 41, on frames 15 through 24.
Importing a movie
Director offers another powerful method of creating a filmLoop, which underlines the similarities between a filmLoop and the score: you can import an saved movie into your current movie (File > Import...> select the movie in the dialogue window). This will import all the cast members of the saved movie into your current movie, and create a filmLoop from the data in the lower panel of the score of the saved movie. Behaviors will no longer function, since the sprites they would have been attached to have been converted to layers inside the filmLoop.
You can now place the imported movie filmLoop in a single sprite channel, and act on it in various ways. You can, for instance, resize it and move it across the stage.
TIP: You can think of this as a Movie in a Member, or MIAM. MIAMs, by their very nature, do not have a single black pixel border. MIAWs do. In many cases you can get rid of the single-pixel black border around a secondary movie, without using an Xtra, by importing the movie instead of opening a MIAW.
Example
Imagine you wish to create an animation of a spaceship spinning uncontrollably off into the dark infinity of space. It would cross the screen from left to right, and get smaller as it moved away. You could create a movie called "SpinLoop.dir" in which the spaceship spins just once. You could then import this into your main movie. Director will automatically convert it into a repeating filmLoop called "SpinLoop". Now you simply place the filmLoop in a sprite, and tween the sprite to move from left to right and get smaller. SpinLoop.dir remains as an independent movie which you can store in a library folder... and use again. [SpinLoop.dcr]
Creating filmLoops on the fly
Why botherAt the beginning of the year, I posted a message on Direct-L explaining how to create a filmLoop on the fly. A number of people wrote back to ask "Neat, but why should you want to?"
One good reason is to economise on sprite channels. A filmLoop need not necessarily contain an animation. It can contain a group of static elements copied from a single frame. Suppose you are writing a kid's game where the user can create a picture by "stamping" bitmap images onto the screen. If you use trails to create the stamped images, you cannot provide an "Undo" feature. If you use a whole sprite channel for each stamped image, the child is likely to find his or her creativity blocked after the 120th stamp. A solution would be to place each image into a separate channel, until all 120 channels were used up ... then create a filmLoop out of those 120 channels, and place it in channel 1. Suddenly, there are 119 free channels.
Another good reason would be to create a "Group" command. In a "Dressing-up game", where the child chooses clothes for a figure, you may want to manipulate the clothed figure as a single unit, so that it can be moved about the screen. To do this, you would create a filmLoop on the fly which would contain the figure and all its clothes.
Converting the score into a filmLoop
When you import a movie, the entire score is converted into a filmLoop. We're going to use a similar technique. We're going empty the score of the current movie, place cast members on the now empty stage, save our new score as a filmLoop, then put the original score back. This sounds a bit like cutting off the branch you're sitting on, but Director can do it so fast you don't have time to fall. Almost.
Exercise
Create a new movie and place a few #shape members in various channels and various frames. Just to fill up the score a bit. Now type this into the Message Window:
set filmLoopMember to new (#filmLoop) set the media of filmLoopMember to the scoreNow be daring. Select your entire score and delete it. It wasn't an undying masterpiece after all. Now type this into the Message Window:
set the score to the media of filmLoopMemberResurrection!
Actually, you can save the score in a variable. Modify your score a little. Now type this into the Message Window:
set saveScore to the scoreNow destroy your score as before and type:
set the score to saveScoreLet's do one more trick. Empty the score then create a filmLoop of the empty score:
set emptyScore to new (#filmLoop) set the media of emptyScore to the scoreNow fill the score up a bit (you can copy and paste your first filmLoop into the score, if you like). When you're satisfied, type this into the Message Window:
set the score to the media of emptyScoreFffoutch... all gone. We'll be using all three of these techniques, but not in this order. The second will serve to save and restore the original score, the third to empty the score in preparation for building the filmLoop, and the first to create the filmLoop.
Score generation
Now that you can create a filmLoop through Lingo, it's time to look at how to customize the score on the fly. Director does this with the score generation commands.
TIP: Score generation commands have no effect on puppeted sprites, so first you should save a list of puppeted sprites and unpuppet them. You would use the list to repuppet them once the score recording is done. I'm not going to do this however, because I have another technique (which we will see shortly) which sidesteps the problem. Just remember that, if you have difficulties with score generation in other contexts, puppets might be the cause.
The basic score generation commands are:
beginRecording -- DO THINGS endRecordingIn between, you can move the playback head around and place cast members in sprite channels. It's easiest to understand this with an example. Let's create a simple floating window with
- a frame
- a drag-cum-title bar at the top
- a grow box in the bottom right corner
Exercise
Open a new movie, and save its empty score into a filmLoop member called "emptyScore". Create a bitmap image for the grow box button. Call it "Grow box". The bitmap is the only element which you won't be able to create through Lingo. Now open a Movie script and type (or paste) the following handlers:
on createWindow -- DETERMINE PALETTE DIMENSIONS set dimension to 64 -- SQUARE WINDOW set titleHeight to 16 -- HEIGHT OF DRAG BAR set borderSize to 1 -- WIDTH OF LINE AROUND WINDOW set shadowSize to 2 -- WIDTH OF SHADOW BELOW WINDOW -- DETERMINE THE POSITION OF THE GROW BOX BUTTON set growMember to member "Grow box" set growRegH to (the width of growMember) - ¬ dimension - borderSize - 1 set growRegV to (the height of growMember) - ¬ dimension - borderSize - 1 - titleHeight set the regPoint of growMember to point ¬ (growRegH, growRegV) -- CREATE THE FRAME set frameMember to new (#field) set the name of frameMember to "Frame" set the border of frameMember to borderSize set the boxDropshadow of frameMember to shadowSize set the boxType of frameMember to #fixed set the rect of frameMember to rect (0, 0, ¬ dimension, dimension + titleHeight) -- CREATE THE TITLEBAR set titleMember to new (#field) set the name of titleMember to "Title" set the text of titleMember to "Window" set the fontSize of field titleMember to 12 if the machineType = 256 then set the font of ¬ field titleMember to "System" else set the font of field titleMember to "Chicago" set the alignment of field titleMember to "center" set the border of titleMember to borderSize set the boxType of titleMember to #fixed set the rect of titleMember to rect (0, 0, ¬ dimension, titleHeight) -- CREATE THE WINDOW OUT OF ITS CONSTITUANT PARTS set windowMember to createLoop ([frameMember, ¬ titleMember, growMember], "Window") end createWindow
on createLoop memberList, loopName set memberCount to count (memberList) -- PREPARE THE SCORE set saveScore to the score set the score to the media of member "emptyScore" -- CREATE THE LOOP beginRecording -- go frame 1 repeat with i = 1 to memberCount set the member of sprite i to getAt ¬ (memberList, i) set the loc of sprite i to point (0, 0) end repeat endRecording set loopMember to new (#filmLoop) set the media of loopMember to the score set the name of loopMember to loopName -- RESTORE THE ORIGINAL SCORE set the score to saveScore return loopMember end createLoopNote how the createWindow handler set the regPoint of the grow box member so that when its loc coincides with the point (0, 0), the grow box is correctly placed. We'll be looking at this technique in much greater depth further on. It will allow us to move a bitmap member around inside a filmLoop as if it were a sprite.
Now take a deep breath and type this into the Message Window:
createWindowWhat do you mean, "Nothing happened"? Look at your Cast Window. Aren't there three new cast members, including a filmLoop called "Window"? Drag the "Window" member onto the stage. Isn't she a beauty?
Just to check that this wasn't a fluke, let's see if this still works in a projector. Empty your score of all sprites, then create a button in sprite 1. Give the button member the text "Create Window", and the following script:
on mouseUp createWindow end mouseUpTo simplify things for the moment, make sure that your button sprite is only one frame long. You now have a one-frame, one sprite movie. Add this handler to your movie script...
on exitFrame go the frame end... and extend your createWindow handler so that the new window is displayed. Here is a quick hack:
... set windowMember to createLoop ([frameMember, ¬ titleMember, growMember], "Window") -- DISPLAY NEW WINDOW IN AN EMPTY CHANNEL set theSprite to random (119) + 1 set x to random (the width of the rect of ¬ the stage) set y to random (the height of the rect of ¬ the stage) beginRecording set the member of sprite theSprite to ¬ windowMember set the loc of sprite theSprite to point (x, y) endRecording end createWindowEmpty your cast of all members except:
- the grow box bitmap
- the "Create window" button and its script
- the movie script with the createWindow and exitFrame handlers
- the emptyScore filmLoop
Now save your movie as "Window1.dir", and make a projector with it inside. Launch your projector and click on the "Create window" button.
There's an ugly flash each time the "Create window" button is clicked. The button and all existing windows disappear momentarily as the score is cleared. The new window then appears for an instant in the corner of the stage, and then all the windows reappear. The new window leaps from the corner of the stage to its random position. No amount of setting the updateLock to TRUE has ever helped me stomp this ugly flash.
Using an off screen MIAW
The flash comes from trying to saw off the branch that you're sitting on. The workaround is to move the score generation to a different branch. Actually, we're going to use a MIAW and shared cast. But we don't want to have the MIAW cluttering up the screen. The trick is to create it... and never open it.
Exercise Create a new movie. Some PCs don't like movies with an empty score, so we're going to create a single-pixel 1-bit bitmap, and place it in sprite 1, spanning a single frame. Now create a filmLoop cast member of the score. Call it "emptyScore". OK, so it's not quite empty, but that doesn't matter.
Let's open our movie up to the outside world by giving it an external castLib. Choose File > New > Cast.... Make sure the options External and Use in current movie are checked. Call your castLib "Transfer". Just leave it empty for now. Save your movie as "LoopMIAW.dir", and your new castLib as "Transfer.cst".
Reopen "Window1.dir". Link the castLib "Transfer.cst" to it (Modify > Movie > Casts... then click on Link and locate Transfer.cst in the dialog window). We're going to have to modify our createWindow handler. Changes are in blue
on createWindow -- DETERMINE PALETTE DIMENSIONS set dimension to 64 -- SQUARE WINDOW set titleHeight to 16 -- HEIGHT OF DRAG BAR set borderSize to 1 -- WIDTH OF LINE AROUND WINDOW set shadowSize to 2 -- WIDTH OF SHADOW BELOW WINDOW -- IDENTIFY SHARED CAST set transferCast to the number of castLib ¬ "Transfer" set lastMember to the number of members of ¬ castLib transferCast -- COPY GROW BOX BUTTON TO THE SHARED CAST set emptySlot to the number of member findEmpty ¬ (member 1 of castLib transferCast) of ¬ castLib transferCast duplicate member ¬ "Grow box", emptySlot -- DETERMINE THE POSITION OF THE GROW BOX BUTTON set growMember to member emptySlot set growRegH to (the width of growMember) - ¬ dimension - borderSize - 1 set growRegV to (the height of growMember) - ¬ dimension - borderSize - 1 - titleHeight set the regPoint of growMember to point ¬ (growRegH, CREATE THE FRAME set frameMember to new (#field, castLib ¬ transferCast) set the name of frameMember to "Frame" set the border of frameMember to borderSize set the boxDropshadow of frameMember to shadowSize set the boxType of frameMember to #fixed set the rect of frameMember to rect (0, 0, ¬ dimension, dimension + titleHeight) -- CREATE THE TITLEBAR set titleMember to new (#field, castLib ¬ transferCast) set the name of titleMember to "Title" set the text of titleMember to "Window" set the fontSize of field titleMember to 12 if the machineType = 256 then set the font of ¬ field titleMember to "System" else set the font of field titleMember to "Chicago" set the alignment of field titleMember to "center" set the border of titleMember to borderSize set the boxType of titleMember to #fixed set the rect of titleMember to rect (0, 0, ¬ dimension, titleHeight) -- CREATE THE WINDOW IN AN UNOPENED MIAW set loopMiaw to window "LoopMIAW" tell loopMiaw set windowSlot to createLoop ( [ the memberNum of frameMember, the memberNum of titleMember, the memberNum of growMember ], "Window" ) end tell forget loopMiaw set windowMember to member windowSlot of ¬ castLib transferCast -- DISPLAY NEW WINDOW IN AN EMPTY CHANNEL set theSprite to random (119) + 1 set x to random (the width of the rect of ¬ the stage) set y to random (the height of the rect of ¬ the stage) beginRecording set the member of sprite theSprite to ¬ windowMember set the loc of sprite theSprite to point (x, y) endRecording end createWindow on exitFrame go the frame endTo make debugging easier, we'll place the createLoop handler in the shared "Transfer" cast. It will also be slightly modified:
on createLoop memberList, loopName -- LOCATE TRANSFERRED MEMBERS set transferCast to the number of castLib ¬ "Transfer" set memberCount to count (memberList) -- PREPARE THE SCORE set saveScore to the score set the score to the media of member "emptyScore" -- CREATE THE PALETTE beginRecording -- go frame 1 repeat with i = 1 to memberCount set theMember to member getAt (memberList, i) ¬ of castLib transferCast set the member of sprite i to theMember set the loc of sprite i to point (0,0) end repeat endRecording set loopMember to new (#filmLoop, castLib ¬ transferCast) set the media of loopMember to the score set the name of loopMember to loopName -- RESTORE THE ORIGINAL SCORE set the score to saveScore return the memberNum of loopMember end createLoopThere are three things to notice:
First, we have created a duplicate of the original "Grow box" bitmap and placed it in the shared "Transfer" castLib. The new members that are going to be used in the filmLoop are created directly in the shared castLib. This means that they are available to both movies. If one movie modifies these members the changes are visible in the other. We'll see why we keep the original "Grow Box" in the internal castLib in a while.
Second, we are getting a different movie to do all the donkey work. Since this movie is never opened, there is no visual cue that anything is happening.
Third; we need to identify the members correctly, regardless of which movie is referring to them. This is done be passing the memberNum of the members between movies, and then getting each movie to find the appropriate members in the "Transfer" castLib.
TIP: Debugging MIAWs, is much easier when the handlers are placed in a castLib which is shared between the MIAW and the stage. This allows you to place a debug checkpoint on the "tell ..." line of your stage handler, then step through the MIAWs code line by line. If the code were not in a shared cast, Director would be unable to display it in the debugger window. Now save your Window1 movie as "Window2.dir". Create a new projector and test it. Was there a flash this time?
Modifying the members of a loop
So far, our grow box doesn't function. Nor does the drag bar. What we're going to do now is to create a behavior which will detect when we click on precise parts of the loop sprite. Then we'll give the behavior two handlers to react to these clicks:- a dragBar handler
- a growBox handler
- the regPoint of the "Grow box" button
- the rect of the "Title" and "Frame" members
- the media of the "Grow box" member in the shared castLib.
Detecting clicks
Save your movie as "Window3.dir". Ensure that you have one valid set of window element members and strip out all the rest. You can also remove the "Create window" button and the "emptyScore" filmLoop from the internal castLib. Delete all sprites from the score.
Create a new script and set its type to "Score" (Modify > Cast Member > Properties... then choose Score in the Type pop up menu). Call your score script "Window-in-a-Sprite". Type (or paste) the following handlers:
property windowMember property frameMember property dragMember property growMember property titleHeight property dragZone property growReg property growZone property growAdjust property topLeft property stageWidth property stageHeight on new me set transferCast to the number of ¬ castLib "Transfer" set windowMember to member "Window" of ¬ castLib transferCast set frameMember to member "Frame" of ¬ castLib transferCast set dragMember to member "Title" of ¬ castLib transferCast set growMember to member "Grow Box" of ¬ castLib transferCast set frameBorder to the border of frameMember set dragZone to the rect of dragMember set titleHeight to the height of dragMember set growReg to the regPoint of growMember set growZone to the rect of growMember - rect ¬ (growReg, growReg) set growAdjust to point (the width of growMember, ¬ the height of growMember) - frameBorder - 1 set topLeft to point (the left of sprite the ¬ spriteNum of me, the top of sprite the ¬ spriteNum of me) set stageWidth to the width of the rect of ¬ the stage set stageHeight to the height of the rect of ¬ the stage end on mouseDown me set relativeMouseLoc to point (the mouseH, the ¬ mouseV) - point (the left of sprite the ¬ spriteNum of me, the top of sprite the ¬ spriteNum of me) if inside (relativeMouseLoc, dragZone) then ¬ dragBar me else if inside (relativeMouseLoc, growZone) ¬ then growBox me else beep end on dragBar me set clickH to the mouseH set clickV to the mouseV set loopLoc to the loc of sprite the spriteNum¬ of me -- MORE LINES TO COME HERE repeat while the mouseDown set deltaH to the mouseH - clickH -- WILL BE MODIFIED set deltaV to the mouseV - clickV -- WILL BE MODIFIED set mouseDelta to point (deltaH, deltaV) set the loc of sprite the spriteNum of me ¬ to loopLoc + mouseDelta updateStage end repeat set topLeft to point (the left of sprite the ¬ spriteNum of me, the top of sprite the ¬ spriteNum of me) end on growBox me -- TEMPORARY STUB put "click in the grow box" endDrag your "Window" filmLoop from the "Transfer" cast into an empty sprite channel , and drag your "Window-in-a-Sprite" behavior on top of it. Now start your movie and try clicking on it in different places. Does the drag bar function?
To stop the user from dragging the window off the stage, insert these lines in the place of the line "
-- MORE LINES TO COME HERE...
...
-- PREVENT THE TITLE BAR FROM LEAVING THE STAGE set maxH to stageWidth - the locH of (growAdjust¬ - growReg + topLeft) set maxV to stageHeight - the locV of topLeft ¬ - titleHeight set minH to -the locH of topLeft set minV to -the locV of topLeft
...
... and replace the lines marked -- WILL BE MODIFIED with these:
set deltaH to min( maxH, max (minH, the mouseH ¬ - clickH)) set deltaV to min( maxV, max (minV, the mouseV ¬ - clickV))
Altering the regPoint of a bitmap
Let's get some action into that grow box. First we're going to get it to move around to follow the mouse. To begin with, we'll use something similar to the dragBar handler (the asterisks indicate where we'll be adding lines to the handler later):
on growBox me set clickH to the mouseH set clickV to the mouseV -- *** set growH to the locH of the regPoint ¬ of growMember set growV to the locV of the regPoint ¬ of growMember repeat while the mouseDown set deltaH to the mouseH - clickH set deltaV to the mouseV - clickV set growReg to point (growH - deltaH, growV ¬ - deltaV) -- *** set the regPoint of member growMember to growReg -- * set the media of member windowMember to the ¬ media of member windowMember -- ** end repeat set growZone to the rect of growMember - rect ¬ (growReg, growReg) endYou'll notice that when you drag the grow box outside the window frame, the whole window moves in the opposite direction. This is because the size of the filmLoop member is changing, but its loc is not. Don't worry.
Altering the rect of vector-based members
Now let's alter the rect of the window frame and title so that the grow box really does its job. Insert the following lines in place of the line "-- *" into the script that you just typed in:
... set bottomRight to growAdjust - growReg set the rect of member frameMember to rect ¬ (point (0, 0) , bottomRight) set the rect of member dragMember to rect ¬ (0, 0, the locH of bottomRight, titleHeight) set dragZone to rect (0,ht to growAdjust - growReg set the rect of mem 0, the locH of bottomRight,¬ titleHeight) ...Just a couple of tweaks and we're done. First, to ensure that the top left of the window remains fixed, insert the following lines in place of the line
"-- **":
... set the loc of sprite the spriteNum of me to ¬ topLeft + the regPoint of member windowMember updateStage ...Second, to ensure that the window cannot be reduced to less than a minimum size, nor extended beyond the edge of the stage, insert the following lines in place of the line"
-- ***"...
... set minH to 64 set minV to 64 set maxH to stageWidth - the locH of topLeft ¬ - the locH of growAdjust set maxV to stageHeight - the locV of topLeft¬ - the locV of growAdjust
...
...and the following lines in place of the line
" set growReg to point (growH - deltaH, growV - ¬ deltaV) -- ***": ... set newH to min (maxH, max (minH, deltaH ¬ - growH)) set newV to min (maxV, max (minV, deltaV ¬ - growV)) set growReg to point (-newH, -newV) ...Now test the grow box again. Ain't that cool?
Altering the media of members
For a final refinement, let's show that the grow box has been clicked on. We'll do this by swapping the "Grow box" bitmap for a "depressed" grow box image. Save your movie as "Window4.dir". This will be the definitive version.
Make a copy of your "Grow Box" bitmap (the one in your internal cast), and use the Paint Window alter the shading so it looks depressed. Call your new image "Grower box". Store it in your internal castLib, just beside its sibling.
This "refinement" is going to require a minor overhaul of our behavior. Here is the final version in full, with the latest changes marked in red:
property windowMember property frameMember property dragMember property growMember % property grow1Member % property grow2Member property titleHeight property dragZone property growReg property growZone property growAdjust property topLeft property stageWidth property stageHeight on new me set windowMember to member "Window" set frameMember to member "Frame" set dragMember to member "Title" set growMember to member "GrowBox" set grow1Member to member "Grow Box" set grow2Member to member "Grower Box" set stageWidth to the width of the rect ¬ of the stage set stageHeight to the height of the rect ¬ of the stage set frameBorder to the border of frameMember set dragZone to the rect of dragMember set titleHeight to the height of dragMember set growReg to the regPoint of growMember set growZone to the rect of growMember - ¬ rect (growReg, growReg) set growAdjust to point (the width of ¬ growMember, the height of growMember) - ¬ frameBorder - 1 set topLeft to point (the left of sprite ¬ the spriteNum of me, the top of sprite ¬ the spriteNum of me) set stageWidth to the width of the rect ¬ of the stage set stageHeight to the height of the rect ¬ of the stage end on mouseDown me set relativeMouseLoc to point (the mouseH, ¬ the mouseV) - point (the left of sprite ¬ the spriteNum of me, the top of sprite the ¬ spriteNum of me) if inside (relativeMouseLoc, dragZone) then ¬ dragBar me else if inside (relativeMouseLoc, growZone)¬ then growBox me else beep end on dragBar me set clickH to the mouseH set clickV to the mouseV set loopLoc to the loc of sprite the ¬ spriteNum of me -- PREVENT THE TITLE BAR FROM LEAVING THE STAGE set maxH to stageWidth - the locH of (growAdjust¬ - growReg + topLeft) set maxV to stageHeight - the locV of topLeft ¬ - titleHeight set minH to -the locH of topLeft set minV to -the locV of topLeft repeat while the mouseDown set deltaH to min( maxH, max (minH, the ¬ mouseH - clickH)) set deltaV to min( maxV, max (minV, the ¬ mouseV - clickV)) set mouseDelta to point (deltaH, deltaV) set the loc of sprite the spriteNum of me ¬ to loopLoc + mouseDelta updateStage end repeat set topLeft to point (the left of sprite ¬ the spriteNum of me, the top of sprite the¬ spriteNum of me) end on growBox me set clickH to the mouseH set clickV to the mouseV set minH to 64 set minV to 64 set maxH to stageWidth - the locH of ¬ topLeft - the locH of growAdjust set maxV to stageHeight - the locV of ¬ topLeft - the locV of growAdjust set growH to the locH of the regPoint ¬ of growMember set growV to the locV of the regPoint ¬ of growMember -- SWAP GROW BOX IMAGE set the media of growMember to the media ¬ of grow2Member set the regPoint of growMember to point ¬ (growH, growV) repeat while the mouseDown set deltaH to the mouseH - clickH set deltaV to the mouseV - clickV set newH to min (maxH, max (minH, ¬ deltaH - growH)) set newV to min (maxV, max (minV, ¬ deltaV - growV)) set growReg to point (-newH, -newV) set the regPoint of member growMember ¬ to growReg set bottomRight to growAdjust - growReg set the rect of member frameMember to ¬ rect (point (0, 0), bottomRight) set the rect of member dragMember to ¬ rect (0, 0, the locH of bottomRight, ¬ titleHeight) set dragZone to rect (0, 0, the locH of ¬ bottomRight, titleHeight) set the media of member windowMember to ¬ the media of member windowMember set the loc of sprite the spriteNum of ¬ me to topLeft + the regPoint of member ¬ windowMember updateStage end repeat set growZone to the rect of growMember - ¬ rect (growReg, growReg) -- REPLACE ORIGINAL GROW BOX IMAGE set the media of growMember to the media of ¬ grow1Member set the regPoint of growMember to growReg set the media of member windowMember to the ¬ media of member windowMember end
OK, the behavior is all yours. Hey, since you've been good, I'll even let you write the getBehaviorDescription handler for it. Take it out for a test drive in the Shockwave movie. You can also download the un-shocked .dir file in Mac or PC format.
Handling text
Before you get too carried away with how easy it is to do neat things with filmLoops, let's examine their weak point. Text. You can't edit text inside a filmLoop. You can however change the text of field members inside the filmLoop. So if you really need to have an editable field, what you do is this:- Copy the text inside the filmLoop into another field member
- Place this field in a free sprite above your filmLoop, taking care to match the size and position of the free field and the filmLoop field exactly
- Set the editable of the free field to TRUE, so the user can enter his or her text
- Copy the text back into the filmLoop field
- Set the media of the filmLoop member to the media of the filmLoop member
- Hide the free field.
FilmLoops in Shockwave
Size limitationsShockwave is not entirely filmLoop friendly either. You may have noticed one aspect of this in my Shockwave demo of the behavior. The window filmLoop starts out almost as big as the stage. If you make it bigger by dragging the grow box, parts of the window will disappear. This is because the rect of a filmLoop in Shockwave is fixed once and for all. If you move any parts of the constituent members outside the initial rect, those parts will no longer be drawn on the stage.
The workaround is to make the filmLoop member as big as it will ever be in the saved .dcr, then make it smaller. You can do this outside the stage, so that it remains hidden. I chose not to this in this particular case, so that you can see the problem for yourself.
No score generation
The other limitation is that Shockwave does not support score generation, so you can't build filmLoops on the fly. So "Undo" in Stamp games and "Grouping" in Dressing-up games is not shockable.
Advantages
You may not be able to create filmLoops, but you can do this:
set the score to the media of member "ScoreLoop"
If you want to know more about the implications of that, ask Joe Sparks. It's his brainchild. Just a hint: you can stream unused cast members, but your movie won't start until the entire score is loaded. If you want the movie to start fast, use a small score. If you then decide you want a big score ...
Summary
PlusFilmLoops are full of surprises. They offer much more that the possibility of creating looping animations.
- You can compress a number of sprites into a single filmLoops and thus gain elbow room when you need a lot of channels.
- You can group related elements together into handy single-sprite units. You can save complex filmLoops as independent movies, and import them into your main movie.
- You can build a filmLoop seamlessly on the fly, by commandeering an unopened MIAW to handle the necessary score generation.
- You can create interaction with all the constituent members, by acting on certain member properties:
- the regPoint of member... for bitmaps
- the rect of member... for vector-based members
- the media of member... for all member types
Minus
Editable text is not supported by filmLoops. Workarounds exist. Shockwave won't let you increase the size of a filmLoop beyond its initial rect, nor will it let you create one on the fly.
That's it.
Now what are you waiting for? Who's going to be the first to send me an animated, fully-interactive filmLoop with a 3-D effect?Acknowledgements
I am indebted to Irv Kalb and Joe Sparks for enlightening me on how to make the most out of a filmLoop. The expression Movie in a Member (MIAM) was coined by Dr. Andreas Viviani. Artwork is by Charlotte Thibault
Copyright 1997-2024, Director Online. Article content copyright by respective authors.