Director Tutorials

 

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.