<<home | <<list | <<week 3 | <<week 4

LINGO INTRODUCTION

Key:
Message WindowScriptsMessages and HandlersVariablesGlobal VariablesProperty VariablesMeCreating Navigation BehaviorsLinear ListsProperty ListsCreating Rollover BehaviorsSimple Button BehaviorButton Rollover Tip


Lingo is a programming language. Lingo scripts are usually stored in cast windows.


Message Window

You can also run short, one-line programs in the message window (window -> message). The message window is a lingo interpreter. It enables you to type and interpret one line of lingo code at a time. The message window is nice for taking your first Lingo steps, and it continues to be useful as you learn new Lingo commands.

The put command is actually used to place text into the message window. If you leave put commands in your code when you make a projector or shockwave movie, it does not affect your movie's performance or cause errors. (Note to De: Use text inspector to change font size in message window to show the class on the projector)

Examples

Type the following in the message window (remember to press the return key after each line, not the enter key on the keypad):
put 42 + 1
beep

The double-hyphen indicates a comment. A comment is code that is not executed. You can put comments on the same line as executable code:
put 42 + 1 -- this code adds the two numbers 42 and 1


Scripts
There are four types of scripts:
  1. movie scripts*
  2. behavior scripts**
    • sprite scripts
    • frame scripts
      (attached to frames via the script channel; the most common messages handled by a frame script are enterFrame and exitFrame)
  3. cast scripts***
  4. parent scripts****
The two kinds of scripts you will be using the most are behavior and movie scripts.

Make sure that a movie script is not set to be a behavior script or vice versa. Use the script's cast member properties dialog box to change it to the proper script type.

*Handlers in movie scripts are global in scope, meaning that they can be called from handlers in any of the other types of scripts and the message window. Movie scripts can include handlers that are automatically executed when the movie starts, stops, or idles. Movie scripts can also be used to define and initialize global variables and to store user-defined handlers.

**A sprite can have more than one behavior or script attached to it. A frame can have only one behavior attached to it. Whenever you attach a behavior to a sprite from the library palette, that behavior is added to the cast. When you edit a behavior, that change takes effect in every frame and every sprite to which it is attached.

***Cast scripts are rarely used. They are not used with modern Lingo programming. Behaviors can accomplish the same tasks and are much more flexible.

****parent scripts are a much more advanced type of script. They actually don't do anything until you use some object-oriented programming Lingo commands to tell them how and when they are to be used.


Messages and Handlers

A message is a signal sent by an event. An event is some action that occurs in a program. An event can be the action generated by a user typing or clicking the mouse, or it can be generated within director when the playback head moves, or it can even be generated by a handler.

For example, when the user clicks the mouse button down and then releases it (an event), a "mouseUp" message is sent. Similarly, the initial click down sends a "mouseDown" message.

Tip: Use Mouse Up vs. Mouse Down
User interface standards for Macs and Windows machines specify that actions take place only after the user clicks down and then lifts up again. Otherwise, users can click and hold and the action can happen while they are still holding. It's not a big deal for simple Director projects, but because a standard has been set, its' best to use it.

Messages get sent to any Director object that is related to the action itself. A "mouseUp" message is sent to the sprite that was clicked. If a behavior happens to be attached to that sprite, Director checks to see whether that behavior handles a "mouseUp" message.

A behavior, or any script, handles a message such as this by defining a handler. Handlers start with the word on. A handler is a way of grouping together several lingo statements to perform a single task. In other words, handlers enable you to execute many lines of code with one word. Lingo has many built-in handlers, such as on mouseUp, but you can also create your own custom handlers (see Creating your own handlers below). In the preceding example, the first line of the handler is on mouseUp. This means that the handler is to handle any "mouseUp" messages sent to that sprite.

The contents of the handler, beep, are the lingo commands that are to be executed if the handler is triggered by the message. The series of commands ends with end, which signifies the end of the handler.

on mouseUp
     beep
end


Many programmers like to place the name of the handler after the end. So the last line of the previous example would look like this:

