Torque Script


As I've said before, Torque Script is much like C/C++, but there are a few differences. Torque Script is typeless—with a specific exception regarding the difference between numbers and strings—and you don't need to pre-allocate storage space with variable declarations.

All aspects of a game can be controlled through the use of Torque Script, from game rules and nonplayer character behavior to player scoring and vehicle simulation. A script comprises statements, function declarations, and package declarations.

Most of the syntax in Torque Game Engine (TGE) Script language is similar to C/C++ language, with a high correlation of keywords (see Table A.3 in Appendix A) between the two. Although, as is often the case in scripting languages, there is no type enforcement on the variables, and you don't declare variables before using them. If you read a variable before writing it, it will be an empty string or zero, depending on whether you are using it in a string context or a numeric context.

The engine has rules for how it converts between the script representation of values and its own internal representation. Most of the time the correct script format for a value is obvious; numbers are numbers (also called numerics), strings are strings, the tokens true and false can be used for ease-of-code-reading to represent 1 and 0, respectively. More complicated data types will be contained within strings; the functions that use the strings need to be aware of how to interpret the data in the strings.

Strings

String constants are enclosed in single quotes or double quotes. A single-quoted string specifies a tagged string—a special kind of string used for any string constant that needs to be transmitted across a connection. The full string is sent once, the first time. And then whenever the string needs to be sent again, only the short tag identifying that string is sent. This dramatically reduces bandwidth consumption by the game.

A double-quoted (or standard) string is not tagged; therefore, whenever the string is used, storage space for all of the characters contained in the string must be allocated for whatever operation the string is being used for. In the case of sending a standard string across connections, all of the characters in the string are transmitted, every single time the string is sent. Chat messages are sent as standard strings, and because they change each time they are sent, creating tag ID numbers for chat messages would be pretty useless.

Strings can contain formatting codes, as described in Table 4.1.

Table 4.1: Torque Script String Formatting Codes

Code

Description

\r

Embeds a carriage return character.

\n

Embeds a new line character.

\t

Embeds a tab character.

\xhh

Embeds an ASCII character specified by the hex number (hh) that follows the x.

\c

Embeds a color code for strings that will be displayed on-screen.

\cr

Resets the display color to the default.

\cp

Pushes the current display color onto a stack.

\co

Pops the current display color off the stack.

\cn

Uses nas an index into the color table defined by

 GUIControlProfile.fontColors. 

Objects

Objects are instances of object classes, which are a collection of properties and methods that together define a specific set of behaviors and characteristics. A Torque object is an instantiation of an object class. After creation, a Torque object has a unique numeric identifier called its handle. When two handle variables have the same numeric value, they refer to the same object. An instance of an object can be thought of as being somewhat like a copy of an object.

When an object exists in a multiplayer game with a server and multiple clients, the server and each client allocate their own handle for the object's storage in memory. Note that datablocks (a special kind of object) are treated differently—more about this a little later.

Note

Methods are functions that are accessible through objects. Different object classes may have some methods that are common between them, and they may have some methods that are unique to themselves. In fact, methods may have the same name, but work differently, when you move from one object class to another.

Properties are variables that belong to specific objects and, like methods, are accessed through objects.

Creating an Object

When creating a new instance of an object, you can initialize the object's fields in the new statement code block, as shown here:

    %handle = new InteriorInstance()    {       position = "0 0 0";       rotation = "0 0 0";       interiorFile = %name; }; 

The handle of the newly created InteriorInstance object is inserted into the variable %handle when the object is created. Of course, you could use any valid and unused variable you want, like %obj, %disTing, or whatever. Note in the preceding example that %handle is a local variable, so it is only in scope—or valid—within the function where it is used. Once the memory is allocated for the new object instance, the engine then initializes the object's properties as directed by the program statements embedded inside the new code block. Once you have the object's unique handle—as assigned to %handle, in this case—you can use the object.

Using Objects

To use or control an object, you can use the object's handle to access its properties and functions. If you have an object handle contained in the local variable %handle, you can access a property of that object this way:

 %handle.aproperty = 42; 

Handles are not the only way to access objects. You can assign objects a name that can be used to access the object, if you don't have a handle at hand. Objects are named using strings, identifiers, or variables containing strings or identifiers. For example, if the object in question is named MyObject, all of the following code fragments (A, B, C, D) are the same.

A

    MyObject.aproperty = 42; 

B

    "MyObject".aproperty = 42; 

C

    %objname = MyObject;    %objname.aproperty = 42; 

D

    %objname = "MyObject";    %objname.aproperty = 42; 

These examples demonstrate accessing a property field of an object; you invoke object methods (functions) in the same way. Note that the object name—MyObject— is a string literal, not a variable. There is no % or $ prefixed to the identifier.

Object Functions

You can call a function referenced through an object this way:

 %handle.afunction(42, "arg1", "arg2"); 

Note that the function afunction can also be referred to as a method of the object contained in %handle. In the preceding example, the function named afunction will be executed. There can be multiple instances of functions named afunction in a script, but each must be part of different namespaces. The particular instance of afunction to be executed will be selected according to the object's namespace and the namespace hierarchy. For more about namespaces, see the sidebar.

