Dynamism
The "Dynamism" Movie Script
Every project in which you want to create WFS dynamic sprites must
have a copy of the "Dynamism" movie script in a Cast. Also,
you must edit a line of this script to set the number of dynamic sprite
channels available to WFS. Please read the below section on the Dynamic
Channel Manager for the where/how/why of this edit.
If you want to create WFS dynamic sprites, you may not need to call
any handlers in "Dynamism" at all except the Destructors.
Please check out the documentation on the "Script
Writer" handlers for the easiest, most powerful way to create
dynamic multi-sprites, elements, and families of multi-sprites. They
are easy to use and may do all you want to do.
You may want to use handlers in "Dynamism", however. Your
primary usage of the Dynamism handlers will probably involve the destructors,
documented here. Although WFS does garbage collection of dynamic sprites
at the end of the movie, you may want to destroy elements or multi-sprites
or families of multi-sprites before the movie is over. In that
case, you will want to look at
Dynamic Channel Manager
What sprite channels are used for dynamic sprites?
WFS provides an API into the Dynamic
Channel Manager.
Dynamic sprites are created by puppeting empty sprite channels (or
using makeScriptedSprite, in DMX 2004 and later), giving the channel
a member (either by creating the member or using an already existing
one), and attaching and initializing behaviors on the fly.
If you've done any puppeting of channels, you know that channels that
have a drag and drop sprite in them at any point in the movie do
not work right as puppeted channels. In other words, you encounter
problems if you mix dynamic and static sprite channels. In particular,
there are problems attaching behaviors to sprites in channels that are
static (drag and drop) at any point during the movie. This
is because when you create a dynamic sprite, it is instantiated through
all the frames of the movie until you destroy it.
How WFS solves this problem is to use channels for dynamic creation/destruction
that never have a static sprite in them at
any point during the movie. The "Dynamism" script partitions
the Score into two blocks. The top block of channels is for static sprites.
The bottom block of (higher-numbered) channels is for dynamic sprites.
The Dynamic Channel Manager in the "Dynamism" script manages
the channels available for dynamic creation.
Editing gWFSLastStaticSpriteChannel
How does WFS know how to draw the line between the two blocks of sprites
(static and dynamic)? It doesn't know how. You have to tell
it where to draw the line. How do you do this? You edit the value of
gWFSLastStaticSpriteChannel in the wfsInitializeDynamism handler in
the "Dynamism" script. The wfsInitializeDynamism script is
shown below. It is recommended you read the comments.
on wfsInitializeDynamism
--FUNCTION:*****************************************************************************
--wfsInitializeDynamism is called in the "1: prepareMovie" script
in the prepareMovie
-- handler as long as the 'Dynamism' script is in a cast.
wfsInitializeDynamism
-- initializes the WFS engine for managing channels of dynamic
sprites.
--IMPORTANT NOTE*********************************************************************
--You *must* edit the value of gWFSLastStaticSpriteChannel, below.
Set it to be the
--highest-numbered sprite channel that has a drag and drop sprite
in it anywhere
--in the Score. Or set it higher than this, ie, if you are developing
and adding static
--sprites to the Score, set gWFSLastStaticSpriteChannel to some
conveniently high
--value (less than 'the lastChannel') so that you don't have
to keep editing it as you
--develop. Then, when your project is finished, edit gWFSLastStaticSpriteChannel
to be
--the highest-numbered channel with a drag and drop (static)
sprite in it so that you
--have as many dynamic channels as you can have.
--All channels higher-numbered than gWFSLastStaticSpriteChannel
will be used for
--dynamic sprites. All other channels will not be used for dynamic
sprite
--creation (nor will gWFSLastStaticSpriteChannel) . By default,
--gWFSLastStaticSpriteChannel=the lastChannel.
--In other words, by default there are *no* dynamic channels.
--WFS splits the Score into two vertical, disjoint parts: static
sprite channels
--(lower-numbered channels) and dynamic channels (higher-numbered
channels).
--Why? Because there are Director problems if you try to use
a channel as dynamic if
--there is a drag and drop sprite in it at any point in the movie.
--Note that you can change the total number of sprite channels
in the movie via
--the Movie tab in the Property Inspector.
gWFSLastStaticSpriteChannel
= the lastChannel --EDIT THIS VALUE
gWFSNumberOfDynamicallyAllocatableChannels=the lastChannel - gWFSLastStaticSpriteChannel
wfsCreateDynamicChannelManager()
gWFSCreatedMembers=[]
gWFSCreatedMembers.sort()
end wfsInitializeDynamism
As it says in the above code, you need to edit the value of gWFSLastStaticSpriteChannel.
By default, you don't have any dynamic channels to play with
because, by default, gWFSLastStaticSpriteChannel is set to the lastChannel, ie, by default
WFS assumes all channels are static.