on mouseUp
     beep
end mouseUp


Lingo doesn't require this, but some programmers use it anyway.

You can place more than one handler in a script. For example, you can have an on mouseUp and an on mouseDown handler both in one behavior. They are triggered by different messages, so they won't get in each other's way.

Example One
Create a movie script (Window -> Script Note default is movie script.):
You can change the type of script by selecting Property Inspector -> Script

on playBeep
     beep
end


When you type playBeep in the message window, your playBeep handler is executed and a beep is played.

Example Two
Put a bitmap castmember on the first three channels in the score.

Create another movie script:

on makeInvisible
     sprite(1).visible = FALSE
     sprite(2).visible = FALSE
     sprite(3).visible = FALSE
end


type makeInvisible in the message window

A handler name can't contain spaces. To make these names easier to read, capitalize the first letter of each word after the first word. However, lingo is not case sensitive so "myNumber" and "mynumber" are the same.

Another function of the message window is to enable you to send messages to the movie. Do this by typing the name of the message. If a movie script has handler that deals with this message, the handler runs. Otherwise, an error message tells you that no handler is defined to receive the message.

DO NOT use an event handler name as a custom handler or a variable name. You should also avoid using any other Lingo syntax as handler names or variable names.


Variables

A variable is a storage container for a value. It is like a box that can hold one item at a time. Variables can store numbers or strings (a series of characters). Variable names are only one word, so you can't have spaces in the name.

It is a good idea to assign variables meaningful names, because the name of the variable should give you an idea of the value it holds. You might be tempted to give it a meaningless name, if you are in a rush or not feeling very creative. Don't do it! Although a is a legal name, it won't make much sense to you in a week from now. It'll be even worse for someone else trying to figure out your code.

In some programming languages, the case of a variable is important. This is not true with lingo. Again, lingo is not case sensitive so the variable "myname" is the same as "myName".

In some programming languages, you need to declare a variable before you can initialize it (initialize means to give it a value). In lingo, a variable is created as soon as you use it.

type in message window (remember to press the return key after each line):

myNumber = 42
put myNumber


myNumber = 42+1
put myNumber



myNumber = 5
myOtherNumber = 3
put myNumber+myOtherNumber



a string such as my name must be written with quotes.
myName = "De"
put myName

A string can be one character long or even zero characters long ("").

Variables can be used in handlers as well.
For example, create a movie script:
on playWithVariables
     myNumber = 5
     myNumber = myNumber + 4
     myNumber = myNumber - 2
     put myNumber
end


Then type playWithVariables in the message window.

There are three types of variables:
  1. a local variable is used inside only one handler. It exists only when the handler is being used, and is disposed of when the handler ends. In other words, when the handler finishes, the variable ceases to exist. If the handler is called again, the variable is recreated from scratch. If you created another handler that used the same variable name, it would be a different variable altogether. Each handler is like a little world all to itself. A local variable inside it belongs to it and no other handler
  2. a global variable is shared by more than one handler.
  3. a property variable is declared at the beginning of a behavior or parent script and is available within the handlers within that script. In other words, properties declared at the top of a script window, outside of any handler, can be accessed from any handler within that Script window. The value of a property variable persists as long as the object that contains it exists.


Global Variables

You can declare a global variable in any handler or in any script in a movie. If you want to initialize a global variable, use the global keyword followed by the name of the variable. You can declare more than one global variable on the same line. To easily differentiate between local and global variables, it is recommended that you begin global variables with the lowercase letter g. This is not required, but highly recommended.

For example:
global gMyNewVariable
global gCost, gRetail, gProfit


To use a global variable in a specific script, you must repeat the global statement within the script. Once declared, the current value or string stored by a global variable is available for use and modification within the handler.

You can use the clearGlobals command in the message window or in a handler to erase all global variables. You can also use the showGlobals command in the message window to see a list of all current globals and their values.

For example, create a movie script:

global gMyNumber

on initNumber
     gMyNumber = 42
end

