CONVERGENCE

SENSE OF PLACE

ERGODYNAMICS:

WORKBOOK

THEATRE

PROCESS

JUMP

OBJECT ORIENTED

A Rough Guide to Lingo

 

This is written as a quick reference for people who have programmed and have used Lingo a little. It was originally written fro Director 4, however apart from not needing to use 'birth' in 5, the object rules are unaffected. Lingo is not case sensitive.

 

Comments

These are very useful. As in any other programming language, the computer completely ignores all comments, which are used to make notes to yourself and others what is going on. In Lingo a comment starts with a -- and is active until the end of the line.

 

Variables

Need not be defined before they are used. The usual sort of identifier conventions (start with a character, not a number) apply. Lingo appears to allow spaces in symbol names, though I would suggest that you do not put spaces in variable names. Use set or put to create a variable.

set something = something_else

put something_else into something

 

To view the contents of a variable at any time, type

put something

which will put the something into the message window. The put something may be part of a script, or may be typed into the message window.

 

Beware

Because there is no need to declare variables in advance, you will find that odd things happen if you mis-spell a symbol name, e.g.

 

set fred = fref + 1

 

which was supposed to increment the contents of "fred" by 1, will either produce an error or (probably) assume that "fref", being non-existent, has a value of zero.

 

Types

There are various types of variable, but there is no need to tell Lingo what type a variable is. Strings are enclosed in double quotes, numbers are not.

set fred = 20.3

set fred = "Dis am de string"

 

Rules of scope ...or what you can see

Variables are local to a handler, unless you make them global, as in

global fred

Global variables can be seen in any handler in any script, but you should use the global keyword in any handler where you need to access a global variable.

Global variables are useful for holding values which will affect all your scripts.

Property values (of objects) are local to that object, i.e. they can be seen in any handler within the object's (cast) script. They cannot be seen outside the object's cast script.

Many people tend to prefix global variables with a "g" and properties with a "p", so that it is obvious that they are global or properties. I fear that I don't....

The "the" keyword can be used to set/inspect the properties of objects from outside the object's parent script.

put the stageRight --displays stage right in the message window.

the general format is put the property of the instance_of_the_object

 

Handlers

Message handlers process either a system generated event (such as mouseUp), or an event which you create. They are defined as follows

 

on someEvent A, B, C, D ....

..

.. (program code)

..

end

A, B, C etc. are "formal parameters", which you don't need to include, but which are a useful way of passing data to your handler. You can invoke this handler, if it is not for a system event, by

 

someEvent(1, 2, 3, 4)

The (1, 2, 3, 4) are termed "actual parameters", and will be copied into your script's formal parameters, i.e. A becomes 1, B becomes 2 etc. It is the position of the parameters which determines what is copied into where, and you should ensure, of course, that the number of actual and formal parameters are the same. Lingo will not (unlike many other languages) do this for you, and tends to be very bad at telling you that you have done something silly. It tends to sulk and do nothing...!

Function handlers are very similar to message handlers, except that they return a (one only) value as well. You define them as

 

on someOtherEvent A, B, C, D ....

..

.. (program code)

.. calculate something

return something

end

The something is returned by the handler, so you must tell it where you want it to put the returned value to be stored

set fred = someOtherEvent(1, 2, 3, 4)

 

Decisions, decisions

 

if ... then ... else

There are various permutations; see the on line help system.

Repetition

Repeat with

repeat with counter = start to finish

..program code

..

end repeat

This construct us useful if you know exactly how many times you want an action performed. See the help system for examples.

Repeat while

 

repeat while testCondition

..

..program code

end repeat

This construct us useful if you do not know exactly how many times you want an action performed, but the action must be repeatedly performed while some condition holds true. See the help system for examples.

exit and exit repeat can be used to force Lingo to exit either sort of repeat loop.

 

Objects

All an object is, is a collection of data (properties) and program code (handlers, but these are termed methods on object oriented jargon - remember Smalltalk?). As you can create as many instances of the same object as you like, you must pass a parameter to each handler saying which instance of the object you are referring to. You create an object with a birth script. A primitive object might look like as below.

You must tell the object what it is on birth (the me parameter), and where you have placed it in the score (the birthSpriteNum parameter).

property X, Y, spriteNum

on birth me, birthSpriteNum, birthX, birthY --Give birth to me

set X = birthX --at birthX, birthY.

set Y = birthY

return me

end

 

on nextFrame me --compute where to go on next frame - a

set X = X + 20 --Nigel generated event.