If the Score of your movie looked like the above, then you would set
gWFSLastStaticSpriteChannel to 4. The WFS Dynamic Channel Manager would
then use channels 5 and higher for dynamic sprites.
Max 1000 Channels in Director 8 - MX 2004
Director 8 - MX 2004 permits a maximum of 1000 simultaneously instantiated
sprites. The number of channels can exceed 1000, but the maximum number
of simultaneously instantiated sprites is 1000. You generally
want to be well below that, for performance reasons, but 1000 is your
absolute max.
How do you change the total number of channels in your movie? The below
graphic shows you how:

The Dynamic Channel Manager will use channels gWFSLastStaticSpriteChannel+1
to the lastChannel for dynamic sprites. I normally set the total number of
channels in a movie to 1000 if I'm using dynamic sprites.
How sprite channels are allocated by the Dynamic Channel
Manager
Before a dynamic sprite is created, the Dynamic Channel Manager checks
to see if there is an available dynamic channel. If there are any, the
Dynamic Channel Manager allocates the first available one, ie, the lowest-numbered
available dynamic channel. So, for instance, the first channel
it allocates will always be gWFSLastStaticSpriteChannel+1,
presuming you have edited gWFSLastStaticSpriteChannel so
that there are some dynamic channels to allocate (see above).
This is kind of important to understand. Why? You need a sense of where
your sprites are in the Score for debugging purposes, for one thing.
Also, sometimes we write handlers that have algorithms that do things
to the sprite in channel 'spritenum - 1' or something like that, ie,
we assume that sprites are contiguous in the Score because they are.
But when you use that sort of logic concerning WFS dynamic sprites,
it doesn't always work.
For instance, if you make a dynamic copy of a multi-sprite (using the
handlers in the "Script Writer"
script) after you have created and destroyed more than 1000 sprites,
elements that were contiguous in the static model you used to generate
the dynamic multi-sprite may no longer be contiguous in the Score. They'll
work fine in the WFS code, but they might not be contiguous in the Score,
perhaps, because, when creating dynamic sprites, the Dynamic Channel
Manager allocates the first available one, ie, the lowest-numbered
available dynamic channel (a sorted queue).
What remains invariant, then? Well, if you use the "Script Writer"
handlers to create multi-sprites, the elements are created in a certain
order. So that if, in a static multi-sprite, a particular sprite was
element 5, it will still be element 5 of a dynamic version of the multi-sprite.
In other words, it will still have spritenum pWFSElementList[5] (unless
you delete lower elements), where pWFSElementList is maintained by the
Manager. wfsGetElementList returns
a duplicate of pWFSElementList, a multi-sprite's list of spritenums
of elements.
Various WFS handlers make calls to the wfsGetUnusedChannel private
handler in the "Dynamism" script to obtain a channel in which
to create a dynamic sprite. You shouldn't have to make calls to this
handler since WFS does it for you, but you should understand how allocation
works. wfsGetUnusedChannel returns a channel number or 0 if there are
no channels available. All dynamically created sprites in WFS have to
go through the Dynamic Channel Manager to obtain a channel.
Various WFS destructor handlers also make calls to the private handler
wfsReturnChannel(theChannel) when dynamic sprites are destroyed. This
returns the channel to the Dynamic Channel Manager so the channel can
be reused.
When you create a static multi-sprite, the elements find their manager
on beginSprite. They can find their manager because they assume
that their manager is the first manager in the Score that is above them
in the Score. You can use wfsAddElementToManager
thereafter, ie, you can change the manager to be some other manager
after the elements become instantiated, so the manager doesn't have
to be above the elements in the Score once the element's beginSprite
handler has run. But a static element does have to be below its initial
manager in the Score.
Dynamic elements don't assume that their manager is above them in the
Score. Why not? Well, given that the Dynamic Channel Manager allocates
the first available channel, ie, the lowest-numbered available dynamic
channel, after your movie runs a while and creates and destroys
sprites, the first available channel could be anywhere among the dynamic
channels. So the beginSprite handler operates a bit differently for
dynamic elements. They are assigned a manager rather than finding their
own manager. This is done with a call to wfsAddElementToManager.
Notice also that when you use wfsWriteElement,
the output handler requires you tell it which Manager to add the element
to. Dynamic elements can't find their manager like static ones can.
Limitations of WFS Dynamic Sprites
There are certain properties that the "Script Writer" handlers
do not read when they write code to copy sprites. Over time, and with
your feedback, this list will be shortened.
In WFS, when a sprite is created using the wfsCreateSprite handler,
the parameters fed to wfsCreateSprite are these: [memberNameOrNumber,
castNameOrNumber, ink, blend, width, height, locH, locV, doNotCreateNewMember].
If you feel there should be more properties read, let me know, please.
Of course, the behaviors are dealt with thoroughly, but the properties
of the member and the sprite are not exhaustively read.
Also, if you use the buttons circled below in the Property Inspector,
the "Script Writer" behaviors will not pick up these properties.
These are relatively easy to deal with, however. For instance, the
button above that makes text sprites editable can be done otherwise.
You can attach a behavior to the sprite, instead, that sets the 'editable'
property.
sprite(whichsprite).editable=1 sets a text sprite as editable, and
setting that property to 0 makes it non-editable
WFS contains the "Drag Element"
behavior, so you won't need the button above that makes sprites moveable.
And the "Drag Element" behavior is better since you can constrain
the movement in a configurable way.
sprite(whichsprite).trails=1 makes a sprite leave trails. Setting that
property to 0 gets rid of trails.
sprite(whichsprite).flipV=1 is the same as clicking the 'flip vertical'
button above.
sprite(whichsprite).flipH=1 is the same as clicking the 'flip horizontal'
button above.
sprite(whichsprite).rotation=90 is the same as specifying 90 degrees
in the rotation text box in the property inspector.
sprite(whichsprite).skew=5 is the same as specifying a 5 percent skew.
And so on: for each button or text box you can set in the Property
Inspector, there's an equivalent Lingo command you can use; the Lingo
way to do these things will be picked up by the WFS Dynamic routines
because you'll have attached a behavior to the sprite, and WFS copies
behaviors perfectly.
Please let me know if there are improvements you feel are warranted
in these areas in WFS. WFS is the first tool that makes dynamic sprite
creation/destruction relatively easy. So I'll need your feedback on
improving it.
Dynamic Sprite Creation Bugs
This section has not been updated concerning makeScriptedSprite/removeScriptedSprite.
This section discusses dynamic sprite creation via puppetSprite in Director
5 to Director MX.
- Keep in mind that dynamic sprite creation is unsupported by Macromedia,
which means that it could change from version to version in Director.
It hasn't from D5 to MX, and many people use it, but that doesn't
mean it won't change.
- If you puppet a sprite channel that, at some point in the movie,
contains a static sprite, and the static sprite has member type x
but you give the puppeted sprite member type y, problems occur. WFS
deals with this by creating dynamic sprites in channels that never
ever have a static sprite in them. And, in this case, you can create
a sprite of member type x, destroy it, and then create a sprite of
member type y.
- Rasmus Keldorff and Robert Tweed say the same thing (that I confirmed
with Flash sprites). Rasmus says: "There is the problem that
I believe Robert mentioned with member types. You cannot 'talk' to
a newly minted vector, Flash or QuickTime puppet sprite (and I believe
animgifs have similar problems), at least until you've had the playhead
move, in which case it re-arrives at the sprite and assigns it the
proper properties for that type. Ie. this code:
sprite(1).member = member("flashmovie")
sprite(1).gotoframe(10)
will fail because the sprite does not yet 'know' that it is a Flash
sprite. Also, this code won't solve the problem:
sprite(1).member = member("flashmovie")
updatestage
sprite(1).gotoframe(10)
because updatestage does nothing but redraw the Stage with the most
recently updated frame information."
You can design around this problem by waiting to issue the sprite(1).gotoframe(10)
command (and the like) a bit later.
- Rasmus Keldorff and Danny Kodicek report the following persistent
problem with no known workaround: Rasmus: "One thing I haven't
seen mentioned regarding dynamic sprites is the problem regarding
color. Setting the .color or .bgcolor of a puppet sprite (I do this
quite a lot with text etc.) causes some problems, because it's very
difficult to get the channel to forget its color assignment. The trick
I usually use is:
sprite(channel).forecolor
= 255
sprite(channel).backcolor = 0
but it doesn't seem to always work; I haven't worked out why. I've
chased this ghost before on this list (or direct-l, I can't remember)
and it seemed there is no way to 'reset' a puppet channel. The only
thing you can really do is to overwrite all the properties you will
need to modify with what you consider to be the default values. Only,
in this case it is a little tricky because the .color property 'colorizes'
the whole bitmap, and there is no way to give it a 'zero-color', or
even 'void-color'."
I haven't encountered this problem, but if Rasmus and Danny both have,
it's out there.
|