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