set Y = Y + 20

 

set the puppet of sprite spriteNum = true

set the locH of sprite spritenum = X --I believe you know

set the locV of sprite spriteNum = Y --about puppet

set the puppet of sprite spriteNum = false --sprites.

end

 

You can see from the "the" keywords that puppet, locH, locV are all "built in" properties of a sprite. You can use read all and alter most of these properties.

See some separate examples which actually give birth to objects...

Sprites and Cast Members are Objects

Sprites and cast members have a list of properties, many of which you can read or change from various check boxes. You can also use the "the" keyword in conjunction with set or put to read and set most of a sprite or cast member's properties, such as:

set the locH of sprite 3 = 300

 

Properties of Sprites

the .... of sprite .... You can use set or put to read or alter a property.

Sprite properties are below; I have put comments against the properties I use. T/F indicates that the value of the property is true or false only.

backColor

blend

castNum Number of the cast member displayed as your sprite.

editableText T/F

foreColor

height

ink Determines how your sprite is drawn.

lineSize

locH Location horizintal on the stage.

locV Location vertical on the stage.

moveableSprite T/F. True if your sprite is moveable

puppet T/F. If true your sprite is a "puppet sprite" controlled by a script.

scriptNum Script number for the sprite. Can't be set.

stretch

type

trails T/F. T leaves a snail trail.

width

 

Properties of Cast Members

the ... of cast ... Properties are:

backColour

depth Colour depth of cast member, e.g. 256 colours.

foreColour

hilite T/F. Highlights some types of cast member, e.g. Radio Button.

modified T/F

name

number Cannot be set.

pallette

picture

preload

scriptText The text of the script, if any, assigned to the cast member. The scriptText of cast property can be tested and set.

 

Checking to See if your Sprites have Collided

These are two built in functions, sprite ... intersects and sprite ... within.

sprite ... intersects ...

compares the position of two sprites, and returns true of false. See the help system for how the boundary of a sprite is defined.

sprite ... within ...

compares the position of two sprites. It returns true if the bounding rectangle of the first sprite is entirely inside the bounding rectangle of the second, otherwise it returns false. See the help system for how the bounding rectangle of a sprite is defined.

Lists

Lists are extremely useful data structures for creating objects dynamically, i.e. as your movie is playing. You can add as many objects to a list as there is memory for them. If you look in the help system you will find that there are several types of list, the simplest being a linear list.

 

set thisList = [1, 2, 3, 4] Creates a list.

set thisList = [] Creates an empty list.

add thisList, thisThing Adds thisThing to the end of the list thisList.

count(thisList) Returns the number of items in the list thisList.

deleteAT(thisList, number) Removes the item numbered number from thisList.

getAt(thisList, position) Returns the item in the position position in the list thisList.

getPos(ThisList , value) Returns the position of the value specified by value in the list specified by thisList. When the specified value is not in the list, the getPos command returns 0. For values in the list more than once, only the first occurrence is returned.

setAt List, Position, Item Inserts the item into the list at position.

Note: Some of these keywords seem to need brackets, some do not. I think I have the brackets correct here, and if you don't use them correctly odd things happen!! A powerful way to process an entire list is to use the repeat with construct

repeat with variable in someList

..

.. (program code)

end repeat

This keyword assigns successive values from the specified list to the variable. You will a handler in one of my examples which moves all objects (which could be many) by simply:

on exitFrame

global animalList

 

repeat with animal in animalList

nextFrame(animal)

end repeat

end

 

Constraining the Position of a Sprite

This applies more to "drag and drop" sprites. The position of a sprite can be constrained by using a sprite's constraint property.

set the constraint of sprite spriteToBeContained to containingSprite

When the constraint of sprite property is turned on, the sprite specified by spriteToBeContained is constrained to the bounding rectangle of another sprite.

The constraint of sprite property affects moveable sprites, and the locH and locV sprite properties. The constraint point of a moveable sprite cannot be moved outside the bounding rectangle of the constraining sprite.

 

To remove a constraint of sprite property, set it to 0:

Getting the Frame Tempo

the frameTempo will get this (but you cannot set it within a script), and you can use it to set the timeStep variable in most of my examples.

set the timeStep = 1.0/the frameTempo

Beware that if you do not use 1.0, then Lingo will assume that you are using integer arithmetic and return a result of Zero!!

 

Inheritance Using Objects

One powerful feature of this type of programming is that you can create child objects which inherit all the characteristics of their ancestors, except for characteristics you wish to change or add.

