Articles Archive
Articles Search
Director Wiki
 

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:

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 bother
At 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 score

Now 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 filmLoopMember

Resurrection!

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 score

Now destroy your score as before and type:

set the score to saveScore

Let'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 score

Now 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 emptyScore

Fffoutch... 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
endRecording

In 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

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 createLoop

Note 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:


createWindow

What 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 mouseUp

To 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 createWindow

Empty your cast of all members except:

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
end

To 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 createLoop

There 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: The growBox handler will work by modifying certain properties of the filmLoops constituent members:

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"
end

Drag 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)
end

You'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: There. That wasn't so bad, was it.

FilmLoops in Shockwave

Size limitations
Shockwave 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

Plus
FilmLoops are full of surprises. They offer much more that the possibility of creating looping animations. Streaming filmLoop members is a powerful technique for building a complex score in Shockwave movies.

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

James Newton started working with Director 5 in 1997. He wrote many of the behaviors that ship with Director since version 7. James lives in Dunoon, near Glasgow, Scotland. His company, OpenSpark Interactive, is responsible for marketing PimZ OSControl Xtra. When not coding he can be found racing his classic Flying Fifteen around the Holy Loch, making things with his four children, or catching escaped hamsters.

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