Behaviors are really not something entirely new as you might expect.
They are nothing more than a simplified way to use parent/child
objects. When a sprite begins, Lingo automatically creates a child
instance of the behavior script just as it would for a parent script.
In the case of a behavior it automatically puppets the sprite, creates
a property variable for the object called spriteNum and initializes
it to the number of the sprite channel. This new object instance is then
stored in an internal list of sprites. This behavior object receives some
new messages such as BeginSprite, and you can send it messages with SendSprite.
In most other respects it operates much as a normal child object operates.
We use sprite behaviors for everything from our developer utilities such as DreamLight RAMLight through full-length CD-ROM projects such as the award-winning KeyQuest edutainment CD-ROM, to our on-line entertainment products like Quipples:
The Internet game show of satirical riddles.
This page also includes some miscellaneous bugs, tips & tricks not directly related to behaviors.
Date: Feb. 20, 2002
(Condensed from my somewhat lengthy discussion about this issue on Wish-Director)
What is the number of the frame script sprite (channel)?
Well, it depends exactly who you ask and exactly when you ask.
First, let me outline how to currently (as of D8.5.1) get a grasp on this slippery beast in Lingo. There are three different values used to indicate the frame script channel depending on how you ask and from where you ask. They are as follows:
- Inside a frame script behavior, the value of the currentSpriteNum is 0.
- Inside a frame script behavior, the value of Me.spriteNum is -5.
- Inside a frame script behavior's isOkToAttach handler, the sprite number argument is 1.
I have personally found that referring to the frame script sprite number by using the currentSpriteNum (which is 0 in a frame script) is currently the best way to refer to a frame script sprite in a behavior, especially since it's the only valid way to access any of the sprites properties via Lingo as you'll see below. (Though you can only access a couple of Director's built in properties for it this way) Currently, in an isOKTattach handler though, you can't use the passed sprite number since the number 1 could easily be mistaken as graphic sprite number 1, so you have to rely on the type argument instead. It would obviously be much more consistent if all three of these access methods used the same number to refer to the frame script channel.
Now, let me outline the case I made to Macromedia to fix this problem by changing Me.spriteNum and the sprite number argument in an isOKToAttach handler, both to also return 0 for the frame script sprite.
Since the original definition of a sprite in Director, going way back, was that of an animated graphic, the script channel was not "really" considered a "sprite" and thus the spriteNum of the frame script channel has always been considered "ill defined". In the current implementation of Director, using behaviors, the frame script channel has much more in common with what Director now considers sprites than it used to. So much so in fact that it would be much more consistent and easier to program if the frame script channel were fully promoted to sprite(0), as it already partially is considered by Lingo, as you'll see below.
- The frame script channel uses sprite behaviors which are attached to it, just like they are to graphic channels, they may span a range of frames just like other sprites. They are written and attached exactly as they are for other channels.
- The frame script channel receives the following sprite behavior messages: beginSprite, endSprite.
- You can send a frame script behavior messages using sendSprite( 0, #message )
- The frame script channel also has properties of sprite(X).startFrame and sprite(X).endFrame.
- The frame script channel also has properties of sprite(X).scriptInstanceList
- To access a frame script sprite's startFrame, endFrame and scriptInstanceList properties, you must use zero... You can't use -5 (startFrame & endFrame will generate script errors, and scriptInstanceList will return 0) and you can't use 1 which really refers to the graphic sprite number 1... These must be accessed as sprite(0), only, so IMHO the spriteNum (and attachedSpriteNum) should be 0 as well for frame script sprites...
So, even though Lingo currently uses three different numbers to refer to the frame script channel ( 0, -5, and 1 ) it only allows access to any of the sprite's properties if you use 0, which corresponds to the currentSpriteNum. If you attach the following behavior to a graphic sprite and the frame script sprite (spanning a different range of frames) and run the movie you'll see what I mean... also, while it's running use sendSprite(0,#report) to send a custom message to the copy running in the frame script.
property spriteNum, pSprite, pSpriteName
on beginSprite Me
Me.pSpriteName = "sprite(" & the currentSpriteNum & ")"
Me.pSprite = sprite( the currentSpriteNum )
Me.report()
end
on report Me
put "sprite( the currentSpriteNum ) =" && \
"sprite(" & the currentSpriteNum & ")"
put Me.pSpriteName & ".startFrame =" && Me.pSprite.startFrame
put Me.pSpriteName & ".endFrame =" && Me.pSprite.endFrame
put Me.pSpriteName & ".scriptInstanceList =" && \
Me.pSprite.scriptInstanceList
put "Me.spriteNum =" && Me.spriteNum
put RETURN
end
-- "sprite( the currentSpriteNum ) = sprite(1)"
-- "sprite(1).startFrame = 1"
-- "sprite(1).endFrame = 28"
-- "sprite(1).scriptInstanceList = [<offspring "SpriteBehavior" 3 146e8058>]"
-- "Me.spriteNum = 1"
-- "
"
-- "sprite( the currentSpriteNum ) = sprite(0)"
-- "sprite(0).startFrame = 5"
-- "sprite(0).endFrame = 8"
-- "sprite(0).scriptInstanceList = [<offspring "SpriteBehavior" 3 146e2788>]"
-- "Me.spriteNum = -5"
-- "
"
If you edit the above script to use Me.spriteNum instead of the currentSpriteNum, it will only work on the graphic sprite, not the frame script sprite...
The primary argument against changing this, is that some people have been using Me.spriteNum = -5 to detect a sprite behavior running as a frame script. I don't recall Me.spriteNum = -5 being documented or supported but I do recall the currentSpriteNum = 0 being both documented and supported to detect a sprite behavior running as a frame script. Therefore, changing Me.spriteNum to also return 0 for a frame script sprite sounds entirely reasonable to me. If the frame script channel were promoted to sprite(0) we could use the spriteNum to access its properties just like in other sprites.
-MikeS
Date: Feb. 1, 2002 (Updated Mar, 12, 2002)
isOKToAttach
Lingo Dictionary. Pg. 252
The example will not work as written, if the user uses the suggested convention of including the line
property spriteNum
to explicitly declare the automatic sprite property spriteNum. As shown on page 475.
Attach this behavior to a sprite and you'll see what I mean:
property spriteNum -- automatically set when instanced
on isOKToAttach Me, spriteType, spriteNum
beep
put EMPTY
put "isOKToAttach:"
put "spriteNum =" && spriteNum
return TRUE
end
You'll see this in the message window:
-- ""
-- "isOKToAttach:"
-- "spriteNum = "
In this case Lingo uses the declared property spriteNum inside the isOKToAttach handler (rather than the sprite number passed to isOKToAttach) and at the time that the handler is actually called, the property "spriteNum" is not defined and is VOID. spriteNum only gets set when the behavior is actually instanced, during run time, so the test will not work as expected.
Instead you should use a different variable name, rather than spriteNum in your isOKToAttach handler. Something like attachedSpriteNum or aSpriteNum will work fine. The example would also work if you don't explicitly declare spriteNum as a property in the behavior. Explicit declaration is optional, but personally I would recommend declaring it, since it makes the code easier to understand to someone who may not be a Lingo expert.
D8.5.1 isOKToAttach BUG
The Lingo Dictionary also states that isOKToAttach handler is only called once, when a behavior is first attached to a sprite or script channel. There is a bug in D8.5.1 where every isOKToAttach handler, of every behavior, is also called multiple times in certain cases when the behavior tab of the property inspector or the behavior inspector is brought to the foreground with any sprite selected.
Steps to reproduce
- Create a new movie in D8.5.1
- Paste this script into a new script cast member
on isOKToAttach Me, aSpriteType, aSpriteNum
beep
put EMPTY
put "isOKToAttach:"
put "aSpriteType =" && aSpriteType
put "aSpriteNum =" && aSpriteNum
return TRUE
end
- Set the script's type to a behavior
- Create a rectangle on the stage
(DONT ATTACH THE BEHAVIOR TO IT)
(LEAVE THE RECTANGLE SELECTED ON THE STAGE)
- Open the behavior inspector
- Click the "+" pop up and release it. You should hear one beep
- Open the message window and you should see the following.
-- Welcome to Director --
-- ""
-- "isOKToAttach:"
-- "aSpriteType = graphic"
-- "aSpriteNum = 1"
- Bring the behavior inspector back to the foreground
Now you'll get multiple beeps and multiple sets of info appearing in the message window. Now any time you switch to another window and then bring the behavior inspector back to the foreground, isOkToAttach will run multiple times. Therefore you may want to avoid putting alerts, beeps, or other similar statements in an IsOKToAttach handler, since they may be called often, when you may not expect them to be.
Thanks,
-MikeS
Date: Jan. 31, 2002
Here's a simple frame behavior you can use to loop in the current frame for a specified number of seconds...
-- Description
on getBehaviorDescription Me
return \
"DreamLightš LoopFrame Frame Behavior" & RETURN & \
"© 2001 DreamLight Incorporated. DreamLight.com" & RETURN & \
"-----------------------------------------------" & RETURN & \
"DESCRIPTION:" & RETURN & \
"Attach to a frame script to loop for the specified number of seconds." && \
"Then goes to the next frame when time is up." & RETURN & \
RETURN & \
"PARAMETERS:" & RETURN & \
"¥ Seconds: Number of seconds to wait, set to zero to wait forever."
end
on getBehaviorTooltip Me
return \
"Attach to a frame script to loop in the frame for X seconds."
end
on isOKToAttach Me, attachSpriteType, attachSpriteNum
return attachSpriteType = #script -- ONLY works as a frame script
end
-- Property Variables
property seconds -- number of seconds to loop, set to 0 to loop forever
property waitTicks -- number of ticks wait for to loop
on getPropertyDescriptionList Me
return [ \
#seconds: [ #comment: "Loop for how many seconds?:", \
#format: #integer, \
#default: 1 ] \
]
end
-- Sprite Handlers
on beginSprite Me
Me.waitTicks = the ticks + (Me.seconds * 60)
end
on exitFrame Me
if (Me.seconds = 0) OR (the ticks <= Me.waitTicks) then go to the frame
end
Hope it helps,
-MikeS
Date: Oct. 9, 2001
>Is there a simple way to combine two or more property lists into one
>"master" property list?
Here's a simple utility function you may use...
on concatLists list1, list2
-- (C) 2001 DreamLight.com
-- Returns list1 and list2 as concatenated list3
if ilk(list1) <> ilk(list2) then
alert( "concatLists: Type mismatch." )
return VOID
end if
list3 = duplicate(list1)
case ilk(list1) of
#list:
repeat with n = 1 to list2.count
list3.append( list2[n] )
end repeat
#propList:
repeat with n = 1 to list2.count
list3.addProp( list2.getPropAt(n), list2[n] )
end repeat
otherwise:
alert( "concatLists: Linear or property lists required." )
return VOID
end case
return list3
end concatLists
--
Here's how you can use it, as seen in the message window...
-- Welcome to Director --
x = [ #one:1, #two:2 ]
y = [ #three:3, #four:4 ]
a = [ 1, 2, 3 ]
b = [ 4, 5, 6 ]
put concatLists( x, y )
-- [#one: 1, #two: 2, #three: 3, #four: 4]
put concatLists( a, b )
-- [1, 2, 3, 4, 5, 6]
--
We typically create utility handlers like this for common functions that we need to perform. Over time, we collect all these into utility scripts for use in subsequent projects. We have created simple collections for common list functions, string functions, etc...
For more complicated common uses, we create object oriented manager classes such as for memory management, sound management, etc...
Hope it helps,
-MikeS
Date: Jun. 13, 2001
i have a string that = "[CompanyOverview, Products, Programs, TopService]"
and i need to get it into a list that will look like this
["CompanyOverview", "Products", "Programs", "TopService"]
Here are two handlers that will do the job. If you build the string without any spaces, then you won't need the second handler... ;-)
Hope it helps,
-MikeS
--
on stringToList str
-- converts a string like this:
-- "[CompanyOverview, Products, Programs, TopService]"
-- into a list like this:
-- ["CompanyOverview", "Products", "Programs", "TopService"]
-- Extract items from the string leaving behind the square brackets
str = chars( str, 2, str.length - 1 )
-- save old item delimiter in case it's used elsewhere
-- this can be safely deleted if you know you're not changing or relying
-- on the itemDilimiter eleswhere in your program, since the default is ","
oldDelimiter = the itemDelimiter
the itemDelimiter = ","
newList = [] -- create a new empty list
repeat with n = 1 to str.item.count -- loop through the items
-- add each item to the list, with any whitespace removed...
newList.add( stripWhiteSpace(str.item[n]) )
end repeat
-- reset itemDelimiter to it's previous value
the itemDelimiter = oldDelimiter
-- return the list to the calling handler
return newList
end
--
on stripWhiteSpace str
-- remove any leading and/or trailing spaces from a string
-- remove leading whitespace
repeat while str.char[1] = SPACE
delete char 1 of str
end repeat
-- remove trailing whitespace
repeat while str.char[ str.length ] = SPACE
delete char str.length of str
end repeat
return str
end
-- Use them this way...
strLst = "[ CompanyOverview, Products, Programs, TopService]"
put stringToList( strLst )
-- ["CompanyOverview", "Products", "Programs", "TopService"]

|
|
 |
Date: Aug. 26, 2000
> So, the rect problem is solved now, but it still draws
> the MIAW on the screen before it moves it to the new loc.
I'm not sure what you're trying to do with the frameLabels, but myWin.open() inherently sets myWin.visible to TRUE. So you could set up the window first and then do myWin.open() when you want to see the window for the first time. Or you could build your code more flexibly to allow various scenarios.
Here's one simple example:
on initWin x, y
global gWin
gWin = window( "launch" )
gWin.windowType = 2
moveWin( x, y )
gWin.open()
end initWin
--
on moveWin x, y
global gWin
winWidth = 800
winHeight = 600
gWin.rect = rect( x, y, x + winWidth, y + winHeight )
end moveWin
--
This way you could simply initialize and open the window like this:
initWin( the stageLeft + 190, the stageTop + 80 )
If you need to do some processing in the window that does require it to be opened before you see it, you could do something like this:
initWin( - 100000, - 100000 )
-- very far away incase there are other monitors, the Mac could have
-- up to six monitors and some may be above to the left, so make
-- sure your numbers are large enough to keep it from showing...
-- do whatever you like... then
moveWin( the stageLeft + 190, the stageTop + 80 )
--
If you wanted to get even fancier, you could build an object to handle the window. If you make a windowHandler object generalized enough, you'd be able to reuse it in other projects that need to handle windows...
-MikeS
Date: Aug. 23, 2000
Automatically setting the user's display to the appropriate
resolution is not difficult, but has some drawbacks. For example,
we've found that some LCD projectors will not display QuickTime
movies at 800x600 (although the host computer screen is
simultaneously displaying it perfectly), but the movies work just
fine at a higher resolution. If we automatically set the resolution,
the user would be hosed.
Another option is to resize the stage itself up to fill the screen. We recently did this on DreamLight Verttice which was originally designed (in 1991) for 512 x 384. We check the user's screen size and if it's 1024 x 768 or larger, we double the stage size up to 1024 x 768 with the following code...
on stageSize whichSize
-- whichSize is 1 for normal size or 2 for pixel doubling
screenRect = (the desktopRectList)[1]
stageSizes = [ rect(0,0,512,384), rect(0,0,1024,768) ]
if whichSize <> 1 then -- use default
if screenRect.right >= 1024 AND screenRect.bottom >= 768 then
whichSize = 2
else whichSize = 1
end if
newRect = stageSizes[ whichSize ]
(the stage).drawRect = newRect -- scale stage to size
-- center stage on screen
hOffset = integer( (screenRect.width - newRect.width) / 2.0 )
vOffset = integer( (screenRect.height - newRect.height) / 2.0 )
(the stage).Rect = offset( newRect, hOffset, vOffset )
end
Actually the code checks for 1 or anything else in which case it pixel doubles if it fits. This way it defaults to pixel double if it fits and we then let the user use command-1 and command-2 to change it themselves if they wish.
Try the DreamLight Verttice download edition to see it in action.
http://DreamLight.com/webshop/entertainment/verttice.html
(Note: It's disabled in the Shockwave edition, so you need to download the off-line version for either the Mac or Windows to see the pixel doubling.)
Date: Aug. 11, 2000
I'm currently updating DreamLight Verttice in D7.0.2 to run in 8-bit and under current Shockwave players. It was originally written in D3 to run in 4-bit and no longer runs in Shockwave 7 or later, it used factories etc... ;-)
I've run into the following two Director bugs...
1) Changing 8-bit palettes in the score immediately shows the new palette while the stage still shows the previous picture (Tested on Mac). If you try to use fade to black or white, it changes the palette first, then fades to black or white and then fades to the new image. Not very useful. I've basically had to resort to doing a pixel dissolve to a black frame, changing the palette while on the black frame and then pixel dissolving to the new frame. Unfortunately pixel dissolves look crappy on the PC so this is not an optimal solution.
2) In a full screen projector under Win-98, Lingo can set the colordepth from 32 down to 8 bit (I need 8-bit so I can colorize the game board via the palettes). The depth does report as changed but the screen just goes black, and stays black, not too useful... ;-) (Tested on Win-98) So I've had to resort to telling the user to change it themselves under Windows and just quit. It works fine on the Mac though.
-MikeS
Date: Jul. 10, 2000
If the window rect is going to be "translated" when I give it to a window, then it would make much more sense to me, if it were then "untranslated" by the same amount when given back to me.
Actually, the more I thought about this, why couldn't Director simply add the menu bar "above" the window rect? Much as the title bar is added above the window rect on the Mac? Then no translation of the rect itself would be necessary and the Lingo code:
set the rect of window "sample" to the rect of window "sample"
would maintain it's logical integrity across operating systems and we'd have direct "unfutzed" access to the window content's rect on all systems. That would make the most sense to me...
We don't have to translate our content's rect on the Mac, even though the title bar is added, why should we have to translate our content's rect on Windows just because a menu bar may be added as well?
On the Mac if I do this:
set the rect of the stage to rect(0,0,100,100)
it translates it down, to account for the menu bar so that then...
put the rect of the stage
-- rect(0, 71, 100, 171)
But no matter what, this...
set savedRect to the rect of the stage
-- ...
set the rect of the stage to savedRect
will always return the window to wherever it was, translated or not...
Just food for thought for a future version of Director...
The more you can maintain the logical integrity of Lingo itself, across systems, the better, IMHO... FWIW... ;-)
-MikeS
Date: Jul. 10, 2000
>Under Windows in authoring, the stage rect is set based on the menu
>bar being inside the stage window, as it would be in a projector. In
>authoring though the menu bar appears at the top of the screen, and
>so the stage gets moved down by the height of the menu by mistake.
If I ask a window to give me it's rect, I would logically expect that I could then give that rect back to the same window later and it would return to its original position. That's just what it does on the Mac but on Windows it moves...
Ideally, the Lingo programmer should be insulated from such operating system kludginess. IMHO, this Lingo code:
set the rect of window "sample" to the rect of window "sample"
should logically return a window to it's original position.
>You could either stop trying to set the rect every frame, or you
>could set the rect once, see how much it was wrong by, and then allow
>for that next time.
Of course I can hack around the problem. I created that sample code, not because I was really trying to set the rect to itself each frame, but simply to illustrate the illogical unsymmetrical handling of the window rect on Windows.
If the window rect is going to be "translated" when I give it to a window, then it would make much more sense to me, if it were then "untranslated" by the same amount when given back to me. This would solve the problem and the Lingo code:
set the rect of window "sample" to the rect of window "sample"
would maintain it's logical integrity across operating systems.
>From my knowledge, this has always worked this way. Or it has for a long time.
This may be true...
Then it's a bug that should have been fixed a long time ago... ;-)
Just my humble opinion... ;-)
Do with it what you wish... ;-) ;-)
-MikeS
Date: Jul. 7, 2000
In the process of tracking down a memory leak/fragmentation problem that surfaced in my Quipples project, I've created a new version of the DreamLight RAMLight. I was getting the NEW RAMLight 3.0 ready for release, with a quick test under Windows (since I develop on the Mac) when I noticed odd movement of the MIAW under Windows 98. Further investigation revealed a strange inconsistency.
I did a quick search on the net to see if there was any information about the problem but didn't turn anything up, so I figured I'd post a report to see if anyone else has seen this behavior and to have someone test it under D8 and other permutations of Windows. It's possible that there's something unique about my Win98 setup, since I would think this would have surfaced already if it were an across the board Windows problem.
DESCRIPTION:
Under Windows 98 setting the rect of a window and getting the rect of a window appear to be using different coordinate systems or offsets...
STEPS TO REPRODUCE:
- Open Director 7.0.2 under Mac OS 9.0.4
- Enter the following frame script in frame 1:
on exitFrame
set the rect of the stage to the rect of the stage
go to the frame
end exitFrame
- Run the movie. Nothing should happen since you are simply setting the rect of the stage equal to the rect of the stage which should be itself.
Now for the fun part...
- Run the same movie under Director 7.0.2 under Windows 98.
In my case, what happens is the stage whizzes off the screen, step by step. It seems that getting the rect of the stage and setting the rect of the stage are using different coordinates or offsets for some strange reason and are not equal. So each time you set the rect to itself, it moves... This also happens with MIAW's using "the rect of window" and first surfaced in the RAMLight since it is a resizable MIAW it adjusts its own rect which works fine on the Mac but was causing it to move each time on Win98.
Has anyone else come across this behavior?
Can someone try it under other permutations of Windows and also under D8 to see if they can reproduce it?
Thanks,
-MikeS
Date: Oct. 1, 1997
>I saw the above behavior just last night on two Macs running OS8 with 6.0
You probably already tried this but be sure to compile all scripts from the menu if strange things begin happening. Periodically I had run into some strange behavior of scripts no longer being recognized during calls to new( script "..." ) and it turned out that they simply need a recompile... ;-)
-MikeS
Date: Sep. 22, 1997
Hi John,
The more I investigate this problem the stranger it gets... ;-)
After the information you posted and my last test I decided to go ahead and figure out just which properties could be used in a beginSprite handler.
So, I did the following;
- Create a rectangle sprite at (10,10,100,100) in sprite 1 frames 5-10
- Create a rectangle sprite at (20,20,200,200) in sprite 1 frames 15-20
- I set various random properties through the score such as moveable, blend etc...
- I created the following handler and attached it to both sprites
on beginSprite me
putProps me, "beginSprite"
end
on endSprite me
putProps me, "endSprite"
end
on putProps me, fromHandler
put ""
put fromHandler && "Sprite Property Test"
put "------------------------------------"
set props to¬
[ "backColor", "blend", "castLibNum", "constraint", "cursor", ¬
"foreColor", "height", "ink", "left", "loc", "locH", "locV", ¬
"memberNum", "moveableSprite", "puppet", "rect", "scriptNum", ¬
"stretch", "type", "visible", "width" ]
repeat with what in props
do "put what & " & QUOTE & ":" & QUOTE & "&& the" && what && ¬
"of sprite the spriteNum of me"
end repeat
end
- I then saved, opened and ran this and was astounded!
This time the rect property was working 100% properly???
-- ""
-- "beginSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- "blend: 50"
-- "castLibNum: 1"
-- "constraint: 0"
-- "cursor: 0"
-- "foreColor: 116"
-- "height: 90"
-- "ink: 0"
-- "left: 10"
-- "loc: point(10, 10)"
-- "locH: 10"
-- "locV: 10"
-- "memberNum: 4"
-- "moveableSprite: 1"
-- "puppet: 0"
-- "rect: rect(10, 10, 100, 100)"
-- "scriptNum: 2"
-- "stretch: 1"
-- "type: 16"
-- "visible: 1"
-- "width: 90"
-- ""
-- "endSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- "blend: 50"
-- "castLibNum: 1"
-- "constraint: 0"
-- "cursor: 0"
-- "foreColor: 116"
-- "height: 90"
-- "ink: 0"
-- "left: 10"
-- "loc: point(10, 10)"
-- "locH: 10"
-- "locV: 10"
-- "memberNum: 4"
-- "moveableSprite: 1"
-- "puppet: 0"
-- "rect: rect(10, 10, 100, 100)"
-- "scriptNum: 2"
-- "stretch: 1"
-- "type: 16"
-- "visible: 1"
-- "width: 90"
-- ""
-- "beginSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- "blend: 50"
-- "castLibNum: 1"
-- "constraint: 0"
-- "cursor: 0"
-- "foreColor: 116"
-- "height: 180"
-- "ink: 0"
-- "left: 20"
-- "loc: point(20, 20)"
-- "locH: 20"
-- "locV: 20"
-- "memberNum: 4"
-- "moveableSprite: 1"
-- "puppet: 0"
-- "rect: rect(20, 20, 200, 200)"
-- "scriptNum: 2"
-- "stretch: 1"
-- "type: 16"
-- "visible: 1"
-- "width: 180"
-- ""
-- "endSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- "blend: 50"
-- "castLibNum: 1"
-- "constraint: 0"
-- "cursor: 0"
-- "foreColor: 116"
-- "height: 180"
-- "ink: 0"
-- "left: 20"
-- "loc: point(20, 20)"
-- "locH: 20"
-- "locV: 20"
-- "memberNum: 4"
-- "moveableSprite: 1"
-- "puppet: 0"
-- "rect: rect(20, 20, 200, 200)"
-- "scriptNum: 2"
-- "stretch: 1"
-- "type: 16"
-- "visible: 1"
-- "width: 180"
- So, I figured that something I was doing with the other properties was trigering an update of the rect property. To pinpoint exactly when the rect property was being calculated or updated I edited the behavior as follows:
on beginSprite me
putProps me, "beginSprite"
end
on endSprite me
putProps me, "endSprite"
end
on putProps me, fromHandler
put ""
put fromHandler && "Sprite Property Test"
put "------------------------------------"
set props to¬
[ "backColor", "blend", "castLibNum", "constraint", "cursor", ¬
"foreColor", "height", "ink", "left", "loc", "locH", "locV", ¬
"memberNum", "moveableSprite", "puppet", "rect", "scriptNum", ¬
"stretch", "type", "visible", "width" ]
repeat with what in props
do "put what & " & QUOTE & ":" & QUOTE & "&& the" && what && ¬
"of sprite the spriteNum of me"
testRect me
end repeat
end
on testRect me
put the rect of sprite the spriteNum of me
end
- I then saved, opened and ran this version and lo and behold, check this out...
-- ""
-- "beginSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- rect(0, 0, 0, 0)
-- "blend: 50"
-- rect(0, 0, 0, 0)
-- "castLibNum: 1"
-- rect(0, 0, 0, 0)
-- "constraint: 0"
-- rect(0, 0, 0, 0)
-- "cursor: 0"
-- rect(0, 0, 0, 0)
-- "foreColor: 116"
-- rect(0, 0, 0, 0)
-- "height: 90"
-- rect(0, 0, 0, 0)
-- "ink: 0"
-- rect(0, 0, 0, 0)
-- "left: 10"
-- rect(10, 10, 100, 100)
-- "loc: point(10, 10)"
-- rect(10, 10, 100, 100)
-- "locH: 10"
-- rect(10, 10, 100, 100)
-- "locV: 10"
-- rect(10, 10, 100, 100)
-- "memberNum: 4"
-- rect(10, 10, 100, 100)
-- "moveableSprite: 1"
-- rect(10, 10, 100, 100)
-- "puppet: 0"
-- rect(10, 10, 100, 100)
-- "rect: rect(10, 10, 100, 100)"
-- rect(10, 10, 100, 100)
-- "scriptNum: 9"
-- rect(10, 10, 100, 100)
-- "stretch: 1"
-- rect(10, 10, 100, 100)
-- "type: 16"
-- rect(10, 10, 100, 100)
-- "visible: 1"
-- rect(10, 10, 100, 100)
-- "width: 90"
-- rect(10, 10, 100, 100)
-- ""
-- "endSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- rect(10, 10, 100, 100)
-- "blend: 50"
-- rect(10, 10, 100, 100)
-- "castLibNum: 1"
-- rect(10, 10, 100, 100)
-- "constraint: 0"
-- rect(10, 10, 100, 100)
-- "cursor: 0"
-- rect(10, 10, 100, 100)
-- "foreColor: 116"
-- rect(10, 10, 100, 100)
-- "height: 90"
-- rect(10, 10, 100, 100)
-- "ink: 0"
-- rect(10, 10, 100, 100)
-- "left: 10"
-- rect(10, 10, 100, 100)
-- "loc: point(10, 10)"
-- rect(10, 10, 100, 100)
-- "locH: 10"
-- rect(10, 10, 100, 100)
-- "locV: 10"
-- rect(10, 10, 100, 100)
-- "memberNum: 4"
-- rect(10, 10, 100, 100)
-- "moveableSprite: 1"
-- rect(10, 10, 100, 100)
-- "puppet: 0"
-- rect(10, 10, 100, 100)
-- "rect: rect(10, 10, 100, 100)"
-- rect(10, 10, 100, 100)
-- "scriptNum: 9"
-- rect(10, 10, 100, 100)
-- "stretch: 1"
-- rect(10, 10, 100, 100)
-- "type: 16"
-- rect(10, 10, 100, 100)
-- "visible: 1"
-- rect(10, 10, 100, 100)
-- "width: 90"
-- rect(10, 10, 100, 100)
-- ""
-- "beginSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- rect(10, 10, 100, 100)
-- "blend: 50"
-- rect(10, 10, 100, 100)
-- "castLibNum: 1"
-- rect(10, 10, 100, 100)
-- "constraint: 0"
-- rect(10, 10, 100, 100)
-- "cursor: 0"
-- rect(10, 10, 100, 100)
-- "foreColor: 116"
-- rect(10, 10, 100, 100)
-- "height: 180"
-- rect(10, 10, 100, 100)
-- "ink: 0"
-- rect(10, 10, 100, 100)
-- "left: 20"
-- rect(20, 20, 200, 200)
-- "loc: point(20, 20)"
-- rect(20, 20, 200, 200)
-- "locH: 20"
-- rect(20, 20, 200, 200)
-- "locV: 20"
-- rect(20, 20, 200, 200)
-- "memberNum: 4"
-- rect(20, 20, 200, 200)
-- "moveableSprite: 1"
-- rect(20, 20, 200, 200)
-- "puppet: 0"
-- rect(20, 20, 200, 200)
-- "rect: rect(20, 20, 200, 200)"
-- rect(20, 20, 200, 200)
-- "scriptNum: 9"
-- rect(20, 20, 200, 200)
-- "stretch: 1"
-- rect(20, 20, 200, 200)
-- "type: 16"
-- rect(20, 20, 200, 200)
-- "visible: 1"
-- rect(20, 20, 200, 200)
-- "width: 180"
-- rect(20, 20, 200, 200)
-- ""
-- "endSprite Sprite Property Test"
-- "------------------------------------"
-- "backColor: 215"
-- rect(20, 20, 200, 200)
-- "blend: 50"
-- rect(20, 20, 200, 200)
-- "castLibNum: 1"
-- rect(20, 20, 200, 200)
-- "constraint: 0"
-- rect(20, 20, 200, 200)
-- "cursor: 0"
-- rect(20, 20, 200, 200)
-- "foreColor: 116"
-- rect(20, 20, 200, 200)
-- "height: 180"
-- rect(20, 20, 200, 200)
-- "ink: 0"
-- rect(20, 20, 200, 200)
-- "left: 20"
-- rect(20, 20, 200, 200)
-- "loc: point(20, 20)"
-- rect(20, 20, 200, 200)
-- "locH: 20"
-- rect(20, 20, 200, 200)
-- "locV: 20"
-- rect(20, 20, 200, 200)
-- "memberNum: 4"
-- rect(20, 20, 200, 200)
-- "moveableSprite: 1"
-- rect(20, 20, 200, 200)
-- "puppet: 0"
-- rect(20, 20, 200, 200)
-- "rect: rect(20, 20, 200, 200)"
-- rect(20, 20, 200, 200)
-- "scriptNum: 9"
-- rect(20, 20, 200, 200)
-- "stretch: 1"
-- rect(20, 20, 200, 200)
-- "type: 16"
-- rect(20, 20, 200, 200)
-- "visible: 1"
-- rect(20, 20, 200, 200)
-- "width: 180"
-- rect(20, 20, 200, 200)
As soon as I made a call to "the left of sprite" the rect property was calculated, or updated or whatever and became available to the beginSprite handler.
So, an easy, albeit unintuitive, workaround to this problem is to access "the left of sprite" before accessing "the rect of sprite" in a beginSprite handler.
John, is there any way that lingo could be updated so that a call to "the rect of sprite" does whatever calculations are being triggered by a call to "the left of sprite" as well? This would seem to me to fix the problem. What do you think? Any other Macromedians have any other input about this problem?
Thanks,
-MikeS
Date: Sep. 22, 1997
Hi John,
Thanks for the information. I went back and ran some more detailed tests based on your information.
The reason that the rect seemed to work within the beginSprite handler every time except the first was that it was using the "previous" rect. The first time through, there was no Previous rect so it reads rect(0,0,0,0). Once it loops or is replayed the rect seemed to be reading properly because there was only one sprite in the channel so the next time through it read the rect from the previous time through which happened to be the proper size because it was a prior instance of the same sprite.
- This shows up easiest by creating three different rectangle sprites in sprite channel 1.
- Use the score to set one to (10,10,100,100) the second to (20,20,200,200) and the third (30,30,300,300)
- Create the following behavior script
on beginSprite me
put ""
put "BeginSprite Rect" && the rect of sprite the spriteNum of me
end
on midSprite me
put "MidSprite Rect" && the rect of sprite the spriteNum of me
end
on endSprite me
put "EndSprite Rect" && the rect of sprite the spriteNum of me
end
- create the following frame script
on exitFrame
sendSprite( 1, #midSprite )
end
- Attach the behavior to each of the three rectangles in sprite 1
- Attach the frame script to any frame in the middle of each of the three rectangles in sprite 1
- Save the movie, open and Run the movie and you'll get the following results:
-- ""
-- "BeginSprite Rect rect(0, 0, 0, 0)"
-- "MidSprite Rect rect(10, 10, 100, 100)"
-- "EndSprite Rect rect(10, 10, 100, 100)"
-- ""
-- "BeginSprite Rect rect(10, 10, 100, 100)"
-- "MidSprite Rect rect(20, 20, 200, 200)"
-- "EndSprite Rect rect(20, 20, 200, 200)"
-- ""
-- "BeginSprite Rect rect(20, 20, 200, 200)"
-- "MidSprite Rect rect(30, 30, 300, 300)"
-- "EndSprite Rect rect(30, 30, 300, 300)"
The beginSprite is reading the rect "before" it has been changed for the new sprite. This gives us the rect of the "previous" sprite instance. The midSprite and endSprite tests are correct.
This leads me to the following question. Is there any way that you guys could have a sprite's properties be initialized before the beginSprite is read? Also, can we get a list of which properties are and are not valid during a beginSprite?
Thanks,
-MikeS
Date: Sep. 19, 1997
OVERVIEW:
When using the sendSprite command, an invisible "me" (or object reference) is created and passed along with the handler call. Normally this goes unnoticed as long as you create all your behavior handlers using the recommended "me" object reference.
If sendSprite doesn't find a handler in the target sprite object however, it passes through to scripts in the cast member, frame script and movie scripts. In these places it is not normal convention to use the me variable because normally these handlers are not associated with objects. Without a clear understanding of what's going on behind the scenes, this can cause trouble.
The Lingo Dictionary is incorrect on page 211 where it discusses the sendSprite command. As written the examples won't work, they are passing variables to the sendSprite command where they should be passing a symbol which is the name of a handler. The on-line entry for sendSprite however is correct, so refer to the on-line help rather than the printed lingo dictionary. ;-)
WHO AM I? EVEN AN OBJECT NEEDS AN IDENTITY:
A rather simple concept that can be difficult to grasp when beginning to write object oriented code is the use of the "me" variable. The term "me" is not a reserved word or any magical variable. It could be named anything such as "self" or "this", but "me" has become the convention in Lingo. It is simply a reference to an object in memory. It is primarily used to identify objects and to give an object a sense of its own identity.
Define an empty object by creating a parent script with nothing in it except a comment. Then name this script cast member "empty object":
-- "empty object" parent script
Then create (or "instance") a child object from this script like so:
-- in the message window type the following...
set childObj to new( script "empty object" )
put childObj
-- <offspring "empty object" 2 aa25d5e>
What's put into the message window is a description of an object reference. This starts with the word "offspring" followed by the name of the cast member in the cast slot where the script was located when the object was first created. This is usually the name of the script itself unless you have moved cast items or loaded different casts after the object was created, in which case the name may not be correct. It is finally followed by hexadecimal numbers that represent the actual memory address where the object instance resides.
The way an object is identified is by this object reference.
When you typed:
set childObj to new( script "empty object" )
Lingo gets the call to new( script "script name" ) and it creates an instance of the object defined in the script "script name". This instance is nothing more than an area of memory that contains a pointer to the script's compiled handlers (in this case nothing but a comment) and a copy of any property variables defined in the script (in this case none). To keep track of this new object instance, Lingo creates a reference to it which it returns to the calling statement. Now obviously this type of empty object is not of much use to anyone... ;-)
Let's take a look at a slightly more interesting object...
-- "simple object" parent script
property name
on new me, whatName
set the name of me to whatName
return me
end
on getName me
return the name of me
end
And let's instance a child object from this new script the same way we did previously but this time add a name after the script as so:
set childObj to new( script "simple object", "Fred" )
childObj
-- <offspring "simple object" 2 aa25f0c>
Doesn't look much different, but after Lingo created the object reference this time, it then looked into the object script itself to see if it contained its own "new" handler and it found one. So it passed the object reference <offspring "simple object" 2 aa25f0c> and the string "Fred" off to this handler which interpreted it in this way.
on new <offspring "simple object" 2 aa25f0c>, "Fred"
set the name of <offspring "simple object" 2 aa25f0c> to "Fred"
return <offspring "simple object" 2 aa25f0c>
end
(NOTE: <offspring "simple object" 2 aa25f0c> is a description of the object reference only, it can't actually be typed out this way in a real Lingo statement it is only shown in these examples for explanation. This is the description of the reference that would be passed to the "me" variable)
So within the scripts "new" handler, the handler can refer to the property variable that belongs to this particular object in memory since it has a reference to the object itself. This way the object has a sense of "self" through the "me" variable.
When the new handler reaches the return statement, it returns the value of me <offspring "simple object" 2 aa25f0c> back to the original calling statement. Then this returned object reference <offspring "simple object" 2 aa25f0c> is bound to the variable named "childObj".
To find the name of the child object, simply ask it this way:
put getName( childObj )
This passes the object reference stored in "childObj" to the getName handler in the object's parent script like so:
on getName <offspring "simple object" 2 aa25f0c>
return the name of <offspring "simple object" 2 aa25f0c>
end
Or, you could simply access the name property variable directly, the same way the handler does by typing this:
put the name of childObj
--Which becomes:
put the name of <offspring "simple object" 2 aa25f0c>
So the me variable is used as a way to identify which particular object you are talking about and to let the handlers themselves know which object instance to work on. This is how one parent script can work on any number of instanced child objects, through these references.
BEHAVIORS:
Behaviors are really not something entirely new as you might expect. They are nothing more than a simplified way to use parent/child objects. When a sprite begins, Lingo automatically creates a child instance of the behavior script just as it would for a parent script. It automatically puppets the sprite, creates a property variable for the object called "spriteNum" and initializes it to the number of the sprite channel. This new instance is then stored in an internal list of sprites.
The behavior handlers can keep track of the current object through use of their own "me" variable.
Technically, if a particular handler of a behavior (or parent script) does not need to access items in the object instance itself through the me variable and has no other arguments, you could leave off the "me" variable. This can lead to problems however because the object reference is usually passed to the handler anyway as the first parameter. This means that if you want to add arguments to the handler, you need to skip the first argument which would be passed the object reference. The easiest way to do this is to always use the "me" variable as the first argument of any behavior or parent handler you are creating.
PROPER USE:
Let's say we have a behavior with a handler named "handleSomething" accepting an argument called "anArgument". Since it is part of a behavior which is really nothing more than a simplified parent/child object, it would normally be written with the me variable as such:
-- SomeBehavior
on handleSomething me, anArgument
put "Now I'm doing my thing with anArgument:" && anArgument
end
Create a rectangle in sprite 1 from frames 1 to 20. In frame 20 put a "go to the frame" loop.
on exitFrame
go to the frame
end
Attach the behavior to sprite 1 and run the movie. While it's running enter this into the message window.
sendSprite( 1, #handleSomething, "TEST" )
-- "Now I'm doing my thing with anArgument: TEST"
To verify that this behavior is indeed an object enter the following:
put the scriptInstanceList of sprite 1
-- [<offspring "Something Behavior" 1 aa258e0>]
--Now when you typed in:
sendSprite( 1, #handleSomething, "TEST" )
Lingo looks up the object reference for any behaviors attached to sprite 1 and finds: <offspring "SomeBehavior" 1 aa258e0>
It then creates the following type of call:
handleSomething( <offspring "SomeBehavior" 1 aa258e0>, "TEST" )
It's very important to note here that the first argument passed is not "TEST" as you might have thought but rather the object reference. This object reference is then caught by the "me" variable as we saw with normal child objects as follows:
on handleSomething <offspring "SomeBehavior" 1 aa258e0>, "TEST"
put "Now I'm doing my thing with anArgument:" && "TEST"
end
So you can see that even though the me <offspring "SomeBehavior" 1 aa258e0> is not used anywhere inside the handler, it is still passed as the first argument and must be caught.
Now comes the tricky part. Normally if we were writing a handler in a movie script, we wouldn't use the me variable since the handler is not used with any object and an object reference is not passed. However, if the sendSprite command does not find an appropriate handler in the target sprite, it looks through the cast, frame and movie scripts for a matching handler. If it finds a match, it executes it and passes it the same arguments it would have passed the target object, including the target's object reference.
The following handler in a movie script may seem to be written properly since it's not an object:
on handleSomething anArgument
put "Now I'm doing my thing with anArgument:" && anArgument
end
However, if this is called as the result of a sendSprite, the "anArgument" variable will not receive the argument you might have expected, but rather a reference to the target object!
Therefore, if you are writing a handler that is to catch a message from a sendSprite call you must deal with the object reference as well. The handler should be rewritten like this:
on handleSomething originalTargetObject, anArgument
put "Now I'm doing my thing with anArgument:" && anArgument
end
Notice that I've added a new first argument to catch the object reference but I did not use the traditional "me" variable. In this case, using the "me" variable could be confusing because the value passed would not really be a reference to this script's object. Since the handler is in a movie script, there is no object. The value passed is a reference to the original target object. You could name this variable anything you want, originalTargetObject seemed appropriate.
Now go forth and communicate with many sprites using sendSprite... ;-)
-MikeS
Date: Sep. 18, 1997
DESCRIPTION:
The rect of sprite does not work properly within a beginSprite handler the first time a movie is run. (Actually it doesn't work at all, see later notes above...)
STEPS TO REPRODUCE:
- Create a new movie
- In Frame 10, Sprite 1, create a rectangle cast member on stage
- Open the script editor
- Enter the following script...
on beginSprite me
put the rect of sprite the spriteNum of me
end
- Set the script type to score script
- Drag this new score script to the sprite you created in frame 10/sprite 1
- Run the movie...
Seems fine. In frame 10 the rectangle appears and the message window reports the rect properly. Now comes the fun part... ;-)
- Save the movie
- Reopen the movie from the saved file
- Immediately run the movie without touching anything else...
You will be told the rectangle is rect(0, 0, 0, 0) the first time it runs. If you subsequently play it again while it's open it works fine but the very first time it's run the rect function does not work properly within the beginSprite...
ENVIRONMENT:
Tested in D6.0 on a Macintosh 9500 MaxPower MP400+ 240MB RAM
running MacOS 7.6.1
Can anyone else verify this for me?
:-)
-MikeS
Date: Sep. 5, 1997
>One option would be to try to
>maintain referential integrity, so that when the castmember is deleted, the
>reference to it by the score is also deleted.
If I had a frame script in the score and I delete the script from the cast, I would like the score reference to be deleted as well. This is by far the most intuitive approach I can think of. Going the other way, leaving the reference to a blank cast slot is fraught with dangers that I don't think are justified. I had run into this myself and reported it as a bug since it is far from intuitive behavior.
If someone pastes a new script over an older script in the cast, then the score references could be maintained to point to the new script.
> The problem is that this
> would break authoring techniques.
I don't use any authoring techniques that use this "feature", though others out there may. If so I think using this "feature" came about from exploiting an obscure situation. I think that streamlining the product and tightening its intuitiveness would be a better solution than trying to save obscure authoring techniques. If many people out there are using this "feature" during authoring then perhaps you could create a user preference that they could turn on. But for the majority of users it would be safer to default this new preference to maintaining referential integrity.
--
Here are some comments I had as I wrestled with this topic upon first using D6... If I found this confusing, just imagine how a poor newbie would feel... Maybe these comments will spark some ideas on how to make this more intuitive.
How many of you out there have accidently opened a script window which then creates a blank "on exitframe" script and enters it into the score?? I'll bet everyone out there. Here's an idea. If one of the script windows is called up which creates a blank "on exitframe" script, if it is then closed without being edited in any way simply make it disappear into oblivion. I can't tell you how many times I've given a file to a beginner to edit castmembers, and when I get it back, there are all types of blank scripts attached to everything known to man because they accidently opened those windows and didn't know how to "cancel" out of them by fully deleting all the text in the window (which isn't very intuitive) Any votes for that change?? At the very least let's just put a real "Cancel" button on the script window which would close it without entering changes and would delete a script that the user never edits in the first place.
It appears that there are a few bugs with the script channel, I'm getting some very erratic behavior... It almost looks like Director 6 is trying to do just what I outlined above but not entirely successfully. Here's what happens...
1) I open a brand new file.
2) I double click on the script channel in frame 1. This opens the expected Score Script 1 window with the empty "on exitFrame" handler...
3) I realize I don't want a script so I immediately close the script window without editing anything. This now creates a behavior icon in cast member #1 but the castmember itself appears empty and enters the #1 in the score script channel.
4) I then double click on cast memeber #1 and it opens the new behavior inspector. So far, I'm not sold on having this open rather than the script itself but I'll suspend my judgement until I'm more used to the new behaviors. It's great to see that you've supplied a preference for this just in case.
5) I then click the little script icon on the behavior inspector and it opens a completely blank script, no "on exitframe"...
6) I then close that window and boom, the cast member 1 behavior icon is gone and cast member 1 is empty. However there is still an entry in the script channel in frame 1 and it refrences the now empty cast member #1???
7) I click on cast member #1 and nothing happens, it's gone. But it's still entered as a 1 in a little blue box in the script channel of frame 1.
8) So, I then switch to the score window and get another surprise. This time the #1 disappears from the script channel of frame 1 but the blue box remains there...
9) I now double click on the script channel of frame #10. This opens the expected Score Script window with the blank "on exitframe" script ready for me. But here's another surprise. If I look at the score the 1 is now entered in frame 10 AND it's back in frame 1 again...
10) I close the script window (without editing anything again) and I get the blank cast member with behavior icon in cast member #1 again.
11) I now go to the score and double click on the frame 20 script channel. This opens a new Score Script 2 window with an empty "on exitframe" handler as expected.
12) This time I add a comment between the on and end like this...
on exitFrame
--boo
end
13) I then close the window. Now cast member #2 shows the beginning of the script starting with on exitFrame and it has the behavior icon, so when I edit the script it seems to handle everything ok but when I close the script editor window without adding to the script it seems to "half delete" the script castmemeber...
So right now I have cast member 2 looking correct but cast member 1 is blank except for the behavior icon. My script channel has a 1 in frames 1 and 10 and a 2 in frame 20...
14) I now go back to the score and double click on the 1 in frame 10's script channel. This opens a completely empty Score Script 1 window...
15) I then close that window without editing anything. Surprise, the castmember behavior icon disappears from cast member 1 and the 1's dissapear from the script channel in the score, however, the blue boxes remain there...
Here's another one for you.
1) Double click a script channel frame. It opens the Score Script Editor for cast member #1 as before.
2) Imediately close it without editing. You get a 1 in the script cell you clicked in and the blank behavior iconized in cast member #1
3) Now immediately Undo. Surprise. It undoes the entry in the score but leaves the blank behavior iconized script cast member in your cast. You can keep doing this and fill your cast up with blank behaviors that then disappear if their script windows are opened and closed.
-MikeS

|