on addOneToNumber
     gMyNumber = gMyNumber + 1
end

on putNumberInMessageWindow
     put gMyNumber
end


Then type the following in the message window:
initNumber
putNumberInMessageWindow
addOneToNumber
putNumberInMessageWindow


To reiterate, a local variable is destroyed when a handler is done. The next time that handler begins, it will not have a value. If you want a variable to retain its value, you should use a global or a behavior property variable.


Properties or Property Variables

A behavior script is an object-oriented programming method. Object-oriented just means that both a program and data are stored in the same place. In the case of behavior scripts, the program is the handlers of the behavior, and the data is the variables used by the behavior. These variables are called properties.

A property is something in between a local and a global variable. A local variable exists only inside a single handler. A global variable exists throughout the entire movie. A property exists throughout the entire behavior script, accessible for all the handlers in the behavior, but not normally accessible outside of it.

You create properties like the way you create global variables. Rather than use a global command, you use a property command. The best place for this is in the first lines at the top of a behavior script. Some programmers like to place a "p" as the first letter of property variables as "g" is a common prefix for global variables. Some developers prefer to place an "i" in front of property variables, to stand for "instance." A "p" or "i" is not required, but highly recommended.

Properties hold the data that is important to the behavior. For instance, if a behavior needs to move a sprite horizontally across the stage, two properties might be "horizLoc" and "horizSpeed". They would correspond to the current location of the sprite and the number of pixels that the sprite moves each frame, respectively.


Using Me

A behavior script by itself is just a bunch of text in a cast member. when you attach it to a sprite, it is still just a bunch of text with which the sprite knows it has a relationship. However, when you run the movie and the sprite appears on the stage, an object is created.

The object, called an instance of the behavior, is sort of a copy of the behavior. The lingo code is loaded into a new location of memory and the property variables are created. This instance is now attached to and controlling the sprite.

If the same behavior is attached to two different sprites, and they both appear on the stage at the same time, they actually have two different instances of the behavior. Both instances have copies of the same handlers, and both have properties of the same names, but the values of these properties are stored in different locations and can have different values.

The idea of an instance needs to be present in your behavior code. Each handler needs to know that it is part of a behavior. To signify this in your code, you should place the parameter me as the very first parameter of all handlers in the behavior. me is a reference to the behavior in the same way that sprite is a reference to a sprite, or that member is a reference to a member.

me has properties, the most useful of which is spriteNum. You can use it to get the sprite channel number of the sprite to which the behavior is currently attached:

For example, create a behavior (or sprite) script:
on mouseUp me
     put me.spriteNum
end


If you don't place the me after the handler name, the script does not work. As a matter of fact, it gives you an error message when you try to close the script window because Director doesn't even know what me is supposed to refer to if it is not included after the handler name as a parameter.


Creating Navigation Behaviors

Previously, we have been creating scripts for every frame we wanted to specifically navigate to.

For example:
on mouseUp
     go to "sally"
end


However, we had to create a different script for each marker we wanted to navigate to. This is time-consuming and repetitive. You can create a behavior that can be used on many different buttons.

For example, create a behavior (or sprite) script:
property pTargetMarker

