Notes On Torque Game Engine
Notes On Torque Game Engine
Created 10/14/06
Updated 11/12/06, Updated 11/26/06, Updated 12/02/06, Updated 12/10/06, Updated 12/17/06, Updated 12/23/06
Updated 12/29/06, Updated 12/31/06, Updated 01/13/07, Updated 01/20/07, Updated 08/08/07, Updated 11/22/07
Updated 12/01/07, Updated 01/12/08, Updated 01/20/08, Updated 01/23/08, Updated 01/27/08, Updated 01/30/08
Updated 02/10/08, Updated 04/04/08, Updated 05/13/08, Updated 05/20/08, Updated 05/25/08, Updated 05/27/08
Updated 06/03/08, Updated 06/07/08, Updated 06/27/08, Updated 03/24/09
Introduction.....................................................................................................................................................................2
Critical Updates..........................................................................................................................................................3
Current Version and History.......................................................................................................................................3
Resources....................................................................................................................................................................4
Basic Principles...............................................................................................................................................................5
Structure of a Project (Game).....................................................................................................................................5
Game Playing Mode vs. Game Editing Mode............................................................................................................7
Moving Cameras and Players in the Editing Environment.........................................................................................7
Terrain.........................................................................................................................................................................8
Objects, Groups, and their Editors..............................................................................................................................9
Mirroring the Mission Area......................................................................................................................................12
Modeling and Textures..............................................................................................................................................12
Sun and Sky..............................................................................................................................................................12
Using Water Blocks..................................................................................................................................................13
Using the fxShapeReplicator and fxFoliageReplicator.............................................................................................14
Defining Paths...........................................................................................................................................................14
Using the Lighting Objects.......................................................................................................................................15
Introduction to Scripting...............................................................................................................................................16
Using the Console Window......................................................................................................................................17
Variables....................................................................................................................................................................17
Operators and Control Structures..............................................................................................................................17
Collections................................................................................................................................................................18
Objects......................................................................................................................................................................18
Packages....................................................................................................................................................................19
Namespaces...............................................................................................................................................................20
Datablocks.................................................................................................................................................................21
Scripting for Game Objects......................................................................................................................................22
Engine Overview..........................................................................................................................................................23
Basic Control Flow...................................................................................................................................................23
Scripting and the Game Engine runtime...................................................................................................................23
Datablocks, Objects, and Namespaces Revisited.....................................................................................................26
The Particle System......................................................................................................................................................30
Precipitation and other Environment Objects...............................................................................................................33
Platform Layer Facilities...........................................................................................................................................34
Commonly Used DataBlocks........................................................................................................................................36
SimDataBlock...........................................................................................................................................................36
ItemData....................................................................................................................................................................36
Built-In Datablock Base Classes and their Hierarchy...............................................................................................37
Creating and Editing the User Interface........................................................................................................................38
Special Combinations of GUI Objects......................................................................................................................39
Binding Keys in Torque with ActionMap.................................................................................................................39
Playing Sounds..........................................................................................................................................................40
Using the Type System in Scripts.............................................................................................................................43
Creating Triggers in Torque......................................................................................................................................44
Scripting Examples for Games.....................................................................................................................................46
Translating/Rotating/Scaling Objects under Script Control.....................................................................................46
Treasure Hunt Example............................................................................................................................................47
Page 1 of 71
Animation Example..................................................................................................................................................47
Teleporter Example...................................................................................................................................................47
Racing Example........................................................................................................................................................48
Door Example...........................................................................................................................................................49
Weapons Example.....................................................................................................................................................49
Players and Bots........................................................................................................................................................50
Weapons Damage Example......................................................................................................................................50
Inventory Management.............................................................................................................................................51
World/Screen Management.......................................................................................................................................53
RifleScoping.............................................................................................................................................................53
Capture the Flag Example.........................................................................................................................................53
Simple AI in the Torque Game Engine.....................................................................................................................54
Deploying Games as Executables.................................................................................................................................58
Appendix A: Generating .DTS files.............................................................................................................................58
Using MilkShape3D and Exporting to DTS.............................................................................................................59
Using trueSpace and Exporting to DTS using Version 1..........................................................................................60
Using trueSpace and Exporting to DTS using Version 2..........................................................................................61
Using Torque ShowTool Pro.....................................................................................................................................64
Appendix B: Generating .DIF Files..............................................................................................................................65
Using QuArK............................................................................................................................................................65
Using trueSpace and the Level Exporter..................................................................................................................66
Appendix C: Introduction to the Terrain Terraformer..................................................................................................68
Appendix D: Introduction to the Terrain Texture Editor.............................................................................................69
Appendix E: Working Notes and Resolved Issues.......................................................................................................69
Plan for MOAH class....................................................................................................................................................70
Introduction
The Torque Game Engine (TGE) is a leading example of the software platform that is at the core of a 3-D game
system, similar to that used in Quake, Doom, World of Warcraft, etc. It was specifically used for the Tribes and
Tribes 2 first-person 3-D shooter games developed during the late 1990s by Sierra Dynamix. Since that time, the
GarageGames Company was spun off to market the engine as Torque and create a set of related development tools
and content packages.
The Torque Game Engine has very advanced rendering of shapes, interiors, terrain, lighting, weather, etc, plus it
supports models of varying levels of detail, an advanced particle system, and has a physics engine to handle gravity,
trajectories, and collisions. Its organization has a server portion with contains the scripting engine and the rendering
engine, a client portion that contains a user interface, and a set of editor/creator tools. The separation of the server
from the client allows for the setup of game servers such as those used for Tribes or Doom.
Examples of games done with TGE include TubeTwist, ThinkTanks, Golden Fairway, Dark Horizons, and Minions
of Mirth. These go beyond the standard games of having a terrain and a warrior moving across it.
The TGE requires a graphics card with texture and lighting capabilities. We are running it on our T42 and T43
computers, which each have a 32MB ATI Mobility RADEON 7500 card. Our T60 has a 64MB ATI Mobility
RADEON x1300 card, and it works even better on the T61. At the low end, we found the Intel Graphics Accelerator
board produced good results.
TGE is licensed at very competitive pricing on an indie basis for independent game developers and hobbyists (for
small companies or those funding their own game development), and more expensive commercial basis for
professional games or those generating revenue above $250,000. We learned about Torque when doing a research
project into game engines in 2005 after trying DarkBasic, and it was also used by Brandon at the iD Tech Gaming
Academy during summer 2006.
Page 2 of 71
Critical Updates
In mid-2008, Garage Games introduced Torque Game Engine Advanced. The price for indies is $295, rather than
$150. They still list the regular TGE at http://www.garagegames.com/products/tge.
In mid-March 2009, Garage Games introduced Torque 3D. The description indicates that it includes Terrain, web
publishing, genre kits, road and path editing, and river editing. The introductory pricing page is
http://www.garagegames.com/products/torque-3d/preorder
The introductory pricing is $250 per developer for Basic, $1000 per developer for Professional. But there is a pre-
order discount of $200.
The source code (C++) is shipped with a purchase of the engine. Having the source code provided is a major selling
point of the Torque product, and is intended to allow for the development of better editing tools, and enhancements.
There is also a variety of content provided with the game, packaged to drive the demos, but this can also be
extracted and moved into your own games.
Integrated Torque Lighting Kit not only is this merged into the TGE 1.5, it is also a re-factored lighting
system so that it is cleaner and easier for game developers to use, and provides better support for
integration of custom lighting models.
Huge Mac Update All of the OSX platform updates integrated into the latest version of Torque Game
Builder have also been integrated into TGE 1.5.
Torque ShowTool Pro ShowTool Pro is included in the purchase of TGE 1.5. This is a viewer to see
where the nodes are within a DTS shape and watch them as they animate. This was previously a separate
purchase.
Vastly improved interior rendering performance with dynamic lights
Improved dynamic light blending on terrains
Updates to the TGE waterblock system that enable it to work for terrains with smaller block sizes, allowing
for higher geometry and texture detail
Support for Visual Studio 2005 and Visual Studio 2005 Express
Mac platform supports Universal Binary for running TGE on both PowerPC and Intel-based Macs
Enabled Unicode and Threading support on Windows 98 and Windows ME
Added the Max2dts Pro Exporter source code
Integrated fxRenderObject to serve as an example for renderable SceneObjects
Integrated the Volume Lighting special effect
Added an enhanced version of RigidShape for doing simple rigid body physics in a generic way
Added the ability to have multiple keybinds bound to the same script function and updated the
optionsDlg.gui to allow this
New multithreaded profiler!
Brought engine and gui code that is shared with TGB up-to-date.
Updated the Telnet Debugger with all of the latest bugfixes and enhancements.
Update for magicButton/InteriorInstance::addChildren(). You can now instantiate SceneObject entities as
well.
Updated the Axis Gizmo to be prettier and a little easier to see/use with some code from Constructor
Page 3 of 71
New Content
Elf Character
Redone Starter.FPS Environment & Mission (Includes Skybox, terrain, etc.)
Redone Starter.Racing Environment & Mission (Includes Skybox, terrain, etc.)
Redone Interactive Walkthrough Environment & Mission (Includes Skybox, terrain, etc.)
Replaced Trees & Rocks with new ones (Courtesy Games Extract)
Redone buildings (Monument, Orc Tower, Hovel)
5 Starter Skyboxes (Courtesy of Red Thumb Games)
Redone Starter.FPS Audio (Courtesy of Dark Combat Audio)
RTS Environment Pack, Winter Environment (Courtesy Todd Pickens)
Tim Aste Environment Pack #1
Tim Aste Combo Pack #2 Particle FX Sampler
Foliage Sampler from FPS Environment Pack (Courtesy Todd Pickens)
Redone GUI from Interactive Walkthrough
Important Bugfixes
Fixed it so that ParticleEmitters will have a properly sized bounding box. This greatly improved engine
performance and ParticleEmitters no longer disappear at odd viewing angles.
The console scripting system can now properly interpret floating point numbers that are not prefixed with a
0 (i.e. .123 as opposed to 0.123).
Objects moved with the Axis Gizmo in the Mission Editor now properly show their new transform in the
Inspector Pane
Re-enabled the ability to move WaterBlocks in the Mission Editor
Dedicated server now runs properly
Fixed a very non-obvious StringStack corruption bug in GuiInspectorField::setData()
Password Edit Field fixed
Fixed the ResourceManager extension registration for ".uft" to point to constructNewFont
Improved axis selection in the Mission Editor
Fixed networking issues with Explosions (they weren't ticking or cleaning themselves up properly in client-
only situation)
Proper alt-tab fix for Windows
GuiBitmapButton now looks for the given texture name to use as the "normal" texture before searching for
texturename_n
Fixed mirror surface support for Map2dif Plus
Resources
The primary resource for information is the Torque web site, which for registered customers provides online access
to the Torque documentation. This is about 1000 pages long, and is organized into a large set of HTML pages by
topic and subtopic. The URL is http://www.garagegames.com/docs/tge/index.php. There is not a downloaded copy
provided with a Torque download/install (nor it is easy to download the pages from the GarageGames site, so we use
it on-line). However, these online pages havent been updated since about 2005, and dont cover the details of the
GUI objects or the particle system.
The newest book is Torque for Teens, by Mike Duggan. Course Technology PTR, 251 Pages, October 2007. List
price $29.99, Amazon price $19.79, used from $18.43. Rated 3 stars on Amazon. This book is considered to be
approachable, detailed and well-delivered, each step building on what has been previously covered. No prior
knowledge except a basic understanding of computers is assumed. Excellent for teenage readers and game
developers. Comments were scattered, as some readers found problems with the supplied code.
Page 4 of 71
The Game Programmers Guide to Torque: Under the Hood of the TGE, by Edward Maurina III. A.K. Peters
(UK), March 2006, 600 pages. List price $64.00, Amazon price $57.60, used from $37.50. Rated 3.5 stars on
Amazon. This is considered by some reviewers to be a complete reference, while others state that it only covers
TorqueScript. The pace and organization is considered to be excellent. We have never seen the book in a Silicon
Valley bookstore, but major parts of it are searchable and provided online through Google Books.
Multiplayer Gaming and Engine Coding for the Torque Game Engine, by Edward Maurina III. A.K. Peters,
March 2008, 450 pages. List price $64.99, Amazon price $54.33, used from $53.20. Rated 4 stars on Amazon.com.
Considered to not be as good as his first book, but this covers a much-needed area of understanding for the Torque
Game Engine.
There is also a support forum for Torque run by GarageGames. It is quite active and has code examples and screen
examples.
The other category of resources is Content, for which GarageGames sells a range of packs, typically in the $30 to
$100 price range. These include castles, interiors, buildings, birds, animals, warriors, soldiers, and animated
characters. Many of these appear very well-executed from the examples page, though we have not bought any as
yet.
The TGE 1.5.2 description states that it includes the Tim Aste Environment Pack #1, which includes:
(Most of this content has been used in the starter games and example games, and isnt collected anywhere else.
While the environment files help, what would really make a difference is if we had more buildings, so we began
creating some during January 2008).
Page 5 of 71
Basic Principles
Structure of a Project (Game)
The fundamental parts of a game are one or more terrain definitions, one or more screen definitions, and a player
object. This provides for a player or camera moving over terrain. The terrain typically has a sky dome and a sun.
The next-most important parts are game objects, which come in a wide variety of forms. The simpler ones are
Static Shapes that are placed on the terrain and can be collided with, but have no behavior. The next more
complex ones are Shapes, which combine a visual representation of an object on the terrain, but with behavior that
can include picking them up. Such shapes are used for inventory items, projectiles, etc., and may be created and
destroyed during game execution. Then there are mission objects which all have special behaviors or
visualization. Examples of these include triggers, particle emitters, and paths. Finally, there are interior objects,
which are shapes that are placed on the terrain and can be walked inside. These typically do not have behavior, and
are not created or destroyed during game execution. Examples would be buildings or bridges.
In the game editor, these objects would appear in the browse tree as follows:
This shows the root of the game as being the MissionGroup, and that contains mission info, a mission area, a sky, a
sun, a terrain, etc.
At the bottom are two game objects, an octahedron and a logo, to be discussed below. The octahedron is actually
from the Static Shape menu, and the logo is from the Shapes menu and has behavior.
client: directory. This contains all of the scripts associated with the user interface. It also contains the .gui
files that define the layout of the user interface screens.
Page 6 of 71
server: directory. This contains all of the scripts associated with the actions of the engine itself, such as
handling physics, rendering output, loading meshes, etc. In a multi-user game, there will be one instance of
the server and several instances of the client, so the shared game state must go here in the server.
data: directory. This contains all our resources, like models, textures, sounds, and few scripts that deal
directly with those resources. Has subdirectories terrain, sounds, skies, shapes, missions, etc. These are
used by both the client and the server.
main.cs: primary initialization instructions for the game. Has methods onStart, onExit, initClient,
initServer, and loadMyMission
To create a game, we have been starting by making a copy of the tutorial.base directory, so that all portions are
already in place.
Then, we edit the main.cs file in the directory from which Torque is started, and change the value of $defaultGame
to the name of the new directory.
Note that the Finney book series uses a different structure, in which the client, server, and data directories are under
a directory called control. There are also refinements, used in the starter FPS, in which the server directory
contains a directory called scripts, as does the client directory.
In game editing mode, you have several different windows available which provide different tools. First screen is
the World Editor (F2). This appears to be simply a navigator to allow you to move around the terrain without the
right sides being taken up by the object tree or the property sheet, etc. It does provide a quick way of seeing the
number of game objects in the game and their placement, as the center point of each will be marked with a red
square plus an id number and a name.
Second window is the World Editor Inspector (F3), which is mostly for changing attributes of objects. This provides
an object tree view in the upper right pane. It can be expanded and collapsed. Objects can be selected from this tree
view, and objects can be dragged and dropped to change their hierarchy. The lower right pane is a property sheet.
This editor will be discussed in more detail below. For now, type in new values and press the Apply button.
Third window is the World Editor Creator (F4). This has the same object tree view in the upper right, but in the
lower right is a tree of known objects types/classes. Click on one to create an instance. The organization of this list
represents a class hierarchy within the data type system, and will be discussed below.
Fourth window is the Mission Area editor (F5). The Area Editor provides the ability to select the size and location
of the mission bounds (or area). Strangely, it also provides a terrain editing feature. It should be noted that
manipulating the mission area seems to have some side-effects. In particular, when you re-center the mission area, it
tends to move the existing interiors and shapes in the mission. The problem is that this movement seems to be
inconsistent. So, if you are going to adjust your mission area, do it before you place objects in your mission, or you
may be very upset at the outcome.
The Player position, which is typically along the ground surface somewhere
The Camera position, which may be the same as the player position, or may be detached and separate.
Page 7 of 71
The Camera menus Drop Camera at Player command (Alt Q) changes the current view to the cameras view, and
sets the cameras position to be at or near the player. After carrying out this command, you can move the camera
anywhere in 3-D space, including going up into the sky and rotating to look down.
The Camera menus Drop Player at Camera command (Alt W) changes the current view to the players view, and
sets the player position to be at the camera. Since the camera may be high up, the player and the current will start
there and drop to the ground.
The Camera menus Toggle Camera command (Alt C) changes the current view from the cameras view to the
players view or vice versa.
Terrain
Though the terrain editing tools are listed on the Window menu before the World Editing tools, it is preferable to set
up the terrain before creating or placing game objects, since their placement height will be based on the terrain.
In general, all Torque games are built around a terrain map (exceptions are discussed below), which can have
regions raised and lowered. Internally, the terrain is represented as a grid of height numbers (from 0 to positive
infinity, though a typical range is 0 through 200 to 300), and texture palette index numbers. The terrain height
numbers are stored in the <missionName>.ter file.
To change the terrain, use the brushes, somewhat like in Photoshop. The actions are:
Add Dirt: raises the area, forming a dome if the soft brush is being used, otherwise it will form a cylinder
up
Excavate: lowers the area, forming a crater if the soft brush is being used, otherwise it will form a cylinder
down
Adjust Height: can move up or down with the mouse
Flatten: sets all cells in the brush to the average of those cells
Smooth: smoothes out differences from cell to cell, raising some and lowering others
Set Height: sets all vertices to pre-selected height. The explanation about how to set the height is below.
The brushes are different sizes, and can be hard edge or soft edge. For most of our work, we use a 5x5 brush, with
soft edges.
The text beside the label Mouse Brush indicates the following:
It shows how many vertices are currently under the brush. However, this does not always match the product
of the brush size dimensions, and we dont know why.
It shows the average elevation of the vertices under the brush. This very handy for reading off the
numbers.
A different approach to controlling what portions of terrain is altered is to use selections of vertices. To get into
Selection Mode, just open the action menu and click Select. Now, you can select terrain as follows:
Click a vertex to select it
Control-click to deselect it.
Having selected the terrain blocks that we wish to modify, we can open the action menu and click Adjust Selection.
Now, we can right-click and drag up-down to raise-lower the elevation of the selected blocks.
To leave selection mode, select any other operation in the action menu. Also, once selected, vertices stay selected,
regardless of mode. If you wish to de-select all selected vertices press CTRL + N or click Select None in the Edit
menu.
Page 8 of 71
It is suggested that you start with a terrain map that is all 100 units high. That way, you can create excavations as
well as hills. Two approaches to set all the terrain to 100 is to use the Set Height command manually across the
entire area, or to use the Terrain Terraform Editor with one Generator operation and an Overall settings of min-
height=100, and heightrange=0. Of course, if you are going to run the Terraform Editor, you might as well use the
other facilities in it to create some interesting canyons and patterns. This is described in Appendix C.
Brush hardness has been mentioned several times but not completely explained. When the brush hardness is set to
Soft, the action strength along the diameter of the brush can be modified. In simple terms, if the strength of action is
set low, then the value change for that part of the brush is also low. Vice versa, if the strength of action is set high,
the value change for that part of the brush will be high. This attenuation is in relation to the movement of the
mouse. The brush gives strength of action feedback through coloration. Brush coloration is a continuous scale from
RED to GREEN. You can manipulate this hardness in the Terrain Editor Settings dialog found under the Edit Menu.
In addition to being able to adjust brush shape, hardness, and size, the Terrain Editor Settings Dialog, found under
the Edit menu, gives us some additional control, including the Set Height value.
After generating your terrain, you then assign terrain tiles using the Terrain Texture Painter window. You can build
up a palette of 6 different tiles for use in the terrain. Click in the Add buttons for the windows to the right side of
the screen, in sequence down the columns.
In addition to the terrain texture, there is a detail texture, which is global for the terrain object. This will be used
when painting the nearby terrain, and is like a bumpmap which alters the appearance of the main terrain texture. For
instance, it might add cracks, lines, or shadows into the ground. In the texture packs that come with Torque 1.5,
such as the Environmental packs, there are specific .jpg files that are intended for use as detail textures.
To create terrain with a hole in it (so that you can pass through into an interior) use the Set Empty action in the
terrain editor. There is also a Fill Empty action.
This is a good time to point out that the .jpg or .png files to be used as textures must be square and a power of two in
size.
After making changes, you must save the file with the File menus Save Mission or Save Mission As
commands.
Each game object has a name and a unique item number. The item numbers are assigned by the editor, and name
defaults to null.
Create game objects with the World Editor Creator window (shortcut is F4). This has a list of creatable item types in
a tree at the lower right. Select the item type. Then move/resize/rotate it by dragging the red, green, or blue arrows.
This is explained in more detail below.
Page 9 of 71
Shapes, which is directory list of the createable datablock classes, organized by an attribute in those
classes called category. The means of defining and organizing the datablock classes will be described in
more detail below.. These objects have scriptable methods, as well as a reference to the shape file to
display. The simplest such datablock class is called StaticShape, which is confusing because it makes the
object appear to have been made through the StaticShape item type listed above.
The create tool is used to create (or place) new content. From the World Editor Creator we can select objects to
insert into our current mission.
Edit the attributes of an object with the World Editor Inspector window (shortcut is F3). First, select an object from
the tree on the upper left, then use property sheet. Make a change, and then press Apply. Important: do not create
names with spaces or special characters in them
The properties are organized into sections on the property sheet. These are useful in that they arrange a complex
property sheet. Consider the Sky object, for instance, as it has sections for Transform, Media, Clouds, Visibility, etc.
Expand and collapse the section of the property editor by clicking on the title of the section. The last section is
always called dynamic fields, and is a set of ad-hoc properties, to be described below.
To scale an object, Control-Alt-click on the red/green/blue arrows. Actually, another approach is that you just click
on a face, and extrude that out.
To lock an object so that it cannot be accidentally moved, rotated, or scaled through the mouse, create a dynamic
attribute called locked, with the value true. Note that it is still possible to alter a locked object through numerical
changes in the World Editor Inspector.
To delete an object, use the World menus Delete Selection command. The shortcut is to press the Delete key,
but this does not always work.
To place multiple game objects into a group create a Mission/Group, and then move objects into it by dragging
them. An object can be in only one group.
Learn to use all of the editing tools available, since the editors will save you considerable time in the game
development. They are writing a complex script for you.
To jump the view to a selected object, use the Camera to Selection command on the World menu. Other
commands on that menu allow you to hide, show, lock, and unlock the selected object, which can be very useful
when working with complex worlds of lots of objects. The hide facility allows you to reduce onscreen clutter, and
the lock facility allows to you prevent accidental changes.
Since both terrain information and object information are stored in matched files, the same rules for saving content
apply. As with the discussion above regarding terrain, after making changes, you must save the file with the File
menus Save Mission or Save Mission As commands.
Page 10 of 71
The object information part of the resulting files is clear text, and consists entirely of calls to new, and
initialization of attributes. Here is an example:
new SimGroup(MissionGroup) {
canSaveDynamicFields = "1";
new ScriptObject(MissionInfo) {
name = "New Mission";
desc0 = "A simple new mission template.";
descLines = "1";
};
new MissionArea(MissionArea) {
canSaveDynamicFields = "1";
Area = "-360 -648 720 1296";
flightCeiling = "300";
flightCeilingRange = "20";
locked = "true";
};
new Sky(Sky) {
canSaveDynamicFields = "1";
position = "336 136 0";
rotation = "1 0 0 0";
scale = "1 1 1";
materialList = "creator/data/skies/sky_day.dml";
};
new TSStatic(campfire) {
canSaveDynamicFields = "1";
position = "-31.3594 -111.232 73.6632";
rotation = "1 0 0 0";
scale = "1 1 1";
shapeName = "~/data/shapes/campfire/campfire.dts";
receiveSunLight = "1";
receiveLMLighting = "1";
useAdaptiveSelfIllumination = "0";
useCustomAmbientLighting = "0";
customAmbientSelfIllumination = "0";
customAmbientLighting = "0 0 0 1";
};
new ParticleEmitterNode(campfireFlames1) {
canSaveDynamicFields = "1";
position = "-31.7859 -110.52 74.2241";
rotation = "1 0 0 0";
scale = "10 10 10";
dataBlock = "FireParticleEmitterNode";
emitter = "FireParticleEmitter";
velocity = "1";
};
new ParticleEmitterNode(campfireSmoke1) {
canSaveDynamicFields = "1";
position = "-31.6712 -110.614 74.4373";
rotation = "1 0 0 0";
scale = "1 1 1";
dataBlock = "SmokeParticleEmitterNode";
emitter = "SmokeParticleEmitter";
velocity = "1";
};
};
Since these files are all clear text, they can edited further outside the editing tools, or to correct for mistakes that
occurred during the editing.
Here is a summary of operations you make do on SimGroups and Objects with the mouse and key combinations.
Page 11 of 71
CTRL +
ON SIMGROUP (Un)select all members in simgroup.
CTRL +
ON OBJECT (Un)select object(s).
SHIFT +
ON OBJECT
We dont yet know how to have a game start out using one mission file, and then jump to another one. However,
this approach is used in many games, such as when the player transfers from one age in Myst to another.
Select the orientation of the mirroring plane (with left and right arrow buttons).
Click Apply to mirror-copy the Source onto the Destination.
Interiors use the DIF format, which is also Torque-specific, and is created from a MAP format, which is common to
a variety of gaming engines such as Quake. These are created in a modeling package such as QuArk or trueSpace,
with a DTS exporter. See Appendix B.
The Terrain Texture Editor adds our textures to the terrain in layers. The first (top-most) texture in the Texture List is
the base layer. This is the texture that is visible if no other textures get applied to a point on the terrain.
As indicated above, terrain textures can be .jpg or .png format, and must be square with a power of 2 for dimensions.
Sun Object
You can control the suns brightness and color balance, as well as the suns angle and azimuth. Use angle values of
0 to 89 degrees.
Page 12 of 71
The sun controls how the shapes and interiors will be lit. You can control the sunlight down to being a mere
moonlight. The sun settings dont affect how the sky is lit, and have only a minor effect on the terrain.
The color attribute is the part of the light that is cast directly onto shapes, interiors, and the terrain. It accounts for
the shadows that interior and terrain features cast. The ambient parameter is the portion of the light that is
scattered by the environment and appears to come from all directions. Both parameters account for the total lighting
of the terrain, the character, and the interiors. Changes to the ambient portion of light are most easily noticed, but
you should experiment with both ambient and color to achieve the desired results. Both parameters take four
arguments <r g b i> where i is the intensity. Currently this has no effect.
There is even a facility to have the sun move across the sky, thus giving us dynamic day and night settings for long
games.
Sky Object
The Sky object defines our skybox. Imagine our world is fully contained within a large box, this is the skybox. The
sky box is composed of a number of layers, each with a different jpg. There can be about 6-7 layers. The collection
is specified through a .dml file, which names the jpgs to use for the layers. We have several sky object collections,
2-3 for day, and one for night, all from the Environment Pack and the demos provided.
A .dml file has the following syntax: it is just a list of textures in this order:
1. sky front
2. sky right
3. sky back
4. sky left
5. sky top
6. sky bottom
7. cloud 1
8. cloud 2
9. cloud 3
Once the sky object is created, you can specify the wind factors for each layer. This is a number, typically in the
range of 0.001 to 0.005.
The sky object also defines the fog intensity and color, which is separate from the sun. The default fog is a day-time
white, but this can be changed down to a night-time level.
A DIF file can also have lighting specified. For instance, when creating interiors in trueSpace, we can create lights
that will appear in Torque when the file is converted. For DTS files, the lighting is only based on the game engine
specifications.
On the World Editor Creator, open the tree to Mission Objects, then to Environment, then select Water.
In the dialog box that appears, give the water object a name, such as Water1.
Page 13 of 71
The Water object will be created at the current location, as with other objects, however, it displays as a plane
indicating the surface of the water, which will follow the contours of the land. Create it at a low spot.
You will need to set the size of the Water object, by changing its scale attribute, typically to something like 300
300 20, in order for the water object to have any useful area. Also, make sure that the media/surfaceTexture and
media/ShoreTexture attributes refer to image files located within the game project directory tree.
Finally, we found that the appearance of water is improved if the sky is not so bright that water textures are washed
out. To control this, select the Sun object from the object tree, and lower the light settings.
The fxShapeReplicator takes a .DTS file as one of its arguments, along with attributes for size and placement of a
disk-shaped region on the terrain, within with a specified number of copies of the .DTS are placed. The copies will
follow the terrain height of the region. Visually, the editor looks like:
The purple band indicates the replicator region, and the sizing of the shapes can be controlled to vary between a
minimum and maximum size. There are other options to describe how lighting affects the shapes.
The fxFoliageReplicator is similar, but it takes one image file (not shape file) and makes copies of it until control of
the replication parameters. The image file is painted so that it always faces the camera. If, for instance, the image
file is a picture of a tree with transparent pixels around the tree (use a .png for this) you will get an image of a forest.
Since these arent actual shapes, you cant walk through the forest and have realistic behavior, but for forests that are
in the background or out of the players walk path this can work very well.
Defining Paths
Click the entry labeled "Path" just once to create a new Path object. Name the Path object, "myPath".
Page 14 of 71
Note: Our newly created Path object has no PathMarkers yet so the Path will not have anything visually
representing it in the Mission Editor. The only indicator of its existence at this time will be in the expandable
"MissionGroup" tree control which is in the upper right hand side of the editor. Of course, this is assuming your still
in "World Editor Creator" mode (F4), which you should be.
In the "MissionGroup" tree, which is in the upper right hand side of the editor, expand out the tree and find the node
labeled "myPath". Make this node the current selection by holding down the "Alt" key while clicking it with the
mouse. If done correctly, the selection color of the "myPath" entry should change from white to gray. This step is
very important because we're about to create three PathMarkers for our path and we need to make sure they get
grouped under our new Path object.
Again, we return to the tree control in the lower right hand corner where we created our new Path object. There, in
the same "Mission" directory you should see another entry labeled, "PathMarker". Click this entry once to place a
new PathMarker into the world. Name the new PathMarker, "myMarker0" and check to make sure it was correctly
grouped under "myPath" in the "MissionGroup" tree control.
If the new PathMarker is correctly grouped under the "myPath" Path object, go ahead and position "myMarker0"
where you want it. Once you're happy with the placement of "myMarker0", repeat step 11 two more times to create
two new PathMarkers called, "myMarker1, and "myMarker2". Make sure to place them far enough apart so you can
see your bot run around.
Note: Be careful not to place your PathMarkers too low on the terrain. If you do, your bot may fall through the
terrain when it spawns and simply fall out of sight.
At this point we have a new Path object complete with enough PathMarkers to see something interesting. Save the
mission file by selecting the "File->Save Mission" menu item and exit completely out of the game.
The lighting objects are located in the mission objects section of the create tree. Some of types of lights include
volumetric lights, spot lights, area lights, etc. In addition, you can define your own light object parameters, and
there is a lighting object editor panel for aid in doing this.
To start with, create a world with limited sunlight, or an interior with a dark room. Then create a VolumeLight, and
set the parameters to texture = MonoFalloff.jpq from the lighting directory. Set the datablock to sgSpotDatablock.
The spotlight shines upwards, so you may need to rotate it over. You can also change the foot and tail lighting levels
using the Inspector.
Another object is the sgLightObject (formerly sgUniversalStaticLight), which is actually more basic that the
VolumeLight, however, it seems to have a frequent initialization problem which is that the visual object doesnt get
built from the configuration information until you save the level and reload it. One approach that minimizes this is
to always choose a known datablock such as the Cottage Light when creating the object.
We will discuss the idea of datablocks in more detail under the section on scripting, but for now, the following
example shows the capabilities that can be expressed in the lighting datablocks. It includes color, intensity, fade-out,
animation, color, etc.
datablock sgUniversalStaticLightData(sgDefaultLightDataBlock) {
className = "sgUniversalStaticLightData";
LightOn = "1";
Radius = "10";
Page 15 of 71
Brightness = "1";
Colour = "1.000000 1.000000 1.000000 1.000000";
FlareOn = "0";
FlareTP = "1";
FlareColour = "1.000000 1.000000 1.000000 1.000000";
ConstantSizeOn = "0";
ConstantSize = "1";
NearSize = "3";
FarSize = "0.5";
NearDistance = "10";
FarDistance = "30";
FadeTime = "0.1";
BlendMode = "0";
AnimColour = "0";
AnimBrightness = "0";
AnimRadius = "0";
AnimOffsets = "0";
AnimRotation = "0";
LinkFlare = "1";
LinkFlareSize = "0";
MinColour = "0.000000 0.000000 0.000000 1.000000";
MaxColour = "1.000000 1.000000 1.000000 1.000000";
MinBrightness = "0";
MaxBrightness = "1";
MinRadius = "0.1";
MaxRadius = "20";
StartOffset = "-5 0 0";
EndOffset = "5 0 0";
MinRotation = "0";
MaxRotation = "359";
SingleColourKeys = "1";
RedKeys = "AZA";
GreenKeys = "AZA";
BlueKeys = "AZA";
BrightnessKeys = "AZA";
RadiusKeys = "AZA";
OffsetKeys = "AZA";
RotationKeys = "AZA";
ColourTime = "5";
BrightnessTime = "5";
RadiusTime = "5";
OffsetTime = "5";
RotationTime = "5";
LerpColour = "1";
LerpBrightness = "1";
LerpRadius = "1";
LerpOffset = "1";
LerpRotation = "1";
StaticLight = "0";
SpotLight = "0";
SpotAngle = "1";
AdvancedLightingModel = "1";
EffectsDTSObjects = "1";
}
Another useful lighting object is the sgDecalProjector, which acts as a projector for an image, showing it on a wall
or other surface. You must also create a Decal datablock which defines the image to be shown (note that the Decal
facility is part of Torque, not the lighting kit, and is used for creating images of footsteps, etc.). The projector
projects vertically on the Z axis, so you may need to rotate it over.
We are still learning about sgMissionLightingFilter, which appears to be a facility to create overall lighting effects
on the entire mission.
Page 16 of 71
Introduction to Scripting
With the basics of world editing out of the way, our next topic is scripting, as it is the basis for behaviors, event
processing, the particle system and the GUI objects. Torque is built around a c-like scripting language complete
with variables and expressions. All scripts are located in .cs files.
We have already seen an example of scripting above, in that the .mis files are actually data-rich scripts, using the
new operator and the assignment statements to create lots of objects in sequence, and having a regular structure so
that the files can be written by the Game Editor.
The scripting language is much like C/C++, in that it has for, if/then/else and similar constructs, along with
algebraic operators. The C++ aspects include objects, classes, namespaces, and datablocks and are covered below.
Important note: If script compilation detects an error, the old .dso file gets used. This is something to watch for
carefully! Suggest that you clear out the .dso files when running tests.
The console is not active at the same time that the game is.
Watch for a notice at the top of the console window that indicates that 1 or more script compilation errors have
occurred. This is a very important hint of trouble. Fix such before continuing!
Variables
Variables come in two flavors in TorqueScript: local and global. Local variables only exist in their local scope- the
function or code-block they are defined in. A piece of code inside a different function would not be able to see the
local variable. Global variables, on the other hand, are permanent and exist throughout the entire program they are
defined in.
Variables are not strongly typed, and need not be declared. In this respect, Torque Script is more like PHP.
However, their scope is determined from the initial symbol with % for local variables, and $ for global variables, as
in:
%local_var = value1;
$global_var = value2;
Case is not significant in variable names. This means that able, Able, and ABLE are all the same variable.
TorqueScript has the usual if/then/else and looping constructs found in Java or C++.
There are also function definitions. Basic functions in TorqueScript are defined as follows:
function function_identifier([arg0],...,[argn]) {
statements;
Page 17 of 71
[return val;]
}
The function_identifier can either be a single identifier, or two identifiers separated by a double colon. The first
format just names the function according to the identifier and puts it into the global namespace. The second format
uses the identifier before the double colon to specify the function's namespace, and the identifier after the double
colon the function's name.
A function does not have to be passed as many arguments as are in its parameter list. If a shorter argument list is
used, then some parameters at the end of the list will be the empty string, like any uninitialized variable in the
scripts.
If you define a function and give it the same name as a previously defined function, TorqueScript will completely
override the old function. Even if you define the new function with a different number of parameters, if it's name is
exactly the same as another function, the older function will be overridden. This is important to note: TorqueScript
does not support function polymorphism in the same way C++ does (meaning that the entire signature is used for
dispatching). However, TorqueScript provides packages, which can get around this problem.
Collections
TorqueScript is extremely flexible with arrays. Many scripting languages are more flexible with arrays than
compiled languages like C++, but TorqueScript is even more flexible than most scripting languages. All this
flexibility can get confusing, and deciphering how TorqueScript handles arrays sometimes trips up new developers.
For example, it is a common misconception that TorqueScript does not support multi-dimensional arrays. The reason
many people get confused about multi-dimensional arrays in TorqueScript is that there are multiple ways to address
the array. As you can see, you can separate the dimension indices (m and n, in the above) with commas, or even
underscores
Below are two more interesting features of the way TorqueScript handles arrays:
1) $a and $a[0] are separate and distinct variables. It is advisable to use some kind of naming convention so it
is clear when you are dealing with an array.
2) $MyArray0 and $MyArray[0] are the same. This appears to be a special syntax role that causes digits at
the end of an identifier to be processed to access array indices without using the common bracket [] syntax.
Again, it is advisable to use a naming convention to identify arrays in your code.
TorqueScript arrays are actually hashmaps rather than indexed arrays. Hence you can use keys such as red,
white, and blue in place of numbers such as 1,2,3. One limitation to TorqueScript arrays is that you cannot find
their size or iterate through the keys. This will be discussed further below in the examples regarding the inventory
system.
Vectors are a helpful data-type which are used throughout Torque. For example, many fields in the World Editor
take numeric values in sets of 3 or 4. These are stored as strings and interpreted as "vectors". Here is an example:
There is a whole set of console operations (read as "available in scripts") for manipulating vectors, and vectors are
taken as input for many API calls, such as to represent an RGB plus alpha color value.
Page 18 of 71
Objects
In Torque, every item in the game world is an object, and all game world objects can be accessed via script. For
example, Player, WheeledVehicle, Item, etc are all accessible via script, though they are defined in C++.
new class_expression(name_expression) {
initialization_statements
};
The class_expression can be an identifier specifying a (valid) object class name. Or, it can be an expression in
parentheses that evaluates to an identifier or a string that specifies a valid class name. Similarly for
name_expression, although the name_expression can be omitted if you don't want to name the object.
The initialization_statements can consist of two parts. First is an optional sequence of identifier assignment
statements, if you want to assign values to the object fields at this time. Second is an optional sequence of more
object creation statements. You can omit either or both. If you omit both, you can also omit the curly braces.
In addition to normal fields, which are common between all instances of an object type, TorqueScript allows you to
create dynamic fields. Dynamic fields are associated with a single instance of an object and can be added and
removed at will. Adding a dynamic field in TorqueScript is automatic. If you try to read an object field and the field
is not found, TorqueScript will simply return an empty string, and no dynamic field will be created. However, if you
try to write to an object field which doesn't exist, TorqueScript will automatically create a matching dynamic field
for the object, and assign it the value you are trying to.
To clone an object, you can optionally specify the name of some other object to copy field values from:
There's one more twist to the object creation syntax. After the name_expression (or, if you're using a
copy_source_identifier, then after that) you can place a comma followed by a comma-separated list of arguments to
be passed to the engine code that creates the object. However no objects except for TCPObject make use of this
feature in the TGE SDK.
Handle: Every object is assigned a unique numeric ID upon creation. This is generally referred to as the
object's handle.
Name: Additionally, all objects may have a name.
In most cases, handles and names may be used interchangeably to refer to the same object, but a word of caution is
in order. Handles are always unique, whereas multiple objects may have the same name. If you have multiple
objects with the same name, referencing that name will find one and only one of the objects.
Packages
Packages are a way of collecting a set of definitions (typically functions), and enabling activation or deactivation of
that set. They are typically used to collect facilities into library-entry like groups, and conditionally use them.
Page 19 of 71
package name_identifier {
function_declarations
}
A function declared inside a package declaration is initially invisible to namespace hierarchy searches; it can't be
used. But when the name of its containing package is passed to the activatePackage global console function, then
that function becomes usable, and it overrides any other function that has the same name and namespace (It can call
the overridden function using Parent). The package can be deactivated by passing its name to the global console
function deactivatePackage.
Namespaces
All class definitions constitute a namespace for their functions and variables, and the namespace hierarchy
corresponds to the inheritance tree for the class definition.
By default, an object's namespace is its class name, and the namespace hierarchy is the class hierarchy of console
objects. The default setup will be modified, especially in the case of datablock objects, but let's just look at an
example using the default setup first.
Let's say you have a Projectile object. Its namespace is Projectile. Proceeding up the namespace hierarchy from
Projectile is the sequence of namespaces Projectile->GameBase->SceneObject->NetObject->SimObject-
>ConsoleObject, because that's what the class hierarchy looks like. If a method or variable cannot be found, an error
message is generated.
You can also specify the starting point for the namespace hierarchy search, by explicitly specifying the namespace of
the function. Unlike invoking a function in reference to an object, invoking a function with an explicit namespace
does not implicitly add an object handle to the argument list. However, the function probably expects an object
handle at the beginning of the list, and if that's the case then you'll want to explicitly pass it a handle there. For
example, both of these invocations do the same thing if %object is a handle for a MyObject object:
Those would both call the same foo_function, a function that is expecting four arguments. And in either case, if
MyObject::foo_function does not exist, then the script interpreter will start searching up the namespace hierarchy to
find a foo_function that does exist.
The identifier Parent is a special namespace that indicates the next namespace up the hierarchy from the namespace
of the current function, whatever that might be. So inside of Projectile::foo_function, Parent would refer to the
GameBase namespace.
Which brings us to the tidbit that when you create an object, you can change the namespace hierarchy and the
namespace of the object. There are a few different ways in which this can be done, depending on the type of object.
When creating an object of type GameBaseData or a derived class, i.e. a datablock object, you can specify a
className field that adds another namespace to the hierarchy, below the class name of the object.
Also, many types of objects (EditManager, GameBaseData, TCPObject, GuiControl, and all their derived classes)
use the object's name to add another namespace to the hierarchy, and the object's namespace is set to be equal to its
name. So, if we created a PlayerData object with className SuperPlayer and named it Hulk, its namespace would
be Hulk, and the path up the namespace hierarchy would be Hulk->SuperPlayer->PlayerData->ShapeBaseData->etc.
On the other hand, if we named it Hulk but did not specify a className, then the object's namespace would still be
Hulk, but the path up the hierarchy would just be Hulk->PlayerData->ShapeBaseData->etc.
Page 20 of 71
BTW, if you use setName() to change an object's name, this does not change the namespace hierarchy or the
namespace of the object.
There's also a generic type of object, ScriptObject, which has two fields named class and superClass. The class field
sets the object's namespace (but not its response to getClassName). If you are using the class field, you may also
use the superClass field, which inserts another namespace in the hierarchy between the class name and ScriptObject.
Since namespaces implement polymorphic dispatching, this is a great way to organize common definition in the
code. Unfortunately the documentation gives a bunch of examples rather than using this terminology, perhaps
because the gaming audience doesnt know the term.
Datablocks
Datablocks can be thought of as content messages about an object. Most objects have a datablock associated with
them, and the datablock structure allows for initialization and inheritance of blocks in a way quite similar to an
object. However, the purpose of the datablocks is to allow information to be passed between the client and the
server sides of the game engine. This is fundamental to the networked aspects of the games: datablocks are passed
between client and server, but the object instance and methods are not. (The methods are expressed in the
compiled .cs files, and these are not moved around, while datablocks created through the scripts and written into
the .cs files can be).
In this example, the new block is called TorqueLogoItem, and it extends ItemData. This defines values for fields
in an ItemData instance.
A datablock is associated with a class instance through the initialization of the class instance. Quite often the
datablock is the first listed initialization variable.
The game developer can create these new datablocks, store them in .cs files, etc., and be used in the game editing
tools for creating triggers, particle Emitters, shapes, etc.
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 class_identifier(name_identifier) {
initialization_statements
};
Page 21 of 71
The class_identifier must be a valid datablock class name, like PlayerData. The name_identifier is whatever name
you choose to give this datablock (as long as it follows the rules for being a valid identifier, and is not a name
already in use).
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. In the TGE SDK,
inventory management is completely done in the scripts, using object fields that the engine code is not aware of.
copy_source_identifier specifies the name of some other datablock from which to copy field values before executing
initialization_statements; this other datablock must be of the same class as the datablock we 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.
Datablocks are templates and we use them to create new objects with the attributes specified by the template.
In fact, to associate a shapeFile it appears that you must create a new datablock.
A datablock is an object that contains a set of characteristics which describe some other type of object. For instance,
a PlayerData object is a datablock that contains many parameters describing the speed, mass, and other properties of
a Player object. When a Player object is created, it is set to reference some already-existing PlayerData object that
will tell it how to behave.
Object handles are unique. You can numerically compare two object handles to see if they refer to the same object.
Additionally, the object handle of a datablock can be passed across a network connection and used on the other side;
datablocks always exist on the server and all its clients, and they use the same handle regardless of whether they are
on the server or on some client. However, handles do not behave that way for non-datablock objects; for a non-
datablock object, a server object will have a different handle than its client ghost objects, so there is no point in
sending an object handle over a network connection unless it references a datablock object.
When a function is called in reference to an object, implicitly the function will be passed another argument at the
beginning of the arguments list: a handle to the object. Customarily this additional argument is named %this in the
parameter list of the function declaration, to indicate that it can be used like the pointer of the same name in C++
objects. So the declaration for our foo_function would have four parameters, the first of which being %this.
Page 22 of 71
FYI, here are some relevant functions defined by the engine into the Console namespace so they are available to all
scripts:
When invoked in reference to an object, getID() returns the object's handle.
An object's handle can also be determined by the global console function nameToID(expression), where
expression is any of the things that can be used to identify an object.
The global console function isObject(expression) is similar, except it just returns a boolean rather than an
object handle.
When invoked in reference to an object, getName() and setName(newName) can be used to read and write
the object's name.
When invoked in reference to an object, getClassName() returns the object's class.
If you're used to C++ there are a few things that probably need emphasizing about TGE scripting objects:
There is nothing special about %this; the use of %this is never implied. If you want to access a field of an
object, you always have to use something that evaluates to an object handle or name followed by a dot
followed by the field name, as in the examples above. The only exception to this rule is in the sequence of
field initialization statements when creating an object.
You may see some variable names in the scripts that use double colons, so that they look like C++ static
member fields of a class. In TGE script, however, colons have no special meaning in a variable name, and
so those variables have no special relationship to any class or object. I suppose that the use of double
colons in variable names can either be viewed as a red herring or as a helpful indicator of the variable's
purpose.
Object handles always behave the same way when used to access object fields or invoke functions. There is
no sense in which an object handle can ever be "cast to another classtype" to change its behavior. When a
handle or object name is used to access a field, it always references the same bit of data. When a handle or
object name is used to invoke a function, the same function is always executed (unless a package has been
activated... more on packages later).
Engine Overview
Basic Control Flow
Because different platforms can have different main() entry points for applications, the Torque Engine main()
function resides in the target OS platform library. In the case of Windows, this is in file
engine/platformWin32/winWindow.cc, where both main() (for console apps) and WinMain() are defined. These in
turn call run() which calls Game->main(int argc, const char **argv). Game is a global object pointer referencing an
instance of the GameInterface class that can be overridden for specific game behavior.
The Torque example program's main initialization occurs in engine/game/main.cc in DemoGame::main(). This
function initializes libraries, initializes game functions and then cycles in the main game loop until the program is
terminated. The main loop basically calls platform library functions (engine/platform/platform.h) to produce
platform events, which then drive the main simulation.
The main.cc file also has DemoGame function overrides for some of the basic event procession functions:
processMouseMoveEvent (which dispatches Windows mouse movements to the GUI), processInputEvent (which
processes other input related events), and processTimeEvent which computes an elapsed time value based on the
time scale setting of the simulation and then:
Page 23 of 71
Checks for network timeouts (dispatchCheckTimeouts() in engine/game/netDispatch.cc)
Before starting it is important that we define a consistent set of terms to describe the concepts we are about to
explore:
Term Definition
C++-Specific Terms
C++ Variable A C++ variable not associated with any class.
C++ Function A routine declared and defined in C++, which is not associated with any class.
A variable defined and declared in a C++ class.
Member Can only be accessed in association with an instance of an object.
A routine defined and declared in a C++ class.
Method Can only be accessed in association with an instance of an object.
Script-Specific Terms
A variable in the Console.
Local or Global
Global variables may be defined in C++ and linked to a global engine variable. Allowed C+
Variable + modifiers: const, static.
A routine in the Console not associated with any particular namespace.
Function May be defined entirely in script or C++.
A variable associated with an object in the Console.
Field Linked with a Member.
A variable associated with an object in the Console.
Dynamic Field Exists only in the Console.
A routine associated with a particular namespace in the Console.
Command Linked with an existing Method.
(deprecated) Console Commands are deprecated-- that is, they should no longer be used. Information on
them is provided for reference only.
A routine associated with a particular namespace in the Console.
Console Method Exists only the Console.
They say, "a picture is worth a thousand words". So, here is an illustration of the above table:
Page 24 of 71
Page 25 of 71
Datablocks, Objects, and Namespaces Revisited
OK, above we dipped into each of these topics, but did little more than get our feet wet. Now is the time to lower the
bar, fasten your seatbelts, and keep your hands inside the cart at all times...
1. The object does not have much associated data and/or has few parameters.
2. The object does have a lot of parameters, but these parameters are likely to be unique, or must be allowed
to be unique, between instances of the object.
3. The object has a lot of data or parameters, but it is OK for these datum/parameters to be shared between
instances.
The first two categories fit the class of objects that do not need and are therefore not created from datablocks.
Conversely, the third category fits the class of objects that could benefit from using datablocks. Why? How? Well,
up until this point, I haven't pointed out a small fact about datablocks. Unlike normal objects, you are only allowed
to have a single instance of any datablock. Furthermore, objects that are created from datablocks all share the same
instance of that datablock.
Table 5.3.
new PhysicalZone() {
};
The above example creates a pzone, but doesn't specify a name or any of the parameters, so they will take the
default value as provided by the C++ class' constructor.
Page 26 of 71
new PhysicalZone(SpeedupZone){
position = "0 0 0";
velocityMod = "2";
};
The above example will create a pzone named 'SpeedupZone' and positioned at <0,0,0>. This particular pzone will
multiply the player's velocity by two when the player enters the zone.
The above example will create a pzone named 'SpeedupZone2' and positioned at <10,10,10>. Aside from position,
which has been re-defined, it will inherit all the parameters of our previous example, 'SpeedupZone'.
The above example will create a pzone named 'FloatySpeedupZone' and positioned at <20,20,20>. In addition to
overriding the position, it overrides gravityMod, giving the block a -2 gravity. As with 'SpeedupZone2', this object
inherits the remainder of the parameter values that have not been overridden from 'SpeedupZone'. Finally, it adds a
dynamic field named 'LastPlayerID' and gives it a value of zero.
Well, this feature is beyond the scope of this guide. Lame-o I know, but hey. If you understand particular feature you
are probably not reading this guide to learn.
new StaticShape(TestTarget) {
position = "0 0 0";
rotation = "1 0 0 0";
scale = "1 1 1";
dataBlock = "SimpleTarget0";
};
The above example creates a StaticShape named 'TestTarget'. It defines the position, rotation, and the scale.
Additionally, it tells the engine to use datablock 'SimpleTarget0' to initialize this object's datablock. Subsequenly,
this object will always be associated with the datablock 'SimpleTarget0'.
The above example creates another StaticShape. This one is named 'TestTarget2'. It inherits all the parameters of
TestTarget and overrides the position. The important thing to understand is that it shares datablock 'SimpleTarget'
with the other instance of StaticShape, 'TestTarget'. i.e., we have two instances of StaticShape that share on instance
of the datablock 'SimpleTarget0'.
Page 27 of 71
Before moving on to datablocks, let me state that for every class that takes a datablock, there is a datablock class.
These datablock classes are usually intuitively named. For example:
Table 5.4.
Declaring Datablocks
So far, we have clarified the connection between objects and datablocks. We have stated that datablocks are objects
in the game world. We've demonstrated that only a single instance of any datablock is created and shared between
any number of datablock-using objects. And, finally, we have shown that there is a one-to-one correlation between
classes that use datablocks and datablock classes in the engine. The only thing remaining for us to discuss is the
declaration of datablocks. So, let's get to it.
// In TorqueScript
datablock DataBlockType(Name [: CopySource]) {
category = "CategoryName";
[datablock_field0 = Value0;]
...
[datablock_fieldM = ValueM;]
[dynamic_field0 = Value0;]
...
[dynamic_fieldN = ValueN;]
};
As you can see, this is almost identical to the syntax used to create Console objects. Let's break it down bit-by-bit
anyway:
datablock StaticShapeData(MyTargets) {
category = "Targets";
shapeFile = "~/data/shapes/targets/simpletarget0.dts";
Page 28 of 71
};
The above example declares a datablock of the type StaticShapeData named 'MyTargets'. Additionally, we have
specified that this StaticShape should be located in the Targets folder in the World Editor Creator Tree. Lastly, it uses
the shape file located at "~/data/shapes/targets/simpletarget0.dts".
The above example creates declares a datablock of the type StaticShapeData named 'SimpleTarget0' which inherits
all the data from MyTargets. In addition, this declaration adds a new variable named 'StartHidden' sets it to 1.
Now, this may seem completely trivial, but it is important to understand, that a majority of the interesting methods
that are called by the engine as a response to user action, like onCollision(), onAdd(), create(), etc., are not called on
instances of objects. They are called on the datablocks of instances of objects that use datablocks, not the objects.
This is crucial because we can do some very special things with datablocks and their namespaces. I've seen
questions time and again in the forums that have their root in confusion about this topic. So, save yourself a lot of
confusion, and make sure you get this idea down firmly!
As previously mentioned, datablocks are nothing more than objects themselves. They exist in the console alongside
regular objects and they too have their own namespaces. For example if we wish to create a new function for
ItemData namespace, we can do something like this:
function ItemData::GetFields(%ItemDbID) {
echo("Calling ItemData::GetFields () ==> on object" SPC %ItemDbID);
echo(" catetory =>" SPC %ItemDbID.category);
echo(" shapeFile =>" SPC %ItemDbID.shapeFile);
echo(" mass =>" SPC %ItemDbID.mass);
echo(" elasticity =>" SPC %ItemDbID.elasticity);
echo(" friction =>" SPC %ItemDbID.friction);
echo(" pickUpName =>" SPC %ItemDbID.pickUpName);
echo(" maxInventory =>" SPC %ItemDbID.maxInventory);
}
The function 'GetFields' is being declared in the 'ItemData' namespace. Considering that CrossbowAmmo is an
instance of ItemData:
==>CrossbowAmmo.GetFields();
Calling ItemData::GetFields () ==> on object CrossbowAmmo
category => Ammo
shapeFile => egt/data/shapes/crossbow/ammo.dts
mass => 1
elasticity => 0.199413
friction => 0.599218
pickUpName => crossbow bolts
maxInventory => 20
Page 29 of 71
Inserting Datablock Namespaces (ClassName)
Datablocks provide a 'hook' with which to manipulate the namespace calling sequence. The 'hook' is the className
field. You'll see this used in crossbow.cs for the CrossbowAmmo datablock. It works thus:
datablock ItemData(CrossbowAmmo) {
...
className = "Ammo";
...
};
What this is doing is 'adding' a new namespace between CrossbowAmmo and ItemData, so that the namespace
calling sequence will look like this: CrossbowAmmo -> Ammo -> ItemData -> et cetera. If we defined two functions
thus:
function Ammo::DoIt(%AmmoDB) {
echo ("Calling Ammo::DoIt () ==> on ammo DB" SPC %AmmoDB);
}
We could then collide with an ammo item and expect to see the following message:
Calling Ammo::onPickup () ==> on ammo DB 66
Calling Ammo::DoIt () ==> on ammo DB 66
This powerful feature allows us to insert a special namespace which we can use for several different datablocks. In
other words, if we were to define two more ItemDatablocks thus:
datablock ItemData(FlamingCrossbowAmmo) {
...
className = "Ammo";
...
};
datablock ItemData(ExplodingCrossbowAmmo) {
...
className = "Ammo";
...
};
Later in our code object derived from the three different datablocks CrossbowAmmo, FlamingCrossbowAmmo, and
ExplodingCrossbowAmmo, can all use the same onPickup() and DoIt() functions as declared in the Ammo::
namespace. This cuts way down on the amount of code we need to write.
In modern 3D games, designers are often faced with the challenge of balancing strain on the system with pure gloss.
As our cowboy hero rides across the desert, we expect little clouds of dust to appear beneath the metal-shod hooves
of his trusty steed. We expect the heat of the desert to warp and wobble the sight of distant mountains. We expect
flies to buzz about the ears of buffalo, and shell casings to fall from discharged rifles.
Page 30 of 71
Clouds of dust and swarms of flies are difficult to model in 3D, and as 3D models, they would hog system resources.
As a result, they must be faked. For this, we implement particle emitters. These special effects systems are
comprised of sprites (sometimes called billboards), which are bitmap images with some level of transparency. They
may be animated. In any case, they almost always face the screen.
The particle system handles smoke, fire, and flame, which are described through datablocks of parameters.
Creating particle emitter systems in Torque is really a three step process. It requires creating the particles
themselves, the behavior that drives the particles, and spot for them in 3D space. These are manifested in Torque as
Particle Data, Particle Emitter Data, and Particle Emitter Node Data. If these terms scare you, dont sweat it once
we have a bit of working code on screen, it will all make sense.
To create a Torque particle, we begin by making the image in a program like Photoshop or GIMP. Particle image
sizes should be powers of 2, not exceeding 512 x 512 pixels. Keep in mind that Torque ignores all color data in this
image, and uses it instead as a transparency map. In this way, black pixels will be transparent; white pixels, opaque;
gray pixels, translucent. Save your image as either a JPEG or PNG file.
Creating a particle datablock in Torque is fairly simple, once youve done it once or twice. Lets look at some
sample code:
datablock ParticleData(steam_puff) {
textureName = "~/data/shapes/steam/steam_puff.png";
lifetimeMS = 300;
lifetimeVaranceMS = 100;
colors[0] = "0.5 0.5 0.5 0.75";
colors[1] = "0.4 0.4 0.45 0.5";
colors[2] = "0.3 0.3 0.4 0.125";
colors[3] = "0.2 0.2 0.35 0";
sizes[0] = 1.0;
sizes[1] = 1.1;
sizes[2] = 1.5;
sizes[3] = 2;
times[0] = 0.0;
times[1] = 0.3;
times[2] = 0.6;
times[3] = 1;
spinSpeed = 0;
spinRandomMin = -100;
spinRandomMax = 100;
gravityCoefficient = -10;
constantAcceleration = 5;
dragCoefficient = 2;
};
Here, we create a ParticleData object template and call it steam_puff. You will notice that the first property within
our datablock is a texture name, which points to our image file. When using Torque, its not necessary to include a
file extension such as .PNG or .JPG. Torque will instinctively try to open either one.
Next, we have lifetime information, which indicates how many milliseconds a particle may remain in the world. In
this case, our steam puff particle will last three tenths of a second, give or take 100 ms, as indicated by the lifetime
variance property. Creating variability is an excellent way of giving your particles some character.
Next, there are lines which control the color and size of the particle throughout its lifetime. Notice the lines that
begin with times[0] times[3]? These are keyframes, and the values they contain represent percentage values, with
0 being the instant the particle enters the world, and 1 being the instant that it leaves. Torque will look at the
information contained in the colors and sizes arrays, and interpolate between them based on the times values.
Page 31 of 71
The colors array accepts four values for each member, which represent red, green, blue, and alpha values.
Remember, our particle image is a transparency map, and otherwise contains no color, so we have to colorize it
ourselves. For color values, 0 always represents no color, while 1 represents full color. Likewise, 0 is invisible, while
1 is fully opaque. In this example, or particle begins life as a semi-transparent gray (red, green and blue are at 50%
each, and our alpha channel is at 75% opaque). Over time, it becomes darker and darker, and more and more
transparent. If you notice the blue channel, it grows darker slightly less than the red and green channels, so our
steam puff also appears bluer as time goes on.
The sizes array works exactly like the colors array, with the size of the particle changing based on the values stored.
A value greater than 1 indicates an increase in size, which a value less than 1 indicates a decrease in size. For our
steam puff, it doubles in size from its initial appearance to its untimely death. Please note that the members of the
sizes array can take values ranging from 0 to infinity.
Now, do you have to include four keyframes? No. In fact, you dont have to include any, but if you dont, Torque
defaults to solid white sprites exactly the size you created them, which dont change their appearance from the time
theyre born to the time they die. There are three variables that control the rotation of a particle. They are spinSpeed,
spinRandomMin, and spinRandomMax. The spinSpeed property can accept values from -10,000 to 10,000. In this
case, we indicate no spin at all, which is 0. On the other hand, we specify a spinRandomMin of -100, and a
spinRandomMax of 100, which allows Torque to randomly adjust the spin of each particle. Again, this adds
variability, and variability adds character.
The final three properties control how a particles motion changes over time. The first property, gravityCoefficient,
indicates how gravity affects each particle. Particles with a positive gravity coefficient will fall, while those with a
negative coefficient will rise. Since were working with steam, we want it to rise, hence the negative value. The
constantAcceleration property indicates how fast the particle moves. It can accept either positive or negative values,
indicating forward or backward motion, respectively. Finally,
the dragCoefficient property indicates how much the particle is slowed per second. We dont have to worry about
this much, since our steam has a shelf life less than one second. However, its useful to demonstrate how particles
can be slowed with time.
Now, a particle on its own does nothing. In order to generate a stream of particles, we need a particle emitter, which,
coincidentally enough, is what we cover in the next section.
datablock ParticleEmitterData(steam_column) {
particles = steam_puff;
ejectionPeriodMS = 25;
periodVarianceMS = 2;
ejectionVelocity = 5;
velocityVariance = 2;
thetaMax = 10;
thetaMin = 0;
phiReferenceVal = 0;
phiVariance = 10;
lifeTimeMS = 0;
};
This particle emitter is called steam_column. Youll notice that the first property, labeled particles, accepts our
steam_puff ParticleData template. This means steam_column will generate multiple steam_puffs, based on the
remaining properties in its declaration. First, we have properties which control how our steam puffs are brought into
the world. In this case, ejectionPeriodMS indicates how often a particle is emitted, and
Page 32 of 71
periodVarianceMS gives us some variability. So our column of steam will generate one puff of steam approximately
every 25 milliseconds. Rather frequent, but hey, its steam.
Next, ejectionVelocity and velocityVariance control how fast each particle travels away from the emitter. Dont get
confused between ejectionVelocity in the emitter and constantAcceleration in the particle itself. These two motion-
based properties could be forcing the particle along two different paths say, up and right.
In fact, thats where thetaMax and thetaMin come into play. These properties essentially control the emitters aim, at
least around the x-axis. They can accept a range from 0 to 180, but of course, the minimum or maximum depends on
its counterparts value. So, if thetaMin is 10, thetaMax must be at least 10. In any case, a value of 0 sends particles
shooting straight up, while a value of 180 sends them straight down.
Likewise, phiReferenceVal and phiVariance control rotation around the z-axis, and can each take a range of values
from 0 to 360. Yes, its rather inconsistent from the theta controls, but it works nonetheless. In this case, our phi
reference value of 0 keeps the emitter from rotating around the z-axis, but our phi variance value allows for some
wiggle room. Again, this kind of variety adds spice.
Finally, we have a property, lifeTimeMS, which determines how long the emitter itself exists in the world. Dont be
fooled, a value of 0 means it never leaves. Any other value for this property must be a positive number. So if you
wanted your emitter to work for 5 seconds and then quit, set this value to 5000.
The precipitation objects are added to your world through the Mission Objects/Environment menu. Each one has a
datablock of attributes specified. Example datablocks contained in example precipitation demos include:
Heavy Rain
Heavy Snow
These datablocks are defined in a manner similar to those used by the particle system. For instance, with your text
editor, make a file named something like "environment.cs" with the following:
datablock AudioProfile(HeavyRainSound) {
filename = "~/data/heavyRain.wav";
description = AudioLooping2d;
};
datablock PrecipitationData(HeavyRain) {
soundProfile = "HeavyRainSound";
dropTexture = "~/data/environment/rain";
splashTexture = "~/data/environment/water_splash";
dropSize = 0.35;
splashSize = 0.1;
useTrueBillboards = false;
splashMS = 500;
};
datablock PrecipitationData(HeavySnow) {
dropTexture = "~/data/environment/snow";
splashTexture = "~/data/environment/snow";
dropSize = 0.27;
splashSize = 0.27;
Page 33 of 71
useTrueBillboards = false;
splashMS = 50;
};
The first section above assigns the name HeavyRainSound as an audioProfile called from the HeavyRain datablock
section. The parameter "filename" is assigned the path to a WAV file.
The second and third sections are the datablocks for HeavyRain and HeavySnow. In each of these datablocks, the
parameter "texture" is assigned the path to a "dml" file that lists the texture(s) for the effect (see example below). In
the HeavyRain datablock is a parameter "soundProfile" that targets the audioProfile in the first section.
Next, in the directories specified by the "texture" parameter, create a file named "raindrops.dml" (or whatever name
you would like as long as it matches the "textureList" parameter). In this file, list the name(s) of the PNG image(s)
to be used. For my rain effect, I used just one 256x256 PNG with alpha channel--basically just some random pixels
painted grey with the background transparent--and placed it in the same directory as the dml file. So the dml file in
this case simply has the following line:
raindrops
If you want the textures to live in a subdirectory (for example "textures") under the directory with the dml file, add
the subdirectory name to the listings also:
textures/raindrops
In the game editor, create a Precipitation block, and have it refer to one of the above datablocks such as Rain. It
can be placed at 0,0,0 with a rotation of 1,0,0,0 and a scale of 1,1,1. Other parameters include:
minSpeed = "0.5";
maxSpeed = "1";
minMass = "5";
maxMass = "10";
maxTurbulence = "0.1";
turbulenceSpeed = "0.2";
rotateWithCamVel = "1";
useTurbulence = "0";
numDrops = "1000";
boxWidth = "200";
boxHeight = "100";
doCollision = "1";
The min/maxSpeed parameters control the downwards dropping of the particles. The min/maxMass parameters
control the size of the particles. The numDrops controls the density of the particles, measured as number of particles
within a box of the specified width and height (for sparse snowflakes, set numDrops to 500 for a box of 200x100.
For rain, set numDrops to 5000 for a box of 100x100. Weve also found that for snow, you want an image that has
fluffy roundish shapes, and for rain, you want an image that has thin vertical streaks).
The remaining parameters control variation in the particle motion due to random turbulence. Generally we leave
these turned off.
Page 34 of 71
Event Model
The game is driven by a stream of events from the platform library. By journaling the stream of events from the
platform layer, the game portion of the simulation session can be deterministically replayed for debugging purposes.
The events used by the platform layer are all subclasses of Event:
- General Events
- TimeEvent
- QuitEvent
- ConsoleEvent
- Input Events
- InputEvent
- MouseMoveEvent
- Networking
- PacketReceiveEvent
- ConnectedReceiveEvent
- ConnectedAcceptEvent
- ConnectedNotifyEvent
This seems to be what the mission files are used for. [more to be filled in]
User Management
Details to be filled in here. [more]
For this reason, there are no objects that go into the components of the shape, such as faces or vertices.
There is one exception, which is that I believe you can associate different objects with different facets or groups
inside a shape or mesh. For example, if the object is a dune buggy, you can associate one object with the buggy
itself, another with each tire, another with the springs, etc., such that the motion of the car can be scripted by fine-
tuning all of these dynamic aspects. Hence, wheels can bounce with the terrain, and a turret could open and close.
This needs more investigation.
Likewise there is a facility to have exhaust come out of specified regions on an aircraft.
Page 35 of 71
Commonly Used DataBlocks
SimDataBlock
The most basic DataBlock available for use in TorqueScript is the SimDataBlock. This DataBlock is extremely
simple; in fact, by default it defines no data which is visible within TorqueScript. However, the SimDataBlock can
be useful in script as a base from which to build more detailed datablocks. Within the engine itself, all datablocks are
derived from SimDataBlock.
In this example, we will introduce the syntax used to work with datablocks in TorqueScript by creating a new
SimDataBlock. The new SimDataBlock will be named "myDataBlock", and we will give it a new member field,
called "newMemberItem". The names "myDataBlock" and "newMemberItem" are arbitrary; we could name them
anything we want.
datablock SimDataBlock(myDataBlock) {
newMemberItem = "Hello";
};
echo(myDataBlock.newMemberItem);
Running this code will result in the text "Hello" being output to the console. More complex datablocks are defined
in a similar fashion.
Note that datablocks of any kind must have at least one member field defined and filled with data in order to be
valid. The item may be either pre-defined in the engine or added in script. The above code would not work if we
had not defined newMemberItem (or some other member field).
ItemData
Like PlayerData, ItemData is derived from ShapeBaseData and includes all the fields ShapeBaseData defines.
ItemData defines new fields as well, and allows developers to quickly specify data related to basic items in the game
world.
As with PlayerData, we will only list the new fields defined in ItemData, for convenience and ease of reading. See
ShapeBaseData for more details on the fields it defines.
Default
Field Name Type Description
Value
Lighting Data
Specifies whether and what type of light is associated with this item.
LightType Valid values are NoLight, ConstantLight, and PulsingLight. Note: all
lightType NoLight
(enum) itemData-based lights are Point lights, this field simply specifies
whether the light pulses or not.
(1.0, 1.0,
lightColor ColorF The item light's color, if a lightType other than NoLight is provided.
1.0, 1.0)
lightRadius Float 10.0 The item's point light radius.
Specifies that only static objects should be lit with this light. May be
lightOnlyStatic Boolean False
used only with static items.
lightTime Integer 1000 For PulsingLight types, the length of a pulse, in ticks.
Page 36 of 71
Default
Field Name Type Description
Value
Physics Data
Used to simulate the physical property of an object's collision
Elasticity Float 0.0 elasticity. In physical collisions, resultant velocity is scaled by
elasticity. Valid range is 0 to 1.
Used to simulate the physical property of friction when colliding with
Friction Float 0.0 an object. In physical collisions, resultant velocity is dampened by
scaling the friction value. Valid range is 0 to 1.
Used to force the item to be sticky; the item will stick to interior or
Sticky Boolean False terrain objects it collides with; data returned in console object
methods such as GetLastStickyPos will be affected.
maxVelocity Float -1.0 Specifies the maximum sustainable velocity for the object.
gravityMod Float 1.0 Scalar multiple applied to gravity effects for the item.
Miscellaneous Fields
Can be used to specify the item name to display when a player picks
pickUpName String "an item"
up the item.
Can be used to specify the item's dynamic type for it's Type Mask.
dynamicType Integer 0
Valid value is $TypeMasks::DamagableItemObjectType.
SimObject
SimDataBlock
TSSShapeConstructor
GameBaseData basic (abstract?) class for things that define the game
ShapeBaseData basic (abstract?) class for game objects that have a shape
StaticShapeData
PlayerData basic class for all players
o Has a list of shapes and animations
o Has a current weapon
o Has a current health/damage level
ItemData this is primary class for non-specific game objects
ProjectileData this used for projectiles fired from weapons.
o Each projectile has a initial location and velocity vector, and a set of
particle emitters for the smoke trail and explosion.
o Each particle can be specified as being ballistic (like a cannonball), or
non-ballistic (like a laser beam).
VehicleData
WheeledVehicleData
HoverVehicleData
MissionMarkerData
CameraData
Page 37 of 71
TriggerData
DebrisData
ExplosionData
FireballAtmosphereData
SplashData
PathedInteriorData
AudioProfile
AudioSampleEnvironment
DecalData
ParticleData
WheeledVehicleSpring
WheeledVehicleTire
We placed this diagram of the built-in classes at this point in the document to show what Torque is designed for, out
of the box: it is designed to handle 3-D interactions between players, projectiles that are fired by players, item such
as treasures or power-ups that are created, found, and destroyed during the game, and the vehicles can be used by
players to move around. The players, projectiles, and items have basic physics model that handles gravity, slopes,
jumping, and fraction, while the vehicle classes have a more specialized physics model that includes the motion of
shocks, springs, and tires. Hence, the standard games are first person shooters, and racing games.
Generating documentation for these built-in datablock classes appears to be one of the most important sources of
information for the developer. There some good summaries in the online EGTGE file, as well as in Appendix D of
the Torque on-line documentation.
Some important classes are: GuiCanvas - The Canvas object, a singleton instance of the GuiCanvas class, is the
root of the currently active GUI hierarchy. It is responsible for processing and dispatching mouse and keyboard
events, managing update regions and cursor handling, and calling the GuiControl render methods when it is time to
draw the next frame. The GuiCanvas keeps track of a stack of content controls - separate hierarchies of GuiControls
that render from bottom to top. The main content control is a screen in the shell, and can be covered by any number
of floating windows or dialogs. The root of each content control hierarchy is sized to cover the entire canvas, so
generally dialogs are composed of a content control containing a dialog control.
Just as pressing F11 brings up the editor for the terrain and game information, there is also an editor for the
interactive definition of UI screens. Press F10 to reach this.
Each UI screen is similar to a Java JDialog object hierarchy with a simple layout manager. The objects on the
screen (called controls) are moved into position like in Java or Visual Basic screen editors.
All of the GUI objects called controls) are based on classes, just as the game objects were. Even the main display
screen shown when playing the same is a defined GUI, with a control that shows the terrain view, and additional
controls for a Heads Up display, status area, or command icons.
GuiButtonCtrl this provides a clickable button with a label and a command script
Page 38 of 71
GuiHealthBarHud shows a health bar, and is typically paired with a GuiBitmapCtrl
GuiTextCtrl this draws a piece of text, and is not editable nor does it process events
GuiTextEditCtrl this draws a piece of text which is editable, and processes events.
Basic Mapping
The first thing were going to do is check out some of the existing code Torque has to offer. For this, we turn to
demo/client/config.cs. Contained in this file we find three important functions:
The first line is common courtesy. If an action map named moveMap exists from a previous game, delete it. Why is
this important? Between games (heck, between levels) the applicable keys may change, and the key responsible for
firing a weapon might become the key responsible for jumping. If we tried to fire a gun that doesnt exist in the all-
jumping level, worlds collide.
In the next line, we create a new action map named moveMap. The new ActionMap() command function takes
one argument, which is the name of our action map. This leads us to an interesting question. Can we create more
than one action map for our game? The answer is, absolutely! Well explore this a little later.
Next, we have a list of key bindings. These key bindings can be created using one of two commands.
The first, and easiest, is bind(). Bind() takes three arguments the name of the input device, the triggering event,
and a function to call. The two input devices we are most concerned with are keyboard and mouse0. As you can
see, in line four of the above code, we are binding a function called showPlayerList to the F2 key on the keyboard.
Easy as pie!
The second method of binding keys is the use of the bindCmd() function. BindCmd() allows you to add two
functions to a key one when the key is pressed, and another when the key is released. So, in line three, we can see
that we are assigning two commands to the Escape key. The first argument (known as the make command), is blank,
indicated by empty quotation marks. The second argument (known as the break command), is escapeFromGame(),
which as you guessed, escapes from the game. This means that
Page 39 of 71
nothing happens when you press the Escape key, but when you depress it, it calls the function which quits out of
Torque.
Now, lets write a few simple functions to test this. Place the following code in a .cs file called bindTest.cs, and place
it in the demo/data/ folder:
function getPlayerPos() {
echo(localclientconnection.player.position);
}
moveMap.bindCmd(keyboard, "x", "", "getPlayerPos();");
Finally, load the .cs file into memory using the exec() command in Torques console
window, like so:
exec("demo/data/bindTest.cs");
Finally, test your method by walking through the terrain with the Mission Editor disengaged (that is, hit F11 if you
still have the menu bar and heads-up-display visible), and the console window closed. Press the x button on your
keyboard. Open the console, and you should have the x-, y-, and z-coordinate values of the player recorded.
Now, lets go back to the notion of multiple action maps. Imagine your game has vehicles which the player can enter
and exit. Obviously, the controls used for driving a car are different from the controls for moving your players
avatar. We might create two sets of key bindings, like so:
new ActionMap(playerMap);
// List key bindings here via playerMap.bind() or playerMap.bindCmd()
new ActionMap(vehicleMap);
// List key bindings here via vehicleMap.bind()or vehicleMap.bindCmd()
Now all we need to do is select an appropriate map based on whether the player is in a vehicle or not. Such code
might resemble the following function:
function toggleMaps(%inVehicle){
if (%inVehicle){
playerMap.pop();
vehicleMap.push();
} else {
playerMap.push();
vehicleMap.pop();
}
}
The function toggleMaps is called whenever the player enters or exits a vehicle. This function takes one argument,
%inVehicle, which is either true or false. If it is true, we need to use the action map for vehicles. We push (activate)
the vehicle map, and pop (deactivate) the player map. If the player is not in a vehicle, we push (activate) the player
map, and pop (deactivate) the vehicle map.
It should be noted that Torque has a default action map, labeled GlobalActionMap, which saves us from at least a
few headaches. Some keys should always be available, no matter whether were in a vehicle or on foot. Keys that
display help menus, for example, or which allow us to exit the game at any time. These should be stored in
GlobalActionMap rather than in our other, mode-dependent action maps. And of course, its a good idea to never
pop (deactivate) GlobalActionMap.
Playing Sounds
Generating audio in the Torque engine is a snap. There are very few hoops to jump through when setting up and
playing sound effects.
Page 40 of 71
Descriptions
While it may seem confusing at first, playing a sound requires establishing two objects in memory an
AudioDescription and an AudioProfile. Lets start with descriptions, since in many ways they are a parent to
our profiles. An AudioDescription is a set of properties that determines how sounds are played in
general. That means that several sounds can share the same AudioDescription. This will become clear when we
look at some code, which well do in just a moment. First, we need to establish how our sound will be used within
Torque. Will it be localized to our computer or accessible across a network? The answer to this question will
determine which keyword we use to create our sound. Localized audio is created using the new keyword, whereas
networked audio is created using the datablock keyword, like so:
// Create a localized sound effect, which can only be heard on your computer.
new AudioDescription(localized_3D_sound){
volume = 1;
isLooping = false;
is3D = true;
referenceDistance = 1;
maxDistance = 10;
type = $DefaultAudioType;
};
// Create a networked sound effect, which can be heard by multiple computers.
datablock AudioDescription(networked_3D_sound){
volume = 1;
isLooping = false;
is3D = true;
referenceDistance = 1;
maxDistance = 10;
type = $DefaultAudioType;
};
Descriptions, whether created via new or datablock, take one argument the name of the description. In the first
description, its localized_3D_sound, and in the second, its networked_3D_sound.
Lets look at the properties within these descriptions. First we have volume, which is set to 1, or 100% of the
actual volume of the sound file in question. Values greater than 1 will increase the volume of the sound, while values
less than 1 will decrease it.
Next, the property isLoop determines if the sound in question loops, or plays one time. Music will probably loop,
so a value of true is appropriate. On the other hand, explosions should only play one time, so a value of false is
appropriate.
The is3D property determines whether the sound is ambient or specific to a particular source. For example, the
sound made when you click on a button in a popup window is ambient it exists outside of the game world. Music
is also ambient (unless tied to a source in-game, such as a phonograph or radio). For these effects, wed set the is3D
property to false. On the other hand, a door opening, glass shattering and dynamite exploding are sounds tied to a
particular source. Their volume and stereo position may change depending on where they happen relative to the
player. In this way, the is3D property should be set to true.
The next two properties, referenceDistance and maxDistance, are only relevant if our sound is a 3D effect.
The first property, referenceDistance, establishes the distance at which the audio is played at full volume. In this
case, the sound effect will be played at full volume as long as it occurs within 1 world unit (about 3 meters) of the
player. maxDistance indicates at what point the volume drops to zero. In this case, the audio will not be heard if
it occurs more than 10 world units from the player.
Finally, we have the type property, which indicates what channel the audio will play from. Torque has three built-in
channels, but others can be created. Our example description uses $DefaultAudioType, which is channel 0. Two
other channels are $GuiAudioType (channel 1), used for the graphical user interface; and $SimAudioType
(channel 2), used for in-game mission objects.
Page 41 of 71
Profiles
Notice how neither of these descriptions reference a particular file? Thats what our AudioProfile is for. An
AudioProfile essentially specifies which sound to play, and which AudioDescription to play it with.
Profiles, like descriptions, also fall victim to the stigma of being either localized or networked, and so we again need
to determine if we use the new or datablock keyword. Lets looky codey:
new AudioProfile(explosion){
filename = demo/data/sounds/explosion.ogg;
description = localized_3D_sound;
};
Profiles accept one argument the name of the profile which in this case is explosion.
Profiles have two properties, filename and description. Filename is simply the path to our intended sound
file. Description is a reference to an AudioDescription, in this case localized_3D_sound, which we created a
few pages ago. Simple enough!
// The following commands can be executed via the console, or planted in functions
elsewhere
GuiButtonProfile.soundButtonOver = over_2D;
GuiButtonProfile.soundButtonDown = press_2D;
The execution of these sounds varies across datablock type. Lets look at a few examples:
datablock ProjectileData(arrow_shaft){
// Here we would normally list all of this
// datablocks properties, however we are
// really only interested in
Page 42 of 71
sound = whistle;
};
Here, we create an arrow as a projectile. As the arrow travels through the air, it generates a whistling sound.
datablock ExplosionData(bloody_arrow_hit){
// Here again wed normally list all of this
// datablocks properties, however we are
// really only interested in
soundProfile = messy_thump;
};
Here, we create an explosion actually, an explosion of gore when an arrow strikes its victim (yuck!). When the
explosion occurs, it plays a messy thump sound, indicated by the soundProfile property.
alxStop(%obj);
Of course, if were playing music, we might want to store its ID number in a global variable, like $music_ID or
something catchy.
Page 43 of 71
Creating Triggers in Torque
Triggers are invisible structures in the Torque engine which have the ability to execute code. You can create triggers
using several methods, including by using the World Editor Creator feature in the Torque mission editor.
To add a trigger through code, create a .cs file and add and modify the following code.
datablock TriggerData(FilingCabinetTrigger){
tickPeriodMS = 1000;
};
function addTrigger(){
%obj = new Trigger() {
position = "100 100 100";
scale = "1 1 1";
rotation = "0 0 1 0";
dataBlock = "FilingCabinetTrigger";
polyhedron = "0 0 0 1 0 0 0 -1 0 0 0 1";
};
}
As you can see, the first step is to create a datablock called TriggerData. It takes one argument, which will be the
name of the parent trigger. In this case, were calling it FilingCabinetTrigger.
The datablock has one parameter, labeled tickPeriodMS. This indicates how often the datablock executes code.
Weve assigned it a value of 1000, meaning the trigger will be executed every second. By the way, the smaller this
number, the greater its strain on your CPU. Deciding on a reasonable interval is important.
Next, we have a function called addTrigger, which, as you might have guessed, creates an instance of
FilingCabinetTrigger.
The first line creates a local variable called %obj, and assigns it the ID of a new Trigger. This new trigger has
several parameters of importance. First, its position is where it appears in the mission. Its scale is also reported, as is
its rotation. These three properties should be familiar to you, having previously worked with static shapes. %obj will
take on the properties of the FilingCabinetTrigger outlined above, by assigning it to the dataBlock parameter.
Finally, a property called polyhedron creates the shape of the trigger. It is currently set up to represent a box,
although additional shapes are possible. To date, I have not found any resource that explains how this polyhedron
parameter works. I tend to manipulate the triggers scale in order to create the proper size and shape.
By running the addTrigger() function from the console, we can add an instance of the FilingCabinetTrigger to our
mission. Next, we need to make it do stuff. The following code offers an example:
Every trigger has two important functions built into it onEnterTrigger() and onLeaveTrigger(). As you might
expect, onEnterTrigger() is called whenever the player moves into the space the trigger occupies. onLeaveTrigger()
is called whenever the player moves out of the space the trigger occupies. Remember, we set the tickPeriodMS to
1000, which means these functions will be called only once a second, and only if their assumptions are met. There is
a third method, onTickTrigger(), which we will address later.
Page 44 of 71
In this case, when the player enters the trigger, Torque will set the value of a global variable called CabinetInRange
to true. It will also print In range in the console. Likewise, when the player exits the trigger area, CabinetInRange
will revert to false, and the Torque will print Out of range in the console.
Notice that these functions take three arguments, which are supplied by Torque. These arguments are %this,
%trigger, and %obj, which are essentially the following objects:
Variable What It Represents
%this The datablock associated with the trigger.
%trigger The trigger which executed the function the one we created in
code.
%obj The object which moved into (or out of) the trigger area it could
be the player, for example.
Now, in order to make this code work effectively, we need to execute the .cs file in which it exists, either through the
console, or by placing it in your game.cs file. Either way, the code to run this is as follows:
exec("demo/data/shapes/filing-cabinet/filing-cabinet.cs");
addFilingCabinet();
Of course, you will need to change the first line to reflect the name and location of the .cs file you have created. You
should also add a static shape in roughly the same spot as your trigger.
Animation Control Via Triggers
If you wanted to control a shapes animation using triggers, we need to take
a few extra
steps. First, we need to create an animated object. Lets assume weve made
a filing
cabinet, which will open and close whenever the player enters and leaves
the trigger area.
We might write code like this (and for your sake, Ill boldface what Ive
changed from the
previous examples:
datablock StaticShapeData(FilingCabinet){
shapeFile = "~/data/shapes/filing-cabinet/filing-cabinet.dts";
};
datablock TriggerData(FilingCabinetTrigger){
tickPeriodMS = 1000;
};
function addTrigger(){
%FilingCabinetObj = new StaticShape(){
position = "100 100 100";
scale = "1 1 1";
rotation = "0 0 1 0";
datablock = FilingCabinet;
};
%obj = new Trigger() {
position = "100 100 100";
scale = "1 1 1";
rotation = "0 0 1 0";
dataBlock = "FilingCabinetTrigger";
polyhedron = "0 0 0 1 0 0 0 -1 0 0 0 1";
cabinetID = %FilingCabinetObj;
};
}
This new code should not be unfamiliar to you at this point. We create a static shape datablock called FilingCabinet,
and then create an instance of that datablock in the addTrigger() function. Notice that we are storing the ID of the
Page 45 of 71
static shape in a local variable called %FilingCabinetObj, which we then assign to our triggers cabinetID property.
In this way, we can modify our onEnterTrigger() and onLeaveTrigger() functions to control the static shapes
animation, like so:
function FilingCabinetTrigger::onEnterTrigger( %this, %trigger, %obj ){
$CabinetInRange = true;
echo("In range.");
openCabinet(%trigger.cabinetID);
}
function FilingCabinetTrigger::onLeaveTrigger( %this, %trigger, %obj ){
$CabinetInRange = false;
echo("Out of range.");
closeCabinet(%trigger.cabinetID);
}
function openCabinet(%id){
%id.playThread(0, "open");
}
function closeCabinet(%id){
%id.playThread(0, "close");
}
Finally, lets look at an addition trigger function, onTickTrigger(), which checks to see if something is inside the
trigger area. This function accepts the same three arguments as onEnterTrigger() and onLeaveTrigger(), but will be
executed not just once, but every time the tickPeriodMS timer is reset. This could be a good way of assigning
damage to a player who has fallen into a pit of lava, for example.
function InsertTestShape() {
%shape = new Item() {
datablock = TestShape;
rotation = "0 0 1 0";
};
MissionCleanup.add(%shape);
Page 46 of 71
echo(%xfrm);
%lx = getword(%xfrm, 0);
%ly = getword(%xfrm, 1);
%lz = getword(%xfrm, 2);
%shape.setTransform(%lx SPC %ly SPC %lz SPC "0 0 1" SPC %zAngle);
}
To generalize this, we could create a class called Treasure, to be used with different data blocks that provide shapes
for the treasures.
Animation Example
For animation, the approach is to schedule another movement in 200 milliseconds. The execution of each one will
schedule another, until the goal is reached.
There are three API routines on the object class to request playing the animation screen for a given object.
Object.playThread(<slotNumber>, ThreadName);
Object.setThreadDir(<slotNumber>, true or false);
Object.stopThread(<slotNumber>);
Teleporter Example
This builds on the trigger discussion earlier.
datablock TriggerData(TeleporterTrigger) {
tickPeriodMS = 500;
};
if (%checkname $= "Teleporter1") {
// if the player didn't recently beam over here... otherwise
// he would be looping around between the two, I guess...
if (!$from2to1) {
%target = "Teleporter2";
$teleSched = schedule(2000, 0, "goScotty", %client, %target);
%client.player.setCloaked(true);
$from1to2 = true;
$from2to1 = false;
}
}
else{
if (!$from1to2) {
%target = "Teleporter1";
$teleSched = schedule(2000, 0, "goScotty", %client, %target);
%client.player.setCloaked(true);
$from2to1 = true;
$from1to2 = false;
}
}
}
Page 47 of 71
function TeleporterTrigger::onLeaveTrigger(%data, %obj, %colObj) {
%checkname = %obj.getName();
%client = %colObj.client;
cancel($teleSched);
%client.player.setCloaked(false);
// if the player leaves the target trigger, he can use it, too...
if (%checkname $= "Teleporter1") {
$from2to1 = false;
}
else if (%checkname $= "Teleporter2") {
$from1to2 = false;
}
}
To use this, make a trigger and a shape. Or make a particle emitter, to create a flaming or fuzzy appearance when
beaming in and out.
Racing Example
The first difference is that the object made as the player is a WheeledVehicle. Its shape file is buggy.dts, which is a
very complex skeleton under a set of shapes, and an unwrapped texture file. It inherits from ShapeBase, which is
the same superclass as Player data. Thus it can become a playable object, though it will not have health, a current
weapon, or a list of animations (such as running, ducking, etc.).
Then the interiors and terrain look like a racetrack on the sand dunes. They use the Tropics tileset.
Another difference is the checkpoint object. This is connected to a Trigger object, which is similar to the teleporter
approach, but when entering the object, it determines if the player has reached a new checkpoint, and flags
otherwise.
If (%obj.client.nextCheck == %trigger.checkpoint) {
If (%trigger.isLast) {
// Player has completed a lap.
%obj.client.lap++;
Page 48 of 71
else {
%obj.client.nextCheck = 1;
commandToClient(%obj.client, 'IncreaseLapCounter');
}
}
else {
// Continue to the next one.
%obj.client.nextCheck++;
}
}
else
centerPrint(%obj.client, "Wrong Checkpoint!", 1, 1);
}
Door Example
We tried several versions of Doors, including ones which would slide because their x,y,z position was being altered
over time. However, the best approach was to have a door be a DTS file that has two positions, open and close,
and an animation script between them.
Then the logic for encountering the door is to tell the door object to play one of the animation scripts.
In particular, this logic was placed into the processing for a physical zone which is the region in front of the door.
When the player is in this region, and presses a bound button such as U (for use), the door opening behavior is
started. When the player leaves the physical zone, then the door closing behavior is triggered.
Weapons Example
An example of weapons is the rocket launcher tutorial from CodeSampler. This defines a weapon as a
ShapeBaseImageData object that defines something with a gun-like appearance, and implements an onFire method.
Here is the process for adding a new weapon:
1. Create model in trueSpace. Should be about 3 units long. Since this will be a DTS export, you must set up
Mesh Layer, Collision Box, and mount point.
2. Create model for ammo in trueSpace. Should be about 0.5 units long. Must also have Mesh Layer and
Collision Box
3. Create directory under data/shapes for weapon.
4. Export model and ammo to this directory.
5. Copy or create bullet model for the projectile fired by this weapon
6. Collect or create sound effects, including firing, explosion, and no_ammo.
7. Create <weaponName>.cs file under server/scripts. This will contain all of the particle effects, as well as
the Item, Projectile, and Image class definitions.
8. Change server/scripts/game.cs to load the new file
9. Change server/scripts/player.cs to specify the maximum inventory levels. For the weapon, it will be one,
and for the ammo, it could be from 10 to about 100.
10. Change client/scripts/default.bind.cs to define keyboard selection shortcuts for the new weapon.
The most important part of the weapon script is that ShapeBaseImageData implements a state transition system that
takes the weapon through a cycle of
Preactivate -> Activate-> Ready -> Fire -> Reload, and back to Ready or to NoAmmo
There are a set of transition times for each. Hence, the rate of fire can be no faster than the sum of the times for
Ready->Fire, and Fire->Reload, and Reload->Ready.
Working upwards from the bottom of the <weapon.cs> file, we have the method that does the actual firing, then the
definition of the image for the weapon, then the item in the world for the weapon, then the projectile, then the
exhaust emitter for the weapon (if applicable), then the emitters and particle for the impact explosion. At the very
top are the sound definitions.
Page 49 of 71
The state system is described near the bottom, with lines like: stateName[0]. Each state has a number, such as 0
through 5. Each state has a time, can have an image sequence, and can have a source.
For instance, in some of our weapons, we have defined a sound to play when the weapon fires, and in some cases,
we have a sound when the particle fired by the weapon hitting an obstacle. Look in the section on Particle Effects
for information about defining the particles.
One of the most common extensions to the player class is to add path following logic. By overriding the following
two methods, the player object will move through the nodes on a path.
datablock PlayerData( MyBot : PlayerShape ) {
// TO DO: Add extra stuff here to manage new A.I. functionality...
patrol = true;
attack = false;
};
When processing damage, remember that Items go through a series of states -> Enabled, Disabled, Destroyed.
However, destroyed, doesnt disable the collision detection, you must delete the object for that to work!
%halfRadius = %radius / 2;
while ((%targetObject = containerSearchNext()) != 0) {
Page 50 of 71
$TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType);
if (%coverage == 0)
continue;
Inventory Management
There is an inventory system which is totally scripted, no C++ code involved built into the starter.fps game. It uses
object datablock names to track inventory and is generally object type, or class, agnostic. In other words, it will
inventory any kind of ShapeBase object, though the throw method does assume that the objects are small enough to
throw.
For a ShapeBase object to support inventory, it must have an array of inventory max values:
%this.maxInv[GunAmmo] = 100;
%this.maxInv[SpeedGun] = 1;
For objects to be inventoriable, they must provide a set of inventory callback methods, mainly:
onUse
onThrow
onPickup
//-----------------------------------------------------------------------------
// Inventory server commands
//-----------------------------------------------------------------------------
function serverCmdUse(%client,%data) {
%control = %client.getControlObject();
%control.use(%data);
if (%data.category $= "Weapon" && %client.player.getInventory(%data) > 0)
{
commandToClient(%client, 'UpdateQuickInv',%data.shapeFile);
}
Page 51 of 71
}
function serverCmdUseById(%client,%id)
{
%control = %client.getControlObject();
if(%id > %client.player.totalInvDirectUseNum)
return;
%control.useById(%id);
%itemName = %client.player.totalInvDirectUseName[%id];
if (%client.player.getInventory(%itemName) > 0)
{
commandToClient(%client,'UpdateQuickInv',%itemName.shapeFile);
}
}
//-----------------------------------------------------------------------------
// ShapeBase inventory support
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
function ShapeBase::use(%this,%data)
{
// Use an object in the inventory.
if (%this.getInventory(%data) > 0)
{
return %data.onUse(%this);
}
return false;
}
function ShapeBase::useById(%this,%id)
{
// Use an object in the inventory.
%itemName = %this.totalInvDirectUseName[%id];
if (%this.getInventory(%itemName) > 0)
{
return %itemName.onUse(%this);
}
return false;
}
...
function ShapeBase::incInventory(%this,%data,%amount) {
// Increment the inventory by the given amount. The return value
// is the amount actually added, which may be less than the
// requested amount due to inventory restrictions.
%max = %this.maxInventory(%data);
%total = %this.inv[%data.getName()];
%totalInvCount = %this.totalInvNum;
%itemAlreadyInInventory = false;
// search
for(%i = 1; %i <= %totalInvCount; %i++)
{
%invName = %this.totalInvName[%i];
%invNumber = %this.getInventory(%invName);
if(%data.getName() $= %invName){
%itemAlreadyInInventory = true;
}
}
if(!%itemAlreadyInInventory)
{
%this.totalInvName[%this.totalInvNum++] = %data.getName();
if(%data.directUse $= "1")
{
%this.totalInvDirectUseName[%this.totalInvDirectUseNum++] = %data.getName();
}
}
if (%total < %max) {
if (%total + %amount > %max)
%amount = %max - %total;
%this.setInventory(%data,%total + %amount);
Page 52 of 71
return %amount;
}
return 0;
}
World/Screen Management
Create a button, labeled it appropriately, given it an appropriate command button Profile, and placed it where we
want it on the screen. The next step is to make the command button perform an action when we click it.
To do so, we will enter a TorqueScript statement into the Command field for our button. If this sounds scary, don't
worry; you'll see that this system is very easy to use. In the Inspector Dialog, find the textbox next to the
'Command' label. Enter the following text in it: "Canvas.pushDialog(HelloWorld);" (no quotes). Once you have
entered the text, click the Apply button.
RifleScoping
[To be filled in]
Use an overlay more]
Change the FOV [more]
// Trigger count
$Game::TeamObjectives[1] = 0;
$Game::TeamObjectives[2] = 0;
$Game::TeamScores[1] = 0;
$Game::TeamScores[2] = 0;
$Game::TeamFlagCaptures[1] = 0;
$Game::TeamFlagCaptures[2] = 0;
$Game::PointsForCapture = 50;
//-----------------------------------------------------------
//NEW FLAG CAPTURE TRIGGER
//--------------------------------------------------------------
// Trigger datablock
//--------------------------------------------------------------
datablock TriggerData(CapFlagTrigger)
{
tickPeriodMS = 100;
team = -1;
teamEmitter = 0;
pointsforcapture = 50;
};
//--------------------------------------------------------------
// Events
//--------------------------------------------------------------
function CapFlagTrigger::onEnterTrigger( %this, %trigger, %obj )
{
// This method is called whenever an object enters the %trigger
// area, the object is passed as %obj. The default onEnterTrigger
// method (in the C++ code) invokes the ::onTrigger(%trigger,1) method on
// every object (whatever it's type) in the same group as the trigger.
Parent::onEnterTrigger( %this, %trigger, %obj );
//SO the trigger requires 3 dynamic variable when placing
//invcheckitem = Flag1 OR Flag2 OR whatever it is looking for in the players
//inv and
//team = 1 or 2 or whatever you labled the team property
// and
Page 53 of 71
//respawnat = the name of a marker object
echo("ENTERING A CapFLAG TRIGGER");
// check to see if the lad who is in this trigger is on the our team
if( %obj.client.team.teamId $= %trigger.team )
{
echo("This is your team");
// ok, its one of our lads.. see if he has the enemy flag?
if (%obj.getInventory(%trigger.invcheckitem) > 0)
{
echo("You Scored");
// cool.. we got a capture!
$Game::TeamFlagCaptures[%obj.client.team.teamID]++;
// make a hoot about capping.
messageAll( 'MsgObjective', '\c2Team %1 captured a flag! %2
points!', %obj.client.team,$Game::PointsForCapture );
$Game::TeamScores[%obj.client.team.teamID] +=
$Game::PointsForCapture;
// take the flag out the guys inventory.
%obj.setInventory(%trigger.invcheckitem,0);
%obj.unmountImage($FlagSlot);
// now return the flag..
echo("ENTERING A CapFLAG TRIGGER");
newflag(%trigger.invcheckitem,%trigger.respawnat);
}
}
}
Overview
The code we will work on in this exercise is only one possible way of adding AI to your project. I state this only to
alleviate any future frustrations you might encounter in using the code provided below. Its a start a jumping off
point for your future masterpieces but its not a total solution. In many cases though, it should work in a pinch.
What we want to create is a system where AI bots can be spawned with a single line of script, which, for example,
could be called when the mission begins, or when the player passes through a trigger.
We also want to establish a system that allows for multiple AI styles. That is, if all bots acted the same way, the
game would become boring. This tutorial will demonstrate how we can control (and even switch) AI styles. Finally,
we need a way to periodically update our AI. Thankfully, Torque makes this easy with the schedule function, which
we will utilize in this example.
Adding a Bot
Torque comes with preset dummy characters known as PlayerBodies. Much of the PlayerBody functionality can be
found in aiPlayer.cs, a file located in the demo/server/scripts/ folder. I recommend looking over this file for
additional help with activites such as pathfinding. Since our AI bots are more of a deterministic variety, well forego
much of this file for a more simplistic approach. To add a bot, use the following code:
Page 54 of 71
Lets take this code a bit further and add it to a custom function, called addBot. AddBot takes four arguments, an AI
style, and position information.
Now, if you want to add a bot to your level, you can simply call the addBot function with a style and position for
each bot, like so:
Still, no action on the part of Kork. But we have added two new properties. The first is botID, which we will use
throughout the exercise. Currently, its set to zero. After the new block that creates our bot, add the following line:
%bot.botID = %bot;
The second property weve added is style, which we will use to control how the bot behaves.
Finally, lets give Kork a crossbow. Below the new block that creates the bot, add the following line:
%bot.mountImage(CrossBowImage, 0);
Still, no Kork just sits there. Thats where the updateBot function comes in handy. Well write the function in the
next section, but for now, add the following line:
updateBot(%bot.botID);
Updating the AI
The updateBot function takes one argument, the ID number of an AI bot. In this function, well make sure the agent
always has ammunition for the crossbow. Also, well determine how far the bot is from the player. Then, well
execute an IB behavior based on style. Finally, well schedule the updateBot function to be called sometime in the
near future.
Next, we need to determine how far away the player and the bot are from each other. This is sadly more
complicated than it needs to be in Torque. The engine stores position information as a single string, instead of three
separate numerical values. To parse out each x-, y-, and z-coordinate, we have to use the getword function, like so:
%botX = getword(%id.getTransform(),0);
%botY = getword(%id.getTransform(),1);
%botZ = getword(%id.getTransform(),2);
%playerX = getword($gspPlayer.getTransform(),0);
%playerY = getword($gspPlayer.getTransform(),1);
Page 55 of 71
%playerZ = getword($gspPlayer.getTransform(),2);
Finally, we can use the values stored here to determine a distance:
%distance = mSqrt((%playerX - %botX)*(%playerX - %botX) + (%playerY
- %botY)*(%playerY - %botY));
For the moment, well skip executing AI behaviors, and instead look at how we can recall
this function automatically. We do so by using the schedule function, like so:
schedule(200, 0, updateBot, %id);
The first argument in this function represents how many milliseconds to wait before calling the third argument,
which in our case is the name of our updateBot function. Any arguments after the third represent arguments for the
function to be called (hence %id, which is the sole argument for updateBot). So, a translation of this statement in
plain English would read In 200 milliseconds, call updateBot for the bot indicated by %id. So now we have a
function that gives the bot ammo, determines its distance from the player, and schedules another update in the
future. In the next section, well explore AI behaviors.
Styles
After we determine a distance from the player, we will steer the behavior of the bot according to its style. We will
add three styles of AI to this example a grunt, which pursues the player, and when in close range, stops to fire; a
sniper, which remains stationary and fires if the player is within a wide range; and a berserker, who always pursues
the player, firing as it goes regardless of distance.
Within the updateBot function, we will execute these behaviors using a switch statement, which looks like this:
switch (%id.style){
// CASES GO HERE
}
Within the switch statement, the property %id.style will be evaluated, and a case will be selected. Each case
represents a particular style of bot AI. Going in reverse order, the berserkers case looks like this:
case 2: // BERSERKER AI
%id.fire(true);
%id.setMoveDestination($gspPlayer.getTransform());
%id.setAimObject($gspPlayer);
The fire function, which takes a Boolean value for an argument, determines whether the bot is firing or not. It is not
a request for a single shot, but rather a toggle switch that sets the bot to fire indefinitely, or remain passive. The
setMoveDestination function takes the players position as an argument, and tells the bot to move towards that spot
on the map.
Finally, the setAimObject function tells the bot to always aim towards the player. This is a very simple AI bot,
which always follows the player, and always shoots. Though simply, hes pretty darn lethal, and you might want to
consider going easy on the number of berserkers in your game!
Finally, we have the sniper, who would do well to stay out of range of the player perhaps in on the top of a
building somewhere:
case 1: // SNIPER AI
Page 56 of 71
if (%distance < 100){
%id.fire(true);
}else{
%id.fire(false);
}
%id.setAimObject($gspPlayer);
This case tells the AI, if the player is within 100 units, fire; otherwise, put the gun away and wait.
The Players ID
Finally, you may have noticed a global variable in there called $gspPlayer. We need to add this variable ourselves.
Although there are other ways of tracking the players ID, I find this method most useful.
Open the demo/server/scripts/ folder, and edit the file named game.cs. Find the createPlayer function, and add the
following boldfaced line, just below the new Player command, and before the call to MissionCleanup:
// Create the player object
%player = new Player() {
dataBlock = PlayerBody;
client = %this;
};
$gspPlayer = %player;
MissionCleanup.add(%player);
Now, whenever you want to refer to the player, you can use the global $gspPlayer variable.
Complete Code
Because were dealing with quite a bit of code, heres the total package.
Page 57 of 71
%id.setAimObject($gspPlayer);
case 1: // SNIPER AI
if (%distance < 100){
%id.fire(true);
}else{
%id.fire(false);
}
%id.setAimObject($gspPlayer);
case 2: // BERSERKER AI
%id.fire(true);
%id.setMoveDestination($gspPlayer.getTransform());
%id.setAimObject($gspPlayer);
}
schedule(200, 0, updateBot, %id);
}
We have seen this approach taken with Marble Blast, which is a completed Torque-based game sold by
GarageGames. We dont know if there is a tool for pruning the directory and files, or if it is done manually.
A DTS file can include a number of levels of detail, a specification of the mesh that is to be used for collisions, a
specification of multiple animations, and even facilities for describing where weapons attached to characters
represented through DTS, where muzzle flash is to be placed, and (for DTS files that are aircraft) where exhaust and
wakes are to be added.
There is a document available on the web that describes the structure of a DTS file, and it shows complex trees. Not
all modeling tools (or Exporters associated with those modeling tools) can generate all features of the file format.
There are two file types that you can export: shapes (DTS) and sequences (DSQ). A DTS file contains meshes,
detail levels, the bounding box, and all necessary nodes for a particular shape. Optionally animation data can also
be included in the DTS file.
DSQ files only contain animation data and require a matching DTS file that contains all of the necessary nodes for
the shape. There can be multiple DSQ files for any one DTS file. Also, DSQ files can be used on multiple DTS
files provided that the animation nodes in the DTS files match exactly.
There is a tool provided with Torque 1.5 called Torque ShowTool Pro, which can be used to view and verify DTS
files. It is described at the end of this appendix.
Page 58 of 71
Using MilkShape3D and Exporting to DTS
This is probably the oldest and simplest approach, and MilkShape is shipped with a DTS exporter. There was one
version of a DTS exporter, dated 2001, which was shipped with MilkShape. However, it appears to be incomplete in
some respects, and there is a newer exporter provided on the GarageGames web site.
The newer version is called DTS Plus Exporter and the DLL is ms2dtsExporterPlus.dll. It is also shipped with
Milkshape3D. The current version number is 2.7.2 and it was developed by Chris Robertson (MilkShape 1.8.2 and
1.8.3 comes with version 2.7.3 installed).
Create object, place all of the textures in the target directory (such as teleporter), then save the .MS3D file to that
directory, finally, choose Export as Torque DTS Plus from the File menu. This will bring up the following dialog
box:
Save the file into the same directory as the material textures.
With the scale left at 0.1, your object should be about 30 to 90 units on all sides. Smaller or larger will create a size
mismatch between the Torque Engine and its characters, and the imported files.
All of the items to be shown must be at the same LOD level (such as level 0), and they must have different names at
the same level. Since MilkShape doesnt always create unique names, you may need to change the names.
Create a collision mesh with a mesh that is called Collision. If there is no such mesh, then the object will be able
to be walked through. In order to avoid having the collision mesh appear in the engine, give it a negative level of
detail. The collision mesh must not be concave and should be a simple shape.
Page 59 of 71
The Create Bounds button is not the same as adding a collision mesh.
If there is animation in the file, select Export Animations, and use the Sequences portion of the middle of the
dialog box to define and name sequences of animation. You could create a walk, a jump, a crouch, a salute, etc.
sequence. We have never actually tried animation as yet.
There is a facility to copy all of the material files that are used into the export area. This allows you to create your
models somewhere else in the file system, then export .dts and materials into the file system used by Torque.
If you wish to have a collision mesh, there are two approaches: first, using generated collision meshes is requested
by creating a layer called collision: box or collision: cylinder. This will cause the exporter to create the smallest
box or cylinder that encloses the object.
To use a custom collision mesh, use layers collision1 through collision9 and place one convex object on each
such layer.
Your object should be about 5 to 15 trueSpace units on all sides to be scaled correctly for human scale.
Your bounds object should have its axes centered and at the bottom (usually 0,0,0) as this is the point where the
object will come to rest on the terrain.
Animation
Create a trueSpace clip for each animation sequence, such as Walk, Jump, etc. The start and end of the
animation cycle is therefore driven by the clip parameters.
Page 60 of 71
contained within the version 1 exporter, such as mount points, but it appears that if you are going to create such
models, you are better off using the version 2 exporter.
In addition to setting up the hierarchy in the correct way, there are many custom object properties that must be
configured in order to export your DTS shape and DSQ animation files. These custom properties are stored in the
object notes of the various objects and helper objects in your scene, which is a facility to attach textual notes to the
objects of the model, much like the Comment facility in MilkShape.
To open the object notes editor click and hold down on the Object tool icon (the arrow icon). Select the Object Notes
tool, which is the top icon from the flyout menu to open the object note editor. If you also have the Scene Editor
open clicking on any object in the scene editor will display the object notes for the current selected object.
There are two types of object properties that can appear in the object notes:
A property name followed by a space and a number. For example: startFrame 0
A property name followed by a boolean state (true or false). For example: twoSided true.
Only one property name and value pair should appear on each line in the object notes. You can also create comments
for yourself in the object notes by adding // to the beginning of the line. Everything after // is ignored to the end of
the line. For example. // Note to self.......
There is a third source of information used for the function of the DTS exporter, which is a configuration file,
located in the directory for the .scn file, which is a file called <fileName>.cfg in the same directory as your DTS file
is being exported to. The configuration file can contain several keywords that determine which objects are exported
and which objects are ignored as well as export parameters.
There are three lists that can be in the configuration file: AlwaysExport, NeverExport, and NeverAnimate. Node and
object names are placed into one of those three lists. By default, all objects are implicitly placed into the
AlwaysExport list. Names can include wildcards (*) to simplify writing configuration files. Lines with + or - along
with the parameter name turn on and turn off boolean parameters.
Lines with = set the value of valued parameters (note: the = occurs at the beginning of a line). If a nodes name
matches a name on the NeverExport list, that node will not be exported. Any meshes that are children of that node
will be parented to that nodes parent (or its parents parent if the parent is also on the NeverExport list).
If a node is on the AlwaysExport list, it will be exported even if there are no meshes on the node, and even if the
name matches a name on the NeverExport list. The AlwaysExport list takes priority over the NeverExport list. If a
node is on the NeverAnimate list, its animation will not be exported even if it contains animations.
Page 61 of 71
Right click on the reflectance shader and select an environment map shader from the reflectance shader
selector.
In your environment map shader use these settings
o Set the luminance slider to 0
o Set the diffusion slider to max
o Set the shininess slider to 0
o Set the specular slider to 0
Your object should be about 5 to 15 trueSpace units on all sides to be scaled correctly for human scale.
Your bounds object should have its axes centered and at the bottom (usually 0,0,0) as this is the point where the
object will come to rest on the terrain.
All Torque scenes must contain the following objects at the scene root level in order to export properly:
A bounding box named bounds. This defines the shapes orientation and position in the world. Without the
bounding box, the scene will not export.
The DTS hierarchy. This is a group in the scene root (base01) that contains at least one detail level marker
(_detail#) and at least one additional group with children (start01) that has geometry somewhere in its sub-
hierarchy and/ or a IK group that deforms a mesh.
A detail level marker indicates to the exporter what detail level mesh should be drawn at a given distance. The
number following the name of an object corresponds to the pixel size in the game engine at which the shape will be
drawn. The shape branch of the DTS hierarchy corresponds to the actual DTS shape that will be exported. The
whole subtree can be under one branch or there can be multiple branches (shape01, shape02, etc.).
Here are the steps to start from a visible shape and fill out the surrounding nodes:
Page 62 of 71
You can have a custom collision mesh. Vehicles are limited to ONE collision mesh for the collision shape.
Complex collisions can be created using multiple simple collision shapes
Collision meshes cannot be concave. In the Torque engine, the collision bounds are shown with a yellow box when
the object is selected in the game editor.
Triangulation is handled automatically by the DTS Exporter. Unless you prefer implicit control of the triangles in
your model it is fine to leave the objects in the scene as quads as the Exporter uses functions in the TSX SDK to
return a triangulated version of the objects in the scene at export and the objects in your scene are left as they are.
Since there is apparently no way in trueSpace to freeze the scale transforms of an object before export you must
export your objects (including the bounds) from trueSpace to the .X format and then load the objects back into
trueSpace. The .X exporter recalculates the vertex positions and recalculates the scale transforms applied to exported
objects. This will allow your objects to export at the proper size.
To export your object to .X format, select an object in your scene or group all of the objects in your scene and select
the object group then click on the file menu and choose: save as object and choose the .X format from the file type
selector. Click the settings button in the save object window and adjust the .X exporter settings to fit your needs. In
the setting dialog box, turn off all options.
Animation
After all of the modeling and scaling (using the .X export/load) has been done, you can add the animation to the file.
We have more discussion of this in the trueSpace notes.
While the Exporter does support a range of animation types, the one that works best is the bones and joints-based
animation.
Page 63 of 71
Performing the Export
Now you are ready to perform the export itself: Left clicking the Exporter icon will export all of the objects in your
scene as a DTS file and any sequences setup in the file will be stored inside the DTS file. Right clicking the exporter
icon will export all of the sequences in the scene as one .DSQ file containing all of the sequences setup in your
scene.
This step produces a dump.dmp file in the output area, which is a text file that contains a processing log for the
exporter. It can provide a trace of what objects were exported, how many nodes they have, etc.
Finally, you will need to copy all of the texture files used into the resulting area.
The screen layout is somewhat similar to MilkShape, with tools across the top, an animation control area across the
bottom, and the display in the middle.
The tools in the upper right change the view settings. The one to zoom in and out (magnifying glass) should not be
confused with the depth of field control (cross symbol). In fact, leave the depth of field setting at about
Page 64 of 71
Since many of our problems have been with the DTS hierarchy and constructing it in trueSpace for the Exporter, the
most useful button is the left middle one called Shape Properties. This brings up the following window:
This shows how the DTS file contains one detail object and one collision object, each of which is a standard mesh.
Related buttons allow the viewing the mesh as wireframe, textured, etc., and numerical read-out of the bounds and
other statistics of the mesh.
There are no facilities for actually editing the DTS file in this tool but the presentations that it gives are very useful
for verifying the file.
We are using version 6.4 alpha 4, which was current as of late 2004.
Overview of Use
We are working with 3 files: bridge, checkpoint, startfinish. Each of these are edited as .map files, and can be
converted to .dif.
Page 65 of 71
Directory Structure
Detailed structure of important files in QuArK directory:
C:\quark-6.4--|
|--QuArK.exe (QuArK executable file)
|
|--Setup.qrk
|
|--/addons-----|
| |--Defaults.qrk
| |
| |--/Torque----|
| |--DataTorque.qrk
| |--UserData Torque.qrk
|
|--/torque-----|
|---/base/textures/common (location of example textures)
|
|---/tmpQuArK--|
|--map2dif.exe
|--/maps (location of .map and .dif files after export)
|--/textures (empty except after "Prepare Used Textures")
With the Level Exporter, you can literally open up whole new worlds! Create the landscapes, buildings or other
environments that your game players will explore and inhabit. Below is a quick summary of what the Level
Exporter offers:
Use the trueSpace modeling tools that you are familiar with to create geometry
Ability to handle large levels and export them in pieces, so oversize levels can be easily created and then
used in game engines
Export to all packages that support standard MAP / WAD format, such as Torque, 3D Game Studio A6,
Blitz Basic, Dark Basic, Quake and Halflife
Import WAD files made in 3D Game Studio A6, Dark Basic, Quake, Halflife or any app that supports the
WAD2 or WAD3 format
WAD files have mipmapped textures
The Level Exporter is designed to export to many game engines and game development packages. However, not all
packages support the same options for MAP and WAD files, so for maximum compatibility, the Level Exporter
gives you access to a full range of options for the specific features of MAP and WAD that will be used, accessed
through easy intuitive menu options.
The Level Exporter has specific support for 3D Game Studio, Blitz Basic, Torque and Dark BASIC, and having
access to these output options ensures wider compatibility with other packages that use MAP and WAD files.
Note that game 'entities' such as switches, paths for enemy AI to follow, pressure points, teleport points, triggers, and
so forth, still need to be set up in your game editor or game development tool. However, the tedious step of
constructing the level geometry will already have been made simpler thanks to trueSpace and the Level Exporter!
Page 66 of 71
Notes:
The Level Exporter processes whole scene so there is no need to select items before exporting, etc.
Do not check Create WAD or the exporter will copy texture files to the output area that are not in the
correct format for Torque (they are for WAD-based engines).
Have the directory names end with a slash.
Make sure scale slider is non-zero. We have been using 10 to 20.
Set the base light level to about 30-40
Make sure that thickness is non-zero. We have been using 6.
Turn on Column Search, Block Search, and Connected Search.
Create each floor of the building to be about 1 trueSpace unit high.
The export is able to change most invalid geometry into valid geometry and we have tested it with intersecting
shapes in trueSpace. However, there are still several important aspects to keep in mind:
The LevelExporter and the Torque Game engine have different rendering facilities than trueSpace. These
facilities are focused on handling large numbers of simple blocks, where each block must be a convex
object. Thus, dont use trueSpace point editing operations that generate concave objects. These will fail.
Avoid use of the polygon boolean operations, as even when the result appears to be convex, it can still fail
(based on a number of tests)
Make sure that all of the textures used are square and a power of two.
Dont use any of the color shaders, use only the texture file shader. If you do use the solid color shader,
you will need to create a .jpg file for the color (it will have a name based on the hex code for the color).
Collect objects together using the group facility rather than the boolean union.
Surprisingly, the sweep and lathe tools can generate objects that will convert and render in the engine, even
concave shapes. However, this is not true for all such shapes.
Page 67 of 71
Specifically avoid the point edit command for Polygon Bevel, which draws a beveling rectangle on another
rectangle. This gives us two rectangles that are co-linear, a near-certain failure.
The second stage is the conversion of the map file to the dif file. In a command window, cd to the interiors
directory, then use the map2dif_plus utility, in the TGE/tools directory. The command line args are:
<torquePath>\tools\map2dif_plus.exe t . o . <filename>.map
You may get a warning about orphaned polygons or brushes. Orphaned polygons can safely be ignored. Brush
errors can be indicative of a problem. Some of the failure modes that we have seen include:
Missing texture
Collinear point warning
Message about 3 points for a brush
The map2dif_plus tool stops before writing the result
The map2dif_plus tool gets a segment fault that causes the Windows Error Report dialog box to appear.
You also need to move all of the texture files used into the directory where the .dif file ends up. We think that the
Torque Engine will also search upwards from that directory into the parent ones if needed.
We are now using map2dif_plus, which was released in early 2007, and is an improvement over map2dif, which was
released by the same author in 2005 to 2006. Features include:
Improved parser
Should be able to load Q1, Q2, Q3 (non-brushdef version), and Valve220 formats
Brush validator
Tries to insure only good brushes make it into the map conversion
Better error messages
Upped and/or removed some of map2dif's hardcoded limits
Allowed completely enclosed interiors
Console.log outputted for extra information and to help track issues
Non-power of two texture error reporting (instead of crashing)
General code cleanup and bugfixes
No brush limits
Upped winding limits
The Terraform Editor has two panes - the top pane displays information about the currently selected operation, and
the bottom pane shows the current operation stack. Between the two is a pull down menu for the creation of new
operations. The first operation in the stack is always the General operation (which can't be deleted). The values set
in this operation are used by subsequent generators and filters.
Page 68 of 71
Bitmap is an operation allows you to import an image file as your terrain heightfield.
The following filtering operations are supported in the editor, if they are applied after a generate, they will apply to
the generate output, while if they are applied on their own, they will be applied to the current heightfield:
Turbulence - perturbs its input by randomly moving mass around
Smoothing smoothes its input
Smooth Water - this is like the smooth filter, but is limited to smoothing terrain that is at or below the level
of global water height (set under General filter). No smoothing is done for features above the waterline
Smooth Ridges/valleys - As the name implies, this filter affects specific regions based on their
characteristics. Plateaus with jagged edges will be rounded at the edges while retaining their original
steepness. Deep dimples in valleys will be filled in. How much depends on settings
Filter - filters an existing operation based on a curve
Thermal Erosion - erodes an existing operation using a thermal erosion algorithm
Hydraulic Erosion - erodes an existing operation using a hydraulic erosion algorithm
Blend - blends two existing operations according to a scale factor and mathematical operator
When you select an operation, it is added after the currently highlighted operation (so you can insert new operations
into the middle of a list of existing operations).
Before we can start texturing our terrain, we need to decide which textures will be part of our palette. To load a
terrain, simple click the Add Material. button and select a terrain from the dialog that comes up.
The Terrain Texture Editor places our textures in layers. The first (top-most) texture in the Texture List is the base
layer. This is the texture that is visible if no other textures get applied to a point on the terrain.
Subsequently added Materials are always placed at the end of the list. These textures are applied based on an
algorithm and settings (or Placement Operations).
Every Texture gets a base filter called Fractal Distortion the purpose of this filter is to provide randomness to the
way the textures are placed. The interface is very similar to the Terraformer's fBm generator. The major difference
is the scale interface. To be honest we don't have a real clue as to the exact function of this device in this context,
but we can hand wave it. We are guessing that control points are levels of detail and that the vertical scale affects
how much of the prior texture(s) show through this texture after blending.
The steepness and height filters are relatively straightforward. The left side of the scale represents less steep terrain
and the right side represents more steep terrain. Again, the vertical scale is the blending factor.
We found that the playGui screen was subtlety altered when the child was added. Based on the premise that the
child might want the mouse events, the screen editor REMOVED the following two attributeValue pairs: noCursor
and helpTag, plus it added canSave=1. Since in our case, the child does not want mouse events, I had to manually
correct to the definition for the GameTSCtrl object back to the definition shown here:
Page 69 of 71
//--- OBJECT WRITE BEGIN ---
//--- THIS CODE WORKS ---
new GameTSCtrl(PlayGui) {
profile = "GuiContentProfile";
horizSizing = "right";
vertSizing = "bottom";
position = "0 0";
extent = "640 480";
minExtent = "8 8";
visible = "1";
cameraZRot = "0";
forceFOV = "0";
noCursor = "1";
helpTag = "0";
};
//--- OBJECT WRITE END ---
Questions pending: on 12/10/06: how do we have a bot automatically follow us? Rather than move randomly or
follow a path?
How do we attach dynamic state to an object? Initially we wanted a health value, but it appears that this is built into
the Player class. How about a Player achievement level or status?
Questions resolved on 12/07 through 01/08: The terraformer is a really important tool for creating interesting
landscapes. Use the fBM Fractal generator most of the time. After creating that, you need to set up the terrain
textures to have grass on the flat lowlands, and rocks on the steep parts, everything else is up for grabs. This in turn
can be done via the Terrain Texture Editor. The texture sets that look good are Tropics (actually a warm desert),
HighPlains (this somewhat reminds me of the Oregon High Desert), and we have created one for FrozenNorth.
The racing game starter kit is an example using this approach, and the track is simply an oval path through
interesting heightfield areas. The average height of the track is about 120, which is a typical mid-level value.
Questions resolved during late 01/08: clarifications on blocks, objects, and namespaces. It is much like Python.
Files generated during late 01/08: Created four .dif files for additional buildings.
Files generated during 05/08: Created several different weapons files, and tried out different particle effects for
them. Was able to control the projectile motion, the exhaust, and the explosion effects. Also handled aiming issues
and firing cycle times.
The students will start with an initial mission, which is a showcase for Torque features including terrain, sky,
lighting, smoke, interiors, water, and more. They will then modify this level.
The second phase changes the player to a dune buggy that can be driven around.
Terrain tilesets
Page 70 of 71
o High Plans
o Frozen North
o Tropical
Static shapes
o Trees
o Campfire + particle generators
o Items including healthpack
Several interiors
o Power/pulse generators dark texture, conic sections, arcs and cylinders
o Witchs House
o Great House
o Bridge
o Dock
o Racing items: barrier, ramp
Several scripts
o Dune Buggy
o Logo Item
The player class handles receiving the global variables which are the trigger counts. Trigger
variables 0 and 1 will fire the weapons located in mount slot 0 and 1.
Hence, there is no visible event dispatching for these events it is built into the Player
class.
Page 71 of 71