Getting a handle on Lingo -
Writing generic scripts & custom handlers
Lingo can be a powerful tool. With Lingo, you can control an entire application
from a single frame in the Score. Most of us creative types don't like programming
much and would prefer to write as little Lingo as possible. However, if
you intend to push your Director projects as far as they can go, understanding
Lingo and the fundamentals of programming is vital.
In this technote, I'll take you through the writing of a few generic scripts
and custom handlers. In the process, we'll see some of the key elements
of programming. Programming terms will be written in bold
the first time they are mentioned. If you're unclear about any
of the terminology that is used, take a look at my programming
definitions page. It is also worth looking at the Learning
to Program with Lingo - conditions, conditional statements & loops technote
and the Variable Types, Locals, Globals and Properties
technote.
The ability to write generic scripts and custom handlers is a valuable skill
as it allows better management of code as well as giving the advantage of
reusing scripts from one project to the next. Writing behaviors with parameters
also allows flexibility and reuse of code. This area is covered more the
technotes - Using me and
other parameters in Lingo - including writing behaviors and Understanding
Behaviors - Creating a Parameter Dialog Box.
First, let's look at what behaviors actually are. A behavior is simply
a script that tells either a sprite or a frame how to behave. Frame behaviors
are placed directly in the Score in the scripting channel and sprite behaviors
are linked sprites. Only one behavior can be placed into a single frame
at one time, but you can attach an infinite amount of behaviors to a sprite.
Let's look at a simple behavior:
on mouseUp me
beep
end
The above behavior could be attached to a sprite. It is generic in that
there are no specific references (no hard-coding). However, if instead of
playing a beep sound, I wanted Director to play a sound member called "click",
I could use the statement puppetSound 1,"click".
This statement is hard-coded. Since it is no longer generic, it less flexible
to use from one project to the next. It can only be reused across projects
if I always name my sound "click".
Below is a sprite behavior that switches the cast member associated with
a sprite when it is clicked. In it, we will refer to objects indirectly
to make the script as generic as possible.
_1. on mouseDown me
_2. -- first record which sprite is being
activated
_3. upMember
= sprite(the clickOn).memberNum
_4. -- change the cast member of the activated
_5. -- sprite to the down-pressed cast member,
which
_6. -- must be in the cast member slot following
the
_7. -- up-pressed cast member
_8.
sprite(the clickOn).memberNum = upMember + 1
_9. -- plays the
initial click sound in channel 1
10.
puppetSound 1, "downSound"
11.
updateStage -- updates
and plays sound
12.
repeat while the mouseDown
13.
nothing
14.
end repeat
15. -- set cast member back to up pressed
position
16.
sprite(the clickOn).memberNum = upMember
17.
puppetSound 1, "upSound"
-- button
release sound
18.
updateStage
19. end
In the above behavior, we have done a few things to make
the script generic. The first is we have used a function in the form
of the clickOn. This function
identifies the sprite that is being clicked so we don't have to refer to
it directly.
Let's look at the script line by line.
Line 1 - We use the mouseDown handler.
This is more useful than the mouseUp handler in
this context as it is time-based (mouseUp is instantaneous,
mouseDown can last as long as you like).
Line 2 - I inserted a comment so that when I come back
to this script later, it helps explain what the Lingo represents.
Line 3 - I am saving the memberNum property into
the variable called upMember. This variable is a local variable.
The memberNum property identifies the cast member number
that is associated to a sprite.
Line 4, 5, 6, 7 - More comments.
Line 8 - Change the cast member that is associated to the sprite
to the next consecutive one in the cast window. So, if the sprite was associated
with cast number 5, it will be changed to 5+1 i.e. 6.
Line 9 - comment
Line 10 - Contains a command puppetSound
to play the sound called "downSound". This is one of the specific
references in this script and so becomes one of the rules to make it work.
Line 11 - The updateStage command tells Director
to refresh the screen and retrigger the sound channel. This automatically
occurs when Director moves from one frame to the next. Since we are not
leaving the frame, we need to tell Director to refresh.
Line 12 - Here we contain a loop in the script. A loop is
a section of a script that keeps repeating. A loop can be told to repeat
a number of times or while a condition is true. In our situation, we have
a conditional loop and the condition is while the mouseDown,
which represents while the mouse is still in the down-pressed position.
Line 13 - The instructions to keep repeating in the loop, which is
just nothing.
Line 14- Tells Director to end the loop.
Line 15 - Comment
Line 16 - Change the associate cast member of the sprite back to
what it was originally.
Line 17 - Play a sound when the mouse button is released. Lingo is
executed line by line and will only move to the next line when the current
one has been fulfilled. In our case, we have included a mouseUp event in
the mouseDown handler. The mouseDown is no longer true (the
mouse button has been released), so Director exits the loop and goes to
line 15.
Line 18 - Refreshes the screen.
Line 19 - ends of the script
The next script builds on the one above but adds a conditional statement.
This script will only change the sprite's cast member when the mouse is
held down over the sprite.
on mouseDown me
-- first record which sprite is being
activated
upMember
= sprite(the clickOn).memberNum
repeat while the mouseDown
if rollover(the clickOn) then
sprite(the clickOn).memberNum = upMember + 1
else
sprite(the clickOn).memberNum = upMember
end if
updateStage -- updates
the screen
end repeat
nothing
sprite(the clickOn).memberNum = upMember
puppetSound "upSound"
-- the button
release sound
updateStage
end
In the above script, we included the bulk of the instructions
inside the loop and we included a conditional statement in the form of if...then...
The line if rollover(the clickOn) then checks to see if the mouse
is over the sprite being clicked and does one thing if this condition is
true, another if it is not.
Now we'll look at using custom handlers. Custom handlers are usually placed
in movie scripts. Movie scripts are different from behaviors
in that they sit in the background until they are called.
A movie script can contain instructions for the movie as a whole or can
contain a custom handler (custom message) that can be activated
from a behavior.
A movie script could contain
on startMovie
beep
end
The above script will play a beep sound when the movie
starts.
In a movie script, we can create our own custom message, which I will name
beepMessage.
on beepMessage
beep
end
In a sprite behavior, I could then write:
on mouseUp me
beepMessage
end
The above behavior will call the custom message beepMessage
and activate all statements it contains when the mouse button is released.
Obviously, this becomes more useful if we have more than one statement associated
with the custom message.
Now we'll look at a script with a more complex custom handler. This script
will change the ink of the sprite property to the reverse and will move
the sprite slightly, all done to make the sprite look like a button being
pressed.
In a movie script we write the following:
on buttonClick
myButton = the clickOn
-- make
the button state reversed to look like it is
-- highlighted
-- by changing the ink effect of the
sprite
sprite(myButton).ink = 2
-- move the button horizontally and
vertically
sprite(myButton).locH = sprite(myButton).locH + 4
-- moves the sprite 4 units horizontally
sprite(myButton).locV = sprite(myButton).locV + 4
-- moves the sprite 4 units vertically
updateStage -- updates
image
repeat while the mouseDown
nothing
end repeat
-- move button back to its original
position
sprite(myButton).locH = sprite(myButton).locH - 4
sprite(myButton).locV = sprite(myButton).locV - 4
sprite(myButton).ink = 0
updateStage
end
I can then create a sprite behavior with the following:
on mouseDown me
puppetSound "downSound"
buttonClick
end
The line buttonClick calls all the custom message
in the movie script. The use of the custom handler/message allows variations
of sprite behaviors that share the buttonClick
routine. For example, we may create a behavior that plays a different sound.