on getPropertyDescriptionList me
     return [#pTargetMarker: [#comment: "Target Marker:", #format: #marker, #default: VOID]]
end

on mouseUp me
     go to frame pTargetMarker
end


The list in the on getPropertyDescriptionList function is a property list that contains one or more other property lists. (A variable can hold only one value at time, but a list, a collection of items, can store multiple values or strings at the same time. A list can hold any data type that a simple variable can -an integer, a string, a reference to a cast member, even another list. If the data you add is a string, be sure to enclose it within double quotation marks. If the data is numeric, quotation marks are not used.

Lingo supports two types of lists:
  1. linear lists:
    • each element is a single value.
    • For example, this linear list is a simple set of numbers:
      [100, 150, 300, 350]
    • values in a linear list are accessed by their position in the list.
  2. property lists:
    • each element contains two values separated by a colon. One value is a property name, always preceded by a pound (#) sign; the other value is the value associated with that property.
    • For example, the following statement sets the variable myList to a property list containing values for the properties #speed, #direction, and #weight. These could be the properties of an asteroid.
      myList = [#speed: 155, #direction: 237, #weight: 8746]
    • values in a property list are accessed by a property associated with the value.
It's usually easier to manipulate a list by assigning it to a variable when you create the list. The value contained in the variable is actually a reference to the list, not the list itself.

The property name for the first (and only in this case) item of the list is the name of the property, turned into a symbol by adding a "#" in front of it. (Computers deal with numbers much more quickly than strings. Sometimes you want the speed of a number and the descriptiveness of a string. Symbols are lingo's way of satisfying this need.) The list that is its property value contains three items:
  1. The first is the #comment property. This is the string that the parameters dialog box will show for this parameter.
  2. The second item, the #format, tells the parameters dialog box what types of values to accept for this parameter. (pass out handout (gary, p. 268))
  3. The last item, #default, is a default value for this parameter. Because no default really makes sense here, a value of VOID is used.
The syntax for a property list is shown below as a list of test scores:
scores = ["Pat": 1200, "Joan": 1545, "Mary Ellen": 950]

In this example, the names (or strings) are properties and the scores (or numbers) are data. Each string is paired with a value (or data) by a : in between them. The name data pairs are then separated by a comma enclosed in brackets.

Strings are not the best choice for property labels. They are slower to process, but more important, using a string as a property label is the only time that you encounter a case-sensitivity issue with Lingo.

Suppose that you tried to get one of the properties and used the wrong case, such as:
put getProp(scores, "pat")

The result is a syntax error. Most of the time, it is preferable to use symbols, which are not case sensitive.
scores = [#Pat: 1200, #Joan: 1545, #MaryEllen: 950]

You can initialize or create an empty property list named Scores by using this syntax:
scores = [:]

An empty linear list consists of two square brackets [ ].


Reusability is one of the powerful features of behaviors.

To complete this behavior, you should add an on getBehaviorDescription function to it. This function displays some informative text in the Behavior Inspector when the script is added to a sprite:

on getBehaviorDescription me
     return "Jumps to a specified marker on mouseUp."
end


To further enhance your custom behavior, you can create a tool tip, the yellow box that appears when you rollover a behavior in the library palette. These should be as short as possible.

on getBehaviorTooltip me
     return "Jumps to a specified marker on mouseUp."
end



Putting your custom behaviors in your library palette

The library palette is just a collection of external casts that are stored in the "Libs" folder located in the director application folder. An external cast can include any kind of cast member (bitmap, text, script, video, etc). For instance, if you're using a logo that appears in all of your movies, you can put the logo into an external cast and then move it into the Libs folder; it will appear in the Library Palette when you reopen Director.


Creating Rollover Behaviors

Create two button bitmaps with the text "normal" on one and the text "rollover" on the other. Name the cast members respectively "button rollover" and "button normal."Make sure the buttons are different colors.

For example, create a behavior (or sprite) script:
on mouseEnter me
     sprite(1).member = member("button rollover")
end

on mouseLeave me
     sprite(1).member = member("button normal")
end


Test it. Now move the button to another channel. Test again. You will notice that the script no longer works.

How can we make this reusable? First of all, we shouldn't hard-code the number "1" as the sprite channel. What is the syntax that we learned earlier that we can use? me.spriteNum

Modify the behavior (or sprite) script you just created:
on mouseEnter me
     sprite(me.spriteNum).member = member("button rollover")
end

on mouseLeave me
     sprite(me.spriteNum).member = member("button normal")
end


When you test it, you will notice that the cast member names are still hard-coded. You can easily figure out the normal state of a button because it is probably the member with which that button is set to begin with. You can get that member and store it in a property in the on beginSprite handler.

Modify the behavior (or sprite) script again:
property pButtonNormal

on beginSprite me
     pButtonNormal = sprite(me.spriteNum).member
end

on mouseEnter me
     sprite(me.spriteNum).member = member("button rollover")
end

on mouseLeave me
     sprite(me.spriteNum).member = pButtonNormal
end


Now you have only one hard-coded element left: the member of the rollover state. There are three techniques for completing this behavior:
  1. assume that the rollover member is always in the very next cast slot over from the normal member. So if the normal member's cast slot is number 2, the rollover member is in cast slot number 3.
    property pButtonNormal, pButtonRollover

    on beginSprite me
         pButtonNormal = sprite(me.spriteNum).member
         pButtonRollover = member(pButtonNormal.number + 1)
    end

    on mouseEnter me
         sprite(me.spriteNum).member = pButtonRollover
    end

    on mouseLeave me
         sprite(me.spriteNum).member = pButtonNormal
    end


  2. you can use the member names to make it even more flexible. You can get the name of the normal member and append something to it, such as "rollover" for instance.
    property pButtonNormal, pButtonRollover

    on beginSprite me
         pButtonNormal = sprite(me.spriteNum).member
         pButtonRollover = member(pButtonNormal.name&&"rollover")
    end

    on mouseEnter me
         sprite(me.spriteNum).member = pButtonRollover
    end

    on mouseLeave me
         sprite(me.spriteNum).member = pButtonNormal
    end


    Why isn't this working?
    Make sure that the normal button is one word. Remove " normal" (don't forget the space in between) from the cast member name.

    In this example, the name of the normal state is taken and the word "rollover" is appended to it. The double ampersand is used to insert a space between the two. The rollover cast member doesn't need to be placed anywhere in particular in the score as long as it is named appropriately.

  3. The best way to determine what member is used in the rollover state is to set the property in the parameters dialog box each time the behavior is applied. Which handler did we use before could accomplish this.

    Use the on getPropertyDescriptionList handler to do this.

    property pButtonNormal, pButtonRollover

    on getPropertyDescriptionList me
         return [#pButtonRollover: [#comment: "Rollover Member:", #format: #member, #default:VOID]]
    end

    on beginSprite me
         pButtonNormal = sprite(me.spriteNum).member
    end

    on mouseEnter me
         sprite(me.spriteNum).member = pButtonRollover
    end

    on mouseLeave me
         sprite(me.spriteNum).member = pButtonNormal
    end
The value of #bitmap (instead of #member) could have been used to restrict the dialog box pop-up to bitmap members only.


Simple Button Behavior

A simple button behavior should do several things. It should enable you to have a different cast member as a down state when the button is pressed. Second, it should recognize the difference between the mouse bing released above the sprite and it being released ouside the sprite. This enables the user to click down, but then move the mouse so that it doesn't activate the button when it's released. Third, the button should perform a task, such as simple navigation, when there is a succesful button press.

A behavior such as this should have two parameters:
  1. a property that determines the down state of a button
  2. a property that defines the navigation function of the behavior
For example, create a behavior (or sprite) script:
property pMemberNormal, pMemberDown, pTargetMarker

on getPropertyDescriptionList me
     list = [:]
     addProp list, #pMemberDown, [#comment: "Down State Member:", #format: #member, #default: VOID]
     addProp list, #pTargetMarker, [#comment: "Target Marker:", #format: #marker, #default: VOID]
     return list
end


(This list is built property by property, and then returned. This makes the code easeir to read than a one-line return command does as we have been doing. Also note that the #marker #format adds the markers #next, #previous, and #loop which can be used along with the go to frame command to go to the next, preceding, or current marker.)
on mouseDown me
     sprite(me.spriteNum).member = pMemberDown
end

on mouseUp me
     sprite(me.spriteNum).member = pMemberNormal
     go to frame pTargetMarker
end

on mouseUpOutside me
     sprite(me.spriteNum).member = pMemberNormal
end



Button Rollover Tip
if importing from photoshop make sure the image dimensions are the same so that the registration points automatically line up.

compare to: library palette -> interactive -> rollover member change library palette -> controls -> push button


<<home | <<list | <<week 3 | <<week 4