start sidebar
Namespaces

Namespaces are means of defining a formal context for variables. Using namespaces allows us to use different variables that have the same name without confusing the game engine, or ourselves.

If you recall the discussion in Chapter 2 about variable scope, you will remember that there are two scopes: global and local.Variables of global scope have a "$" prefix, and variables of local scope have a "%" prefix. Using this notation, we can have two variables—say, $maxplayers and %maxplayers— that can be used side-by-side, yet whose usage and meaning are completely independent from each other. %maxplayer can only be used within a specific function, while $maxplayer can be used anywhere in a program. This independence is like having two namespaces.

In fact, %maxplayer can be used over and over in different functions, but the values it holds only apply within any given specific function. In these cases, each function is its own de factonamespace.

We can arbitrarily assign variables to a namespace by using special prefixes like this:

 $Game::maxplayers $Server::maxplayers 

We can have other variables belonging to the namespace as well:

 $Game::maxplayers $Game::timelimit $Game::maxscores 

The identifier between the "$" and the "::" can be completely arbitrary—in essence it is a qualifier. By qualifying the following variable, it sets a context in which the variable is meaningful.

Just as functions have a de facto namespace (the local scope), objects have their own namespaces. Methods and properties of objects are sometimes called member functions and member variables. The "member" part refers to the fact that they are members of objects. This membership defines the context, and therefore the namespace, of the methods and properties (member functions and member variables).

So, you can have many different object classes that have properties of the same name, yet they refer only to the objects that belong to that class. You can also have many different instances of an object, and the methods and properties of each instance belong to the individual instance.

In these examples:

 $myObject.maxSize $explosion.maxSize $beast.maxSize 

the maxSize property could have three entirely different meanings. For $myObject, maxSize might mean the number of items it can carry. For $explosion, it might mean how large the blast radius is. For $beast, it might mean how tall the creature is.

end sidebar

When an object's function is called, the first parameter is the handle of the object containing the function. Therefore, the function definition of the afunction method in the preceding example would actually have four parameters in its parameter list, the first of which will be the %this parameter. Note that only the last three parameters are used when you call the afunction method. The first parameter that corresponds to the %this parameter in the definition is automagically inserted by the engine when you call the function. You may be familiar with the this token in C/C++; however, in Torque there is nothing special about it. By prior convention, that variable name is often used when referring to an object's handle within one of its methods, but you could call that parameter anything you want.

If you want to access a field of an object, you always have to use something that evaluates to an object handle or a name followed by a dot followed by the field name, as in the A, B, C, and D code fragments seen earlier. The only exception to this rule is in the sequence of field initialization statements when creating an object with the new statement.

Datablocks

A datablock is a special kind of object containing a set of characteristics that are used to describe another object's properties. Datablock objects exist simultaneously on the server and all its connected clients. Every copy of a given datablock uses the same handle whether it is on the server or a client.

By convention, datablock identifiers have the form NameData. VehicleData, PlayerData, and ItemData are all examples of datablock identifiers. Although datablocks are objects, we typically don't explicitly call them objects when referring to them, in order to avoid semantic confusion with regular objects.

A VehicleData datablock contains many attributes describing the speed, mass, and other properties that can be applied to a Vehicle object. When created, a Vehicle object is initialized to reference some already-existing VehicleData datablocks that will tell it how to behave. Most objects may come and go throughout the course of the game, but datablocks are created once and are not deleted. Datablocks have their own specific creation syntax:

 datablock ClassIdentifier(NameIdentifier) {       InitializationStatements }; 

The value of this statement is the handle of the created datablock.

ClassIdentifier is an existing datablock class name, like PlayerData. NameIdentifier is the datablock name you've chosen. In both cases you must use valid identifiers. Initialization-Statements is a sequence of assignment statements.

The assignment statements assign values to datablock field identifiers. It's possible for the contents of these fields to be accessible by both the script code and the engine code—and in fact that is often the case. In that situation you, of course, need to assign a value to the field that makes sense for the type of information it's supposed to be holding.

You don't have to restrict yourself to only initializing (and later using) fields that are accessible by the engine code. An object can have other fields as well; the engine code can't read them, but the scripts can.

Finally, note that there's a variation on the datablock creation syntax:

 datablock ClassIdentifier(NameIdentifier : CopySourceIdentifier) {       InitializationStatements }; 

CopySourceIdentifier specifies the name of some other datablock from which to copy field values before executing InitializationStatements. This other datablock must be of the same class as the datablock you are creating, or a superclass of it. This is useful if you want to make a datablock that should be almost exactly like a previously created datablock (with just a few changes) or if you want to centralize the definitions of some characteristics in one datablock that can then be copied by multiple other datablocks.




3D Game Programming All in One
3D Game Programming All in One (Course Technology PTR Game Development Series)
ISBN: 159200136X
EAN: 2147483647
Year: 2006
Pages: 197

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net