To create a child object, you must create a property ancestor, and set the ancestor to the birth script of your ancestor object, like this:

property ancestor --A special property, the ancestor object.

--**** The birth script. As the sprite channel number is passsed ****

--**** as a parameter to this object, we must pass it to the birth ****

--**** script of our ancestor as well. Clear as mud? ****

 

on birth me, birthSpriteNum

set ancestor = birth(script "animalObject", birthSpriteNum)

set the speed of me = 80

set the castNum of sprite (the spriteNum of me) = cast "predatorCast"

return me

end

Our child object has inherited everything from its parent, except for the things which have been changed.

The rules of scope appear to change now. If the ancestor object has properties such as X and Y, you must refer to these properties specifically as the X of me or the X of ancestor. Either seems to be acceptable.

Your child object inherits all the properties and handlers of its ancestor.

If you create a method (handler) with the same name as one in the parent object, your child's new method overrides the parent one.

If you create a method with a name not used in the parent, then you have created a method which is only understood by your child object. There follows an example:....

Animals example ("herbpred.dir")

Below is the family tree of my objects, with their properties and methods (handlers). See the program itself for a more detailed explanation of what each method does.

You can see that "herbivoreObject" inherits everything from "animalObject", and therefore is effectively "animalObject". "ManicAnimalObject" differs from "animalObject" in that its speed and heading are calculated by a different method. "PredatorObject" is the object which differs the most from its parent. It has its own "calcNewHeading" and "calcNewSpeed", and it also has a new method "killAnimal". This object can kill! If you are into violence, then a thought would be to cut and paste this method into the parent "animalObject". All these animals would then inherit the ability to kill.....

 

Other Goodies

Three function handlers which you might want to cut and paste for yourself:

findFirstFreeSprite() Returns the first available sprite (score) position.

calcDistance(X1, Y1, X2, Y2) Returns the distance between the positions X1, Y1 and X2, Y2.

calcBearing(X1, Y1, X2, Y2) Returns the bearing of the position at X2, Y2 from the position X1, Y1.

 

Loosely

The example is based on a global list of animals, "animalList", which starts as an empty list. Animals are added to the list as they are created. Animals are inserted in the next available score position, as found by findFirstFreeSprite. This handler looks for the first sprite with a castNum of zero. Dead (eaten by predators) animals are removed from this list as they are eaten. The castNum of the sprite a dead animal occupued is set to zero, marking that position as vacant.

AnimalObject

Î Travels in a straight line (defined by its "speed" and "heading" properties until is wanders near the edge of the stage.

Î It then always alters course by +0.2 radians, until it has wandered away from the side of the stage. This is a pretty crude rule, try to think of something better.

HerbivoreObject

inherits everything from "animalObject", and is therefore identical.

ManicAnimalObject

Inherits everything from "animalObject" except:

Î It calculates its speed and heading differently. Approximately every 3 frames in 10 it changes its speed and heading to random values.

Î It retains the "off the stage" rule from its parent.

PredatorObject

inherits everything from the parent "animalObject" except:

Î It calculates its heading finding the nearest animal and sets its heading to the bearing of the nearest animal. i.e. it chases the closest animal.

Î It may be borne hungry, or not hungry.

Î If it is hungry it will eat any animal it intersects, and:

Î It removes the eaten animal from "animalList".

Î It increases its size.

Î It moves fster

Î It becomes not hungey

Î If a predator hasn't eaten for 10 seconds, it becomes hungry.

Bugs

You will see that odd things can happen as animals are eaten (removed from "animalList"). I strongly suspect that this is because I do not update the score (remove dead animals, change the "spriteNum" property of the surviving ones accordingly). I have created a global "deadAnimalList" for this purpose, but I do nothing with it at present.....

"Herbpred.dir" Bug fixed!

Simple solutions are often the best. There is now no need for a list of dead animals.

Î As before, the example is based on a global list of animals, "animalList", which starts as an empty list. Animals are added to the list as they are created.

Î Animals are inserted in the next available score position, as found by findFirstFreeSprite. This handler looks for the first sprite with a castNum of zero.

Î Dead (eaten by predators) animals are removed from "animalList" as they are eaten.

Î The castNum of the sprite a dead animal occupued is set to zero, marking that position as vacant.


| CONTENTS | TRANSMISSIONS | MAPS | STUDIO | BOOKLIST | MATERIALS |

| PRODUCTION | SOFTWARE | HARDWARE | LINKS | EMAIL | TUTORIALS | MEDIASPACE |