Qscript Crash Reference

© Brennan Young 2000, 2002



LiveStage's Qscript is a 'high level' programming language, similar to other popular multimedia and web scripting languages like Lingo or Javascript. It is a comparitively small language which maps the QuickTime 'wired action atoms' provided by Apple onto a simpler, more accessible form.

People who have used scripting languages before will have no trouble entering Qscript. Newcomers to programming may find it a bit of a challenge. One problem is understanding the 'coarse' structure of a scripting language - the grammar. The Qscript reference palette solves many of the usual problems with spelling mistakes by providing drag and drop symbols, but still does not and can not help with constructing your Qscripts.

This document is intended to provide a quick reference to that coarse structure, including a few underdocumented gotchas, so that such people can begin to grasp the grammar of Qscript. I hope also that people with some experience with programming will be able to skim over this document and get up to speed more rapidly.

There is no substitute for trying things out. Be prepared for a certain amount of frustration when you have errors, and terrific exhiliration when you get your scripts working. Start small, even the most meagre Qscripts can have broad effect on a QuickTime movie.



Quick Links


How Qscript relates to QuickTime

Qscript Elements
   Actions
   Expressions
   Targets
   Assignments
   Control Statements
   Declarations
   Comments

Handlers
   Mouse Events
      MouseDown
      MouseUp
      MouseClick
      MouseEnter
      MouseExit
      MouseMoved
   FrameLoaded
   KeyPressed
   ListReceived
   Idle
   Custom Events


Using Expressions
   Expressions With Operators
      Mathematical Operators
      Logical Operators

   Precedence

   Datatypes
      Integer
      Float
      Boolean
      String
      Array
      'Valueless' expressions

   Variables
      Movie Variables
      Global Variables
      Sprite Variables
      Local Variables
      Recursion Issues

   Constants



How Qscript relates to QuickTime
Qscript is not an Apple technology, it was developed by Steve Israelson and the other programmers at totallyHip. Nonetheless Qscript is intimately connected to QuickTime. There is nothing in Qscript which does not have some mapping inside QuickTime, but not every Qscript expression compiles to a wired action. Some are conveniences provided by LiveStage which become constants at the moment the movie is compiled.

Each line of Qscript gets converted to 'wired action atoms' (similar to bytecode tokens) by the LiveStage compiler when you export or preview a movie. These 'atoms' are then interpreted by the QuickTime engine as executable instructions when the movie is loaded into a QuickTime aware container. No trace of your Qscript code remains in the exported movie, only the wired action atoms which it represents.

Wired actions are usually active whether or not the movie is running, indeed, you can make your wired actions do stuff only when the movie is paused and freak out your more conservative multimedia colleagues. In QuickTime Player you can disable the wired actions of a particular Sprite track.



The elements of Qscript
QScript is composed of several basic grammatical parts. In every case a line of Qscript will be composed of one or more of these parts. You can also (and should) include blank lines freely in your scripts to aid readability.

Actions
Actions are instructions which affect the way the various objects in a movie will behave.
An example action is

StartPlaying


Expressions
Expressions are ways to describe data and can take various forms. See Using Expressions below.
An example expression is

((MouseHorizontal - TrackWidth) + 10)


Targets
Targets are ways of referring to different parts of your movie(s), so that you can refer to particular tracks, particular movies or particular sprites. Some actions and properties have default targets, in which case, it is not necessary to specify the target. Other actions and properties will not work, or will work unexpectedly if you do not also supply a target, so it is good practice to do so.
An example target is

TrackNamed("buttons").

Notice the dot (full stop or period) after the target. It is necessary to seperate the target from the action or property by a dot, as is common in other scripting and programming languages.


Assignments
Assignments are special instructions which allow you to create and modify variables. They always take the form

variableName=expression

See Using Expressions below.

An example assignment is

rightBorder = (TrackWidth - 10)

The expression is evaluated first, and the result is assigned to the variable. This means that you may include a reference to the variable itself in the expression. For example;

rightBorder = rightBorder + 10

Compare this use of the = symbol with the equality operator.


Control Statements
Control statements are instructions which change the flow of your scripts. Normally, your scripts are executed line by line, one line after the other. Control statements allow you to skip over or repeat some lines if a particular condition is met. Control statements usually include boolean (logical) expressions (described below).
An example control statement is

IF (MouseHorizontal > TrackWidth)
   TrackNamed("buttons").SetEnabled(TRUE)
ENDIF


Declarations
When you are going to use variables in your scripts, the scope, or 'accessibility' of those variables must be declared beforehand.

An example variable declaration is

SpriteVars speed

Constants, which are similar, need not be declared in your scripts, but must be defined in the 'defines' tab of the main project window.


Comments
Comments are another very useful kind of Qscript which do not really form part of the language itself. They enable you to put descriptions into your scripts so that it is easier to understand what is going on. You may return to the script weeks or even months later and by then you will almost certainly have forgotten what the script was doing.

Comments are a good way to describe, in human language, what is going on. They can also be used to experimentally 'deactivate' lines of Qscript, so that you can see how the movie behaves without those lines.

An example comment is

//This is where the collision detection code starts...

Sometimes it is useful to make a comment which lasts for more than one line. In this case, you may use the multiline comment tag which starts like this

/*

and ends like this..

*/

Note; There is a bug in LiveStage 2 where the auto-colouring of multi-line comments does not work, but apart from this, multi-line comments are treated correctly in that version.




Handlers
Qscript code is placed inside handlers, which are sequences of code associated with particular events. In many cases, the events are generated by the way the mouse interacts with the movie, but there are other kinds of event too. Handlers can be connected to sprites, where they are most powerful, but they can also be connected to VR and text hotspots.



MouseDown
This handler is executed when the mouse button is pressed down when the cursor is over the sprite or hotspot.


MouseUp
This handler is executed when the mouse button is released after being pressed down while the cursor was over the sprite or hotspot, even if the mouse cursor is not over the sprite or hotspot at the moment the mouse button is released.
In other words, a mouseUp event always follows a mouseDown event, no matter where the cursor is when the mouse button is released.


MouseClick
This handler is executed when the mouse button is released when the cursor is over the sprite or hotspot after being pressed down while the cursor was over the same sprite or hotspot.

If the mouse button is pressed when the cursor is outside the sprite or hotspot and then released over it, a mouseClick event will not be generated.

In other words, MouseClick handlers are only executed in sprites and hotspots which have already received a mouse down event.

The mouseClick handler is therefore the best place to put the main actions for 'button' scripts.


MouseEnter
This handler is executed when the mouse cursor moves over the sprite or hotspot.


MouseExit
This handler is executed when the mouse cursor leaves the sprite or hotspot.


MouseMoved
This handler (QuickTime 5 / LiveStage 3 and later only) is executed (often) when the mouse moves over the sprite or hotspot. It is ideal if you want the sprite to do something continually - like pulse or animate - while the mouse is over a sprite.


FrameLoaded
This handler is executed when the sample containing the sprite is loaded into memory. If the sample is removed from memory and then reloaded (i.e. when the playback head moves to another part of the movie outside the scope of the sample and then back again), the handler will be executed again. It is an ideal place to initialise data which will be used by the sprite sample for the duration of its existence.

IMPORTANT
Even though the LiveStage user interface might suggest otherwise, the FrameLoaded event is not sent to individual sprites, but to the sprite sample which contains them. References to ThisSprite, or relying on the sprite being a default target in a frameloaded handler will fail because the scope of frameloaded handlers is technically outside of the sprite itself. If you want a sprite to refer to itself inside a FrameLoaded handler, use the special target form

SpriteOfID($ThisSpriteID)

This special target form is designed so that the preprocessor (which maps defined constants in your Qscripts before the compiler gets to work) will compile the target with the actual ID of the sprite instead.

Useful Tip
This form is a little longwinded to apply rigorously throughout a more complex Frameloaded handler, so a good tip is to call a custom initialisation handler in the sprite itself from the frameloaded handler. This means that you only need to use this special case once for each sprite. Inside the initialisation handler, you can assume that the sprite is the default target for sprite actions and properties, just as you would in any other handler, which makes your scripts quite a bit neater. For example your frameloaded handler can contain only the following line:

SpriteOfID($ThisSpriteID).ExecuteEvent($"init")

...and then in the custom event, you can put the sprite's initialisation code without needing to use the special target form again.



KeyPressed
This handler (QuickTime 5 / LiveStage 3 and later only) is executed when a key is pressed, but it will only be sent to the track/sprite which has keyboard focus. Be sure to set the 'can have focus' checkbox in the track header, and then use the 'setFocus' action to set keyboard focus to a specific sprite. Note also that the key event will pass through to the application unless you also use the EatKeyEvent action.


ListReceived
This handler (QuickTime 5 / LiveStage 3 and later only) is executed when a QTList is received following an asychronous action such as ExchangeList.


Idle
This handler is executed fairly regularly when the movie is not 'busy' doing something else. The idle 'frequency' is actually just the minimum amount of time that QuickTime will delay between sending idle events, and so can not be relied upon for accuracy, but you can specify a preferred delay in the header of the sprite track. The default is 1 idle event every 1/60 second, but in practice it is very rare to achieve this many idle events, particularly on slower computers. In QuickTime 5 and later, and in LiveStage Pro 3 and later, it is possible to change this value at runtime using the 'setIdleDelay' or (deprecated) 'setIdleFrequency' actions.
See also the document Idle Vs. Event Trigger modifiers.

Custom Events
These handlers are not executed, or even present, unless you create them by clicking on the 'New Event Handler' button, and arrange for them to be called explicitly from another handler, or from an event modifier. You can call a custom event by its ID, or (more expressively) by giving it a name and using the name when you call it, like this:

SpriteNamed("AlienController").ExecuteEvent($"StartSwarm")

See also the document Idle Vs. Event Trigger modifiers.

See also the section Recursion Issues.

Request to Modify Movie
This event was included in earlier versions of LiveStage, but has since been removed because it does not do anything.



Using Expressions.
Expressions make scripts more rich and powerful and can be used in various ways. Understanding how to work with expressions is the key to working effectively with Qscript.

The most direct kind of expression is literal data, for example a numerical value;

GoToTime(600)

This action requires a numeric expression as a parameter. Here the literal numerical expression 600 is used to ensure the movie goes to the time exactly 1 second into the movie.

Although some Qscript actions demand literal expressions as parameters, expressions need not always be literal, they can also be Qscript properties, for example;

GoToTime(MaxLoadedTimeInMovie)

In this case, the movie is instructed to go to the time in the movie which has loaded so far. MaxLoadedTimeInMovie is a Qscript property which represents this value.

Expressions can be also be variables which represent data, for example;

GoToTime(x)

In this case, the variable 'x' would be assumed to represent a numerical value. (See 'Variables' below).

Expressions can also be defined constants. These are similar to variables, but they can not change while the movie is running. Constants are described below.


Expressions with Operators
Most importantly, expressions can be combined using operators, which make them even more powerful, for example:

GoToTime((MaxLoadedTimeInMovie - 600) + x)

Mathematical Operators

Most operators are familiar mathematical symbols representing arithmetical operations;

+ Add
Example: 5 + 2 (gives 7)

- Subtract
This can also be used to denote a negative value, which can also be denoted with the operator NEG
Examples:
5 - 2 (gives 3)
-5 - 2 (gives -7)

/ or ÷ Divide
Either symbol is allowable, but the latter is not standard ASCII, and (beware!) in some countries, the ÷ symbol is used for the subtraction operator. If you intend to share your code with other users, or even just put it in a plain ASCII email (for example to the LiveStage Talk list) it is wiser to use the forward slash character (/) instead of the high byte, non-standard ÷.

Note: The result of a division operation may be a floating point number even if the arguments are both integers (whole numbers).

Example: 5 / 2 (gives 2.5)

(See DIV below).

* Multiply.
The 'cross' symbol (×) is not standard ASCII, and it is easily confused with the letter X. Similarly, the 'middle dot' (·), used to signify multiplication in some countries is easily confused with the stop or point character and again is a high-byte / non standard character, so the asterisk (*) is traditionally (and pretty much exclusively) used as the multiplication operator in programming, and Qscript is no exception.

Example: 5 * 2 (gives 10)

There are several other mathematical operators used in Qscript which may be less familiar. They certainly have their uses, but skip over them if they seem obscure at this point. Examples are given to help you understand them;

DIV integer divide.
Whereas / or ÷ may result in a floating point number, DIV is division where the 'remainder' part of the answer is discarded. The result is always an integer.
Example: 5 DIV 2 (gives 2)

REM remainder of integer division.
This gives the 'remainder' part of the answer is discarded after an integer division. The result is always an integer.
Example: 5 REM 2 (gives 1)

ABS absolute value.
This gives the magnitude of a numerical expression regardless of whether it is positive or negative.
Examples:
ABS -2 (gives 2)
ABS 2 (gives 2)

Equality and Inequality Operators
There are also some special operators for logical operations, some of which might seem unusual if you have never used a scripting or programming language before. Some of these operators are common in everyday speech however, so they are very easy to understand, others might be familiar from mathematics.

= is equal
Note. This is not the same use of this symbol as in an assignment statement. Instead it is used to evaluate a boolean expression, most commonly in an 'if' or 'while' control statement, for example

IF( livesLeft = 0 )
 
//Whatever
ENDIF

!= is not equal
Example:

IF( livesLeft != 0 )
 
//Whatever
ENDIF

< is less than

Example: IF( livesLeft < 0 )

<= is less than or equal to

Example: IF( livesLeft <= 1 )

> is greater than

Example: IF( livesLeft > 0 )

>= is greater than or equal to

Example: IF( livesLeft >= 0 )

Logical operators are also usually used in control statements, so that a particular sequence of instructions may or may not be executed if the logical expression is true.

AND logical and
This is used to combine two boolean expressions into a single boolean expression. Both sides of a logical 'and' expression must be true for the whole expression to be true.

OR logical or
This is used to combine two boolean expressions into a single boolean expression. Either side of a logical 'or' expression may be true for the whole expression to be true.

NOT logical not
This is used to reverse a boolean expression. True becomes false and vice versa.


Precedence
Using parentheses to group parts of an expression will influence the order in which they are evaluated. In the example GoToTime((MaxLoadedTimeInMovie - 600) + x), the expression (MaxLoadedTimeInMovie - 600) is evaluated before the variable x is added. This kind of construction can have an enormous influence on the way your expression is evaluated.


Datatypes
Like most scripting languages, Qscript is 'loosely typed'. This means that it is not fussy about what type of data you store inside your variables. Nevertheless, you should ensure that you use the right type of data in the parameters of your Qscript actions. The QuickTime wired engine can convert data types when it is necessary or possible, but can not perform miracles or heuristics.

There are three basic or 'primitive' datatypes.

Integer
Commonly known as 'whole number'. Integers are numerical values with no fractional parts, such as 4 or -3.

Float
Also known as 'floating point number' or 'real number'. Floats are numerical values with a fractional part, such as 0.5 or -99.004

Boolean
This is used in logical operations. Boolean expressions must evaluate to TRUE or FALSE.
(TRUE is identical to the integer literal 1, and FALSE is identical to the integer literal 0).

In addition there are two more sophisticated types, but if you have used other scripting or programming languages, you will notice that support is currently very limited:

String
A string is a sequence of characters, such as a word, a sentence or a url. Strings are denoted by double quote characters at the beginning and end. If you need to include the " character inside a string, use \" in its place and the Qscript preprocessor will handle it properly. String variables can be created using the SetString() action, and appended together using the AppendString() action, which can be very powerful when working with urls. You can also use string literals in your scripts without needing to use the SetString() action.

Array
An array is an indexed series of other values of a fixed size. Arrays are declared in a similar fashion to other variables, and can also have different scopes, but the size of the array must be specified in the declaration:

SpriteVars inputs [10]

Then to access a value in the array, you use arrayName[index] for example

firstinput = inputs[0]

IMPORTANT
Arrays are zero-indexed. This means that the first item in an array will always have the index 0. If you want to use conventional anthropoid finger counting, declare your array to be one bigger than you intend it to be, so that you can start your index at 1 instead. (This is counter intuitive for many people, but has some technical advantages apparently).

'Valueless' expressions
Sometimes you will find that a Qscript property has no value, for example, the number of sprites in a track which does not exist. In many scripting and programming languages, such values are assigned a particular datatype, void, null, nil or some such, or even generate errors. QuickTime has no such datatype, and does not generate error messages when wired actions are nonsensically organised.

So, you have to be careful when assigning a valueless expression to a variable which already has a value because that variable will still contain the value it had before the 'failed' assignment statement. If the variable had no value in the first place, it will still have no value afterwards.

Advanced technique using 'valueless' expressions;
This can actually be used to your advantage. For example, if you wanted to know whether a particular sprite exists or not, you could assign a variable to a known value, then immediately assign it to a property of the sprite you are checking the existence of. If the contents of the variable still contains the old, known value you can be pretty sure that the sprite does not exist.





Variables
Variables are very powerful kinds of expressions. They enable the scripter to use information which will, or may, change over time. Variables are common in human language - we might talk about 'the time' or 'the weather'. Variables allow us to express our intentions in a more general fashion without needing to be specific or literal about the kind of information we expect to be dealing with at any one time.

Just like 'human' variables, Qscript variables have a scope. Scope describes the domain where the information is 'known' or 'seen'. 'The weather' is something different in Vancouver to 'the weather' in Copenhagen. Outside of its scope, the variable is hidden from other scripts, and when the scope no longer exists, the variable is removed from memory altogether. There are four kinds of scope in Qscript variables. Most kinds of variable scope can only be used in sprite scripts, with the exception of MovieVariables, which can also be used in text and VR hotspots.

Movie Variables:
Movie Variables can be used to share data between sprites in different tracks in the same movie. For example:

MovieVars activeAreaH
activeAreaH =
ThisTrack.trackWidth - 20

Advanced use of movie variables:
Because MovieVars are accessible from other movies, they are therefore an ideal way to share data between between movies running in seperate windows in a web browser, or on the same webpage. This is achieved by storing the variable at a particular address.

In the following example the movie variable x, stored at address 10, is assigned the value 256.

MovieVars x : 10
SetVariable(10, 256)

This movie must be exported with a particular 'name' property (look in the info tab under movie identification to set or change the name of the movie.) Let's say we call the movie 'a'. Then to retrieve this data from another movie running in the same container, you would use syntax like this

ThisSprite.MoveBy.(MovieNamed("a").GetVariable(10))

The name of the variable is only relevant in the movie which sets the movie variable. You do not need to worry about the name in the movie which 'gets' the variable, indeed, you do not even need to declare the variable as a variable because it is 'got' through the 'getVariable' action.

Global Variables:
These are accessible to all sprites in the same sprite track. They are therefore an ideal way to share data between sprites, for example, a global variable might be used to store the number of aliens that had been shot in a space game. If any of the aliens got 'hit', it could set this variable to be one less than its current value. Example:

GlobalVars aliensHit

aliensHit = aliensHit + 1


Sprite Variables:
These are accessible to all the handlers in the same sprite. They are therefore an ideal way for sprites to 'remember' their own data independently of other sprites, for example, a sprite variable might be used to store the speed that a sprite was moving. Another sprite with the same scripts could have a sprite variable with the same name, but because of the scope of that variable, it could have a different value. Sprite Variables are used enormously in the construction of behaviors. Example:

SpriteVars mySpeed

mySpeed = 2


Local Variables:
These persist in memory only for the duration of execution of a particular handler. If you need to store some information briefly, for example for a calculation whose result is used in a further calculation, Local scope is ideal. Example:

LocalVars n

n = 1


Recursion Issues
Scope in LiveStage is not as cleanly handled as in most other object based scripting systems. If a custom event handler calls itself recursively (either directly or indirectly), the same local variables will be used in each case. Solutions to this problem might involve the use of arrays, with a global index to signify the 'depth' of recursion.




Constants
Constants are similar to variables in the sense that they are text symbols which represent values, but unlike variables they can not change value while the movie is running. Their value is fixed just before the movie is compiled by LiveStage by a 'preprocessor'. Constants must be declared in the 'defines' tab of the main project window. For example;

kNumAliens = 12
kEndLevelTime = 900


Then when you wanted to refer to these constants in your Qscripts elsewhere, you would use the form $"constantName", for example:

IF(aliensHit = $"kNumAliens")
   
Gototime($"kEndLevelTime")
ENDIF

When you export or preview a movie, the LiveStage preprocessor will run through all your scripts replacing all the constants with the literal value they represent, only then will your Qscript be compiled down to wired bytecode. In other words, the above code would actually be compiled from this;

IF(aliensHit = 12)
   
Gototime(900)
ENDIF

It's very important to realise that you can not modify a constant as you can with a variable. Even so, constants will usually be faster than variables for QuickTime to handle at runtime so there is some advantage to using them in every case where you know that a variable will not change its value at runtime.

By convention, many Macintosh programmers prefix constants with the letter 'k'. You are not in any way required to follow this convention, but some people find that it helps. You will notice that many of the predefined contstants in Qscript are prefixed with the letter k.

 

 

 

 

 


back to

b

a

c

k

s

t

a

g

e