100% found this document useful (1 vote)
1K views

Game Programmers Guide To Torque

The Game Programmer's Guide to Torque is A GarageGames book. Book is dedicated to wife Teresa, for her encouragement and advice. Garagegames staff for making publication of the book possible.

Uploaded by

AimlessZealot
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
1K views

Game Programmers Guide To Torque

The Game Programmer's Guide to Torque is A GarageGames book. Book is dedicated to wife Teresa, for her encouragement and advice. Garagegames staff for making publication of the book possible.

Uploaded by

AimlessZealot
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1265

The Game Programmer's Guide to Torque

"

----

-----

-------

The Game Programmer's Guide to Torque


Under the Hood of the Torque Game Engine
A GarageGames Book

Edward F. Maurina III

A K Peters, Ltd. Wellesley, Massachusetts

---

--

-- -

_. - - - - - - - -

Editorial, Sales, and Customer Service Office A K Peters, Ltd. 888 Worcester Street, Suite 230 Wellesley, MA 02482 www.akpeters.com

Copyright C2006 by GarageGames, Inc. All rights reserved. No part of the material protected by this copyright notice may be reproduced or utilized in any form, electronic or mechanical, including photocopying, recording, or by any information storage and retrieval system, without written permission from the copyright owner. Set in ITC Slimbach and ITC Eras by A K Peters, Ltd. Cover image and art in the Advanced Maze Runner prototype by Christophe Canon.

Library of Congress Cataloging-in-Publication Data

Maurina, Edward F., III., 1969The game programmer's guide to Torque: under the hood of the Torque Game Engine / Edward F. Maurina III. p. cm. "GarageGames book." Includes index. ISBN 1-56881-284-1 (pbk. : alk. paper) 1. Computer games-Programming. I. Title. QA76.76.C672M36 794.8'1526-dc22 2006 2005056630

Printed in the United States of America 09 08 07 06 10 9 8 7 6 5 4 3 2 1

This book is dedicated to my wife Teresa, for her encouragement, her advice, and most of all for her tolerance of the odd hours I kept while locked away in my office writing this book. I must give special thanks to Jerry for acting as an idea bouncing-board and for listening patiently as I discussed chapter ideas over, and over, and .... Of course, I must also thank the many members of the GarageGames community for their unfailing interest in the guide and their encouragement. Lastly, I would like to thank the GarageGames staff for making the publication of this book possible, giving specific thanks to the "draft reviewers"-Josh Williams, Matt Fairfax, Ben Garney, Matt Langley, and Justin Dujardin.

Contents
Preface ix

Introduction
1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. 3

/I
2
3

Engine Overview
Torque from 10,000 Feet Torque Tools Introduction to TorqueScript 13 35 97

III
5
6
7

Game Elements
Torque Core Classes Basic Game Classes Mission Objects Game Setup Scripting Gameplay Scripting Special Effects Standard Torque Game Engine GUI Controls Game Interfaces 143 157 263 347 383 419 455 539

Gameplay Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201

8
9

10

11 12 13 IV
14

Making the Game


Putting it All Together Index 571 599

vii

--- ---

-- -

--

--~--

Preface
$0, you want to make a game? You may be standing in a bookstore holding

this book in your hands, or you may be reading this online. Whatever the case may be, some or all of the following thoughts and questions are probably running through your mind:
I want to make a game, but can I do it on my own or with a small team?

Making a game is great fun, and a very rewarding experience. You can definitely make a game alone or with a small team as long as you have the right tools available to you. One of those tools is the Torque Game Engine (TGE) and the other is Game Programmer's Guide to Torque eGPGT). Using TGE and GPGT, you can create any game that your imagination can encom pass and that your skills will allow. TGE sounds good, but will GPGT tell me what I need to know to make my particular game? TGE is a powerful and flexible game engine that can be used to make any number of different and unique games. You may choose to make single-player or multiplayer games. The game can be a shooter, an adventures, or a role-playing-game, to name just a few. Game Programmer's Guide to Torque will teach you the Torque skills you need to create these game types. (See section 1.1. "About the Torque Game Engine," and section 1.2, "What This Guide Contains," to learn more.) Can I get up to speed fast enough to make my game? Like any other complex and powerful piece of software, Torque can be hard or easy to learn. Everything depends on your approach to the task and whether you have the right resources available to you. With Game Programmer's Guide to Torque, with the hundreds of samples that come on the accompanying disk, and with the experience of making the sample game we write while reading this book, you will be able to ramp up very quickly and to move on to your goal-namely, making your own game. Having been down the path you are just now starting upon, I know how hard it can be to get started and how hard it is to stay motivated in the face of the many challenges involved with learning to use Torque along with the other skills you will need to acquire. I decided to write this guide so that others would not have to struggle to learn Torque. In closing, this guide is the result of my own need for a better reference and my desire to help other learn about the powerful and flexible Torque Game Engine. It is the culmination of my own game-writing and Torque-using journey. J sincerely hope that it provides you a pleasant beginning to your own game-making adventures.

ix

----

- - -------

--_.

__

.-.

- - ._._----

Introduction

Part I

',:0..

_.-

-_.-

-----'-

'--

Chapter 1 Introduction
1.1 About the Torque Game Engine
1. 1. 1 What Is Torque?
The Torque Game Engine (TGE) is a AAA 3D game engine made available to the indie games community by GarageGames. It is the product of many years of dedicated work and interactive design and development by the staff of Dynamix, a well-known game development company which the founders of GarageGames previously started. As Dynamix made games, they would reuse and refine, taking the best parts of their work to the next generation of the engine. With this engine, they produced games like Earthsiege, Starsiege, 1Hbes, and eventually 1Hbes 2. All in all, it is safe to say that the code in this engine has its roots in code written as far back as 1995 and perhaps even earlier. In summary, the Torque Game Engine is a product with man-centuries of development done by proven experts who time and time again used this engine to produce stellar titles. As far as I know, there is no other game engine like this on the market at any price.

1. 1.2 Why Should I Use Torque?


Educational: One of the best ways to learn programming is to read code written by other developers. If you are going to read code, you might as well have fun and read game code and learn a few tricks in the process. Resume BUilding: Mod rmodify) the engine to show off your skills to future employers. MOD Makers: How many times have you gotten stuck trying to mod other engines because they did not support feature X7 Now you have the source and can easily add any features you want and truly differentiate your mod from the rest. To Make Great Games! That's what we all live for, so do it. This is an unprecedented opportunity to build your game using an industry-proven game engine that rocksl -GarageGames Site

One of the beauties of the Torque Game Engine is that you don't have to use it to make games. "What's that, you say?" I repeat, you do not have to use the Torque Game Engine to make games. With the features included in this engine, you can just as easily make a variety of professional, educational, or "your category here" products.

Part I

Introduction

Of course, you must abide by the end user license agreement (EULA), but once you have licensed the engine, the terms of the agreement are pretty free about what you can create. The only real limitation is your own imagination.

1. 1.3 Not Just First-Person Shooters


Some people, examining the Torque Game Engine for the first time, may be under the impression that it is only for making first-person shooters (FPS). Nothing could be further from the truth. Yes, it is well suited to the FPS genre, but it can and has been used to make a variety of different game types.
Current Titles

Marble Blast GOLD

Think Tanks

Lore

Orbz

-- - - ------

Introduction

Chapter I

Sports

RocketBowl Plus

Golden Fairway

Educational

3-D Language Spain

Upcoming Titles
RacIng

Driving

dRacer

Part I

Introduction

Minions Of Mirth

1.2 What This Guide Contains


By the end of this book, you will understand how to use Torque, and we will even make our own little game in the process. This book aims to fill the following needs.

Learning guide. The guide is designed to quickly walk you through the concepts and learning required to get started on your own games. To that end, it comes with a lesson kit containing complete sets of ready-to-run sample scripts (from discussions) and sample lessons covering all topics discussed. Reference guide. To make this guide useful even after your preliminary learning experience is complete, the guide is formatted in a way that facilitates looking up specific topics. Also, it comes with quick-reference guides covering all TGE console classes, scripting, script functions and methods, GUI (graphical user interface) controls, etc. Prototyping help. This guide and the accompanying lesson kit in combination with the resources that come with TGE itself should provide all the materials you will need to create your own game prototypes. Teaching aid. The guide and associated kit have also been designed with the classroom in mind. The contents are suitable to support game-design courses. A specialized kit is included containing many ready-made lessons/samplers in the following categories: scripts, GUI controls, interfaces, HUDs (heads-up displays), and all of the major 3D/engine topics discussed in the guides. All lessons can "be extended, and new lessons can be added with relatively little effort.

1.2. 1 Summary
This book is intended as a starting point for the completely new user, but it is also suitable for the user who is moderately experienced with Torque already.
6

Introduction

Chapter 1

It has the following chapters:

Part I: Introduction Chapter 1: Introduction Part II: Engine Overview Chapter 2: Torque From 10,000 Feet. This chapter gives the ten-minute description of Torque and introduces the new user to important concepts and terminology. Chapter 3: Torque Tools. Here, we discuss all of the built-in tools and establish an understanding of how to use the TCE kit in editing mode. Chapter 4: Introduction to TorqueScript. Here, we introduce the embedded scripting language that comes with TGE. This chapter covers the complete syntax and the major concepts required to work with this scripting language. Part III: Game Elements Chapter 5: Torque Core Classes. This chapter examines the core scripted classes and their importance in the hierarchy, structure, and behavior of the engine. Chapter 6: Basic Game Classes. Here we cover the basic classes used to represent shapes, images, and interiors. Chapter 7: Gameplay Classes. This chapter reviews the classes through which we implement game interactions that define the gameplay. We also introduce important game-design concepts like the inventory. Chapter 8: Mission Objects. TCE provides a myriad of object classes. In this chapter, we discuss all of the mission/game/level placeable objects that have not yet been discussed, excluding special effects. Chapter 9: Game-Setup Scripting. Here we work through in-depth discussions of critical scripting classes and features that are associated with setting up and maintaining a game. Chapter 10: Gameplay Scripting. In this last scripting chapter, we examine a variety of scripting functions. examining how they work and providing the context in which they contribute to gameplay. Chapter 11: Special Effects. This chapter splits out several classes used for audio and visual special effects. Chapter 12: Standard TGE GUI Controls. In this chapter, we discuss the 32 most important GUI controls. Chapter 13: Game Interfaces. This follow-on chapter builds on the last chapter and walks through the creation of two sets of themed game interfaces. Each set includes a splash screen, a main menu, and a credits screen. We also specify and design three types of HUD to show that complex HUDs can readily be created from basic TCE controls.
7

Part I

Introduction

Part IV: Making The Game Chapter 14: Putting It All Together. Here, we build a complete singleplayer game prototype. We plan the game; accumulate many of the example scripts, shapes, interiors, and interfaces created in the prior chapters; and glue them all together with a small set of gameplay-specific scripts. Appendices. In order to facilitate the learning process and to fill the role of reference, an extensive set of appendices is included in electronic form. These appendices include complete references of game class fields and methods, console functions, callbacks, and GUI controls, to name a few.

1 ~3 What This Guide Does Not Contain


This guide obviously does not contain the answer to every question that every person who uses Torque can come up with. My hope is that it contains enough information and is accessible enough that you can learn how to answer these unanswered questions on your own. However, sometimes that just isn't going to happen, so a "Getting Help" appendix has been included to assist you. First, though, let's determine what you should know before starting.

1.4 What You Should Know Before Reading This Guide


Ah, the fateful question, "What do you, the reader, need to know?" First, understand that this guide is here to help you, but you are going to have to do some real work to learn what it has to tell you.
TANSTAAFL: There ain't no such thing as a free lunch.

-Robert Heinlein

Second, since this guide is aimed at a broad audience, I have created a pseudo-matrix below listing topics you should at least be passingly familiar with based on your role in your team.
Who Are You?
I'm like, the artist, dude.

Some Stuff You Ought To Know


Basic modeling and animation concepts: convex vs. concave, skeletal animations, texture (IFL) animations, blended animations. At least one modeling tool in this list: MilkShape 3D (MS3D), 3ds Max, Maya, or gameSpace/trueSpace. (Yes, there are other options; see appendix.) If the only tool (in the prior list) you know is MS3D, then be sure to add QuArK, cartography Shop, or Hammer to your list. More? Sorry, this isn't an art guide. In fact, you probably know more about art than I do!

Introduction

Chapter I

WIIo Are You?


I'm the programmer, man. You got any Dewsky?

!ioIne stuff You OUght To Know


C/C++ Scripting in Perl, TCl, or perhaps another game-engine scripting language. Math. If you flinch when I say algebra, geometry, trigonometry, vectors, matrices, or cauchy-Schwartz inequality (OK, you can flinch on that last one), Math World (http://mathworld.wolfram.comJ) is your friend. If you are familiar with client-server architectures and simulation concepts, you will have a great head start.

;.'

I am the game designer. Enter my world ...

The limitations of your target system(s) as well as the limit on the speed of light. Sure, your team can probably make a 4096-player RFMMOTTIG (really freakin' massively multiplayer online tick-tack-toe game), but you probably don't want to. I'm only partly joking here. If this is your first game and you are the idea guy or gal, keep it realistic. There is real work in making a game. The limits of the tools your team's programmers, artists, and other folks use to implement your ideas. What the ... ? Why are you reading this ... er, I mean you should know of course, Sir/Madam/Other, every member on your team will need two copies of this guide. One for work and one for home. We can't have people getting hernias carrying these back and forth. This guide is specifically written for you (and for small teams). You will need to know everything on the list above (excluding da boss). Also, be sure to look at the reference appendix and get your hands on some of those books and resources. You've got a real challenge ahead of you, but you can do it!

Me da boss.

Jack of all trades (JOAT), AKA lone wolf.

1.5 How To Obtain Torque (Licensing TorqueJ


OK, so you're sold. You've bought this book (please tell me you're not standing in the bookstore still deciding ... ). Whatever the case, you have decided that this Torque thing sounds like a good deal. To get your hands on this state-of-the-art engine, simply do the following: 1. 2. 3. 4. Visit the GarageGames website: http://www.garagegames.com/. Follow the links to the products page. Add Torque to your cart. Click the "Buy Now" button and follow the instructions.

-------

Part I

Introduction

1.6 Getting Started, One Step at a Time ...


On first picking up the Torque Game Engine, you may be somewhat overwhelmed. If asked, most GarageGames members will probably admit that they were, too, and so was 1. The fact is, this engine and all the associated files are massive. Just doing a quick count on the current version of the code brings up the following metrics (counts may vary): 2329 source files containing 593,930 lines ("d25k lines of code and lines of comments),
N

167k

322 script files containing 49,856 lines of script (N37k lines of of script, and N7k lines of comments), and this guide comes with a kit that adds another 187 script files containing 19,566 lines of script (N 11 k lines of script, and N 5k lines of comments). No matter how you twist it, turn it, chop it, or sort it, Torque is big. Big not only in raw size but in features. However, approached with an inquisitive mind, and with the understanding that nothing is free, especially an understanding of the ins and outs of this engine, you can master Torque.

1.7 "rhe GarageGames Community and Resources


I've stated this in more than one forum, and I must state it here: the GarageGames community is excellent. 1 continue to be impressed on a daily basis by how well attended the forums are and how quickly people give answers to questions. The GarageGames site provides several resources. Forums. These are areas where you can post questions, ideas, general complaints, etc. To date, there have been tens of thousands of posts. At last count (not including forums dedicated to released games), there were 12 major forum categories containing 64 subcategories. Resources. These are community submitted items including scripts, code, web links, books that are good to read, accumulated references, and more. These resources are organized by date and rating (among other categories) . News. The GarageGames site has a news page and a newsletter. Very cool.

1.8 Conventions
Throughout the guide, I will attempt to align my naming conventions and terminology with those you will encounter in the official Torque SDK (software development kit) documents and elsewhere on the GarageGames site. In the cases where this is not possible, I will make it clear that the names/terms in use are of my own invention.

10

Introduction

Chapter I

1.8. 1 Icons Legend: Warnings, Notes, and Expert Tips


Throughout this guide, you will be presented with side notes of various forms. Some of these will be warnings of odd or misleading behavior, others will be notes on interesting bits or facts, and some will be expert tips for those who want to explore the edges of Torque's behaviors. You will be able to recognize these side notes by looking for the following icons.

Warning

Note

Expert Tip

1.8.2 Game-Building Lessons


Throughout the guide, you will find sections marked as one of the following:
1. Maze Runner Lesson 11123 (90 Percent Step). If you intend to make the game at the end of the guide, you must complete these lessons. They construct game elements without which the game will not function.

2. Maze Runner Lesson 11123 (10 Percent Step). These lessons are considered optional when making the initial version of the game. If you should choose to skip them, the game will still be playable but may be a bit rough around the edges. These lessons will be largely independent of each other, but if a lesson depends on another lesson, the numeric ID of the lesson, as well as the chapter it is in, will be referenced.

Combined Lessons Appendix


For those who want the entire lesson set in one place, all of the lessons from the printed chapters, up to but not including Chapter 14, are included in the "Combined Lessons" electronic appendix.

Skip Ahead! To learn about the motivation for the above lesson titles, and to learn what the game will be, please skip ahead to Chapter 14. There, you should read Section 14.1, "Maze Runner: A Simple Single-Player Game," which includes the following.
11

ParT I

Introduction

Game Elements. Here, we will briefly discuss the concept of a game element. Game Goals, Rules, and Mechanics. Next, we will explore the motivation for planning a game's goals, rules, and mechanics before we write the game. Then, we will do this planning for our game. Setting up our workspace. Before we can start working on the lessons, we need to set up a workspace. In this section, [ will instruct you on what steps are required to prepare for the lessons. 90 Percent or 10 Percent? Lastly, I will give you an overview of the 90 percent versus the 10 percent steps and why these ideas matter. So, skip ahead; it's OK. When you're done, you can come back and start learning about Torque!

12

\_---

En ine Overvievv

Part II

..

~-

~-

--.

--~

-'.-.-

---

-- --

.~---

Chapter 2 Torque from 10,000 Feet


The Torque Came Engine (TCE) has a long legacy. In its various incarnations, it has been used to make both non-networked single-player games and networked multiplayer games. Today, TCE has the following features.

Single-player and muitiplayer ready. TCE is based on a standard clientserver architecture and is fully scalable to 128 players and beyond. Raster-based graphics. TCE is not shader based but has the capability to incorporate any features you desire (you have the source code). Furthermore, it is the predecessor to the Torque Shader Engine (TSE), and thus most things learned using TCE will apply to TSE. Event-driven simulation. TCE is designed around an event-driven simulator. It utilizes separate client and server event loops. Additionally, most game logic and CUI logic is driven by an event system. Memory and network bandwidth efficient. TCE is designed to have a reduced memory footprint and an accompanying low-bandwidth requirement per connection. It utilizes static datablocks for common information and network compression plus transmission-reduction algorithms. Broad functionality. Because of its long heritage, TCE comes ready with most of the methods and functions required for standard game calculations, actions, and responses. Fully integrated. TCE incorporates all the code required to render/play/ capture all game elements, including CUIs, sound, 3D graphics, and other I/O (input/output). It also includes a large and expanding set of content creation and debugging tools out of the box.

2.1 TGE Terms and Concepts


When you first start working with TCE, you will come across terms like interior, shape, datablock, portal, [FL, image, etc. Some of these words have TCE specific meanings, others are industry-standard terms, and a small set are hybrid terms with meanings in both worlds. Either way, if you are not very experienced, just trying to figure out what these terms are may be a big challenge. To help ease this transition, we will run through some of the more confusing terms and concepts you will encounter while working with TCE. For a more extensive list of terms, see the "Clossary Of Terms" appendix.
15

Pan: II

Engine Overview

2.1.1 Shapes and DTSs (TGE TermJ


A shape, also known as a DTS object, is a model created using a polygon (or equivalent) editor. Such models may have skeletal animations (see Section 2.1.8, "Animations: Blended vs. NonBlended"), multiple skins (textures), animated skins, visibility animations, multiple levels of detail (see Section 2.1.5, "Level of Detail"), translucent and/or transparent components, multiple collision boxes (see Section 2.1.6, "Collision Detection"),

and much more. This is the first of two model categories llsed by TGE. DTS, which stands for the Dynamix Threespace Shape, is both the shorthand notation for this concept and the file extension (e.g., player.dts). Shapes are generally used to represent nonstructural entities such as players, power-ups, trees, and vehicles. Shapes can be created with 3ds Max, MilkShape, or Caligari's gameSpacej trueSpace, to name just a few possible content-creation tools. See the GarageGames website to learn how this is done and to find the proper exporter for your content tool(s).

Non-DTS Renderers?
Some users have complained that they would rather use an alternate format instead of being "forced" to use the DTS format. This is entirely possible. Users have already produced alternate mesh renderers to include such formats as 3DS and MS3D. If you have a favorite format and are familiar with how it works, you can simply pick up one of the previously mentioned mesh renderers and modify it for your own format.

Shapes in Our Game


In the prototype for our game, we will need just a few shapes: a player, coins, maze blocks, and fireballs.

An avatar or player. The lesson kit comes with Joe Maruschak's "Blue Guy" (Figure 2.1, left), but we will not be using him beyond a quick introduction. Why? In order to demonstrate the minimum set of animations that need to be included to make the shape work with the Player class, we will make the "Simplest Player" (Figure 2.1, right), a simple geometric shape. Pick-ups, maze blocks. and fireball blocks. In our game, we will also require shapes to represent coins that we can pick up. Also, we will need
16

Torque from 10,000 Feet

Chapter 2

Figure 2.1.
Simple Player shapes.

Blue Guy

Simplest player

Figure 2.2.
Required shapes and blocks.

Coins

Maze blocks

Fireball blocks

a variety of blocks and obstacles (fireball blocks) to build our mazes from (see Figure 2.2).

2. 1.2 Interiors and DIFs lTGE Term)


Interiors are models created using convex (see Section 2.1.3, "Convex vs. Concave") brushes. The InteriorInstance class, frequently referred to simply as Interior(s), is used to display models that represent any structural object, to include such things as buildings, bridges, walls, and other large structures. The motivation for this name comes from the fact that these objects can have an actual inside, Le., interior. This modeling technique is used to solve a few technical issues associated with creating large and geometrically complex models that are intended to be entered by other models (or the camera). Some of the biggest technical problems solved by this technique are the following.

I,

Efficient collision detection. Binary space partitioning (BSP) trees are generated and used for detecting collisions against Interior objects. BSP trees provide a very efficient way of determining object collision, one of the most CPU-intensive processes a real-time application performs.

17

-_. - -

--

_..

_--

--------

--_._--

-- --

-------

Part II

Engine Overview.

Visibility culling. This technique also provides numerous shortcuts for culling of visibility through the use of portals (see Section 2.1.7, "Portals") so that rooms and terrain that the player can't see don't get sent to the graphics card for rendering. This is a lot harder to do, from a mathematical standpoint, than a nonprogrammer might imagine. Efficient lighting. Finally, this technique "regularizes" (to abuse the English language a bit) the process of calculating lighting and shading as affected by the presence of the model in the game world.
DIF, which stands for Dynamix Interior Format, is both a shorthand notation for the same concept and the extension for these files (e.g., myBuilding.diO. Interiors can be created with QuArK, Worldcraft/Hammer, 3ds Max, MilkShape (not advised), or Caligari's gameSpace/trueSpace. See the GarageGames website to learn how this is done and to find the proper exporter for your content tool(s).

2.1.3 Convex vs. Concave IIndustry TermsJ


In TGE, all collision meshes must be convex, not concave. The trouble is, many people either do not know what these terms are or cannot remember how to identify a convex or concave mesh. Finding the parts of a mesh that are concave (making it a bad collision mesh) can be frustrating at best. Therefore, you can follow this simple rule when making collision meshes:
If any line segment on the mesh, when extended infinitely in both directions, passes through the interior of your mesh, the collision mesh is concave and therefore bad.

Or the shorter version:


line segment passes through interior of collision mesh ... bad (Figure 2.3).

Figure 2.3. Using line segments to discover concavity.

Line segment passes through-{;oncave

Problem so!ved-{;onvex

18

"------

Torque from 10,000 Feet

Chapter 2

Figure 2.4.

Using dimples to find concavity.

Has dimple-concave

Problem solved-convex

Alternatively, you can examine your mesh and look for dimples, that is, regions where the surface curves inward. (Figure 2.4)

2. 1.4 Convex Brush (Industry Term)


A convex brush is a single instance of some regular convex geometry. Convex brushes are combined to create models that can then be converted into an interior. In TGE, anyone interior may be composed of many hundreds or even thousands of convex brushes.

2.1.5 Level of Detail (Industry Term)


Often referred to as simply LOD, level of detail pertains to the complexity of a 3D model relative to the current viewing distance to that model. This complexity increases or decreases as the camera (the viewer) moves nearer to or farther from a shape, respectively. In TGE, both Shapes and Interiors support the ability to automatically substitute new models for a Shape or Interior as the distance from the Shapel Interior changes. These substituted models should have fewer polygons as the distance increases. This has the effect of reducing the rend~ring load for distant objects, increasing overall frame rates. Properly done, this allows for the creation of complex and densely populated indoor, outdoor, and mixed scenes.

2. 1.6 Collision Detection, or COLDET (Industry Term)


Collision detection (COLDET) can loosely be described as the process of detecting when two or more objects (in the simulated world) come into contact with each other. COLDET is a feature that enables interactivity in the game world. TGE (I.4 +) supports a number of unlimited collision detection bounding shapes for polygon models. This means that the level of COLDET

19

Part II

Engine Overview

interaction is completely under your control. Additionally, TGE provides automated generation of COLDET structures for some Shapes and for Interiors, thus reducing your responsibility while not reducing flexibility.

Collisions in Our Game


Like many games, our game relies on collisions for parts of the interactive experience. In particular, we will want our player to be "killed" if he is struck by a fireball. We will want the player to be able to pick up coins and grenades, which are part of the game's objective.

2.1.7 Portals (Industry TermJ


As was noted above when we discussed Interiors, TGE supports portalized rendering of interior models. That is, Interiors support the insertion of portals. These portals will divide an interior into sectors. In Figure 2.5, we have a single interior with four rooms, numbered 0 through 3. There are three doors in this interior. The thin lines in the picture are portals situated within the doors that connect each room. In Room 0, there is an observer A, and in Room 2 there is an observer B. Observer A is facing the door between Rooms 0 and 1. Because a ray cast from Observer A's position can penetrate both the portal between Rooms 0 and 1 as well as the portal between 1 and 3, all three rooms (0, 1, and 3) must be rendered for Observer A, but Room 2 does not need to be rendered. Observer B is facing the door between Rooms 2 and 3. Because a raycast from Observer B's position can only penetrate the portal between rooms 2 and 3, only these two rooms (2 and 3) need to be rendered for observer B. The other two rooms (0 and 1) do not need to be rendered. In both ofthe above cases, if no portals were used, or if the feature were not available, for both Observer A and Observer B, all four rooms would need to be rendered.

Figure 2.5.

Interior with portals.

o
2

2. 1.8 Animations: Blended vs. Non-Blended (Industry TermsJ


In TGE, meshes (models) are animated using skeletal animation. The engine supports two styles of skeletal animation: absolute (non-blended) and blended. In simplest terms, absolute animations override all prior animations of all joints that the absolute animation affects. For example, we have an animated arrow. This arrow has a base position, a non-blended animation to the left, and a blended animation to the right. Assume that the left and right animations are equal and opposite each other.
20

'-- -

Torque from 10,000 Feet

Chapter 2

If we play the sequences in Table 2.1, we get the listed results.


5equence(s)
Non-blended Blended Blended followed by non-blended Non-blended followed by blended Non-blended followed by non-blended

Result
Arrow leans left. Arrow leans right. Arrow leans left. Arrow back in base position (straight up). Arrow leans left, just as if it were non-blended only once. Arrow leans twice as far right as single blended.

Table 2.1.

Blended and non-blended animations.

Blended followed by blended

In Chapter 7, "Gameplay Classes," we will build some real animations, but if you wish to learn more, I suggest perusing some of the online animation docs at GarageGames and/or purchasing BraveTree: Girl Pack (see "Favorite Resources" appendix for details on where to find these).

2.1.9 Image File Lists, or IFLs (TGE TermJ


Another kind of animation supported by TGE is texture animation. The premise of this animation style is that the engine will swap the current texture for another at fixed time intervals, thereby animating the texture in question. This animation is accomplished by specifying texture names in a special way, identifying the texture as an IFL-driven texture (in the model definition). Then, a text file is supplied (by the modeler), specifying the names of the textures to use and the number of frames to play each texture. Beyond this, the animation sequence is played like any other animation.

,
",

2.1.10 Callbacks (Industry TermJ


For the purpose of this guide, a callback is any console method (scripted function associated with an object in the game world) that is automatically (or directly) called by the engine (or scripts) in response to some event. These callbacks are part of what drives a game.

Callbacks in Our Game


Although we do not strictly focus on callbacks in this guide, several of them will be required to complete our game. Therefore, at the appropriate time, we will take a little time out to discuss and clarify those callbacks that are in fact needed: onCollision () , onPickup () , and others.
21

i)art /I

Engine Overview

2.1.11 20 and 3D Sound (Industry TermJ


In the GarageGames forums, online documentation, and in this guide, you will see references to sound as being either two-dimensional (2D), or threedimensional (3D). Although odd sounding (no pun intended), these concepts are quite simple. A 20 sound is a sOWld that has no apparent origin, and when played, will play equally loud from the left and the right speaker (assuming you have only two). A 3D sound has (at a minimum) an origin associated with it and is thus transformed and attenuated based on the listener's location relative to the sound's source. That is, 3D sound may play more loudly from one speaker than the other(s). Please note that 3D sounds can have several other factors associated with them, and that this code exists in the engine. However, all other specialized 3D sound effects are not (by default) compiled into the engine.

Sounds in Our Game


Our game would not be complete without sounds, both for the interfaces and for the game itself. So, we will take time out in later chapters to walk through the setup of the following sounds: Splash-screen music (20 non-networked), This sound plays when the splash screen is displayed. Button-over and button-press feedback (2D non-networked), These sounds play to indicate that the mouse has moved over a button, or that a button has been pressed. In-game music (2D non-networked). We will learn to play music client side. Fireball warning (3D networked). This sound will be played when a fireball is about to shoot and will give warning in advance of the action. It is the only networked and the only 3D sound we will work on.

2.1.12 Missions (TGE Term)


In the gaming world, there are many words used to described similar things. One of those things is a game level. In Torque, a game may have one level or many. These levels are called missions. Another way to come to grips with the mission concept is to understand what goes into a mission file. Mission files (stored under the data directory) have the extension .mis. If you were to open one of these files, you would see that it contains a script that is creating and placing content. So in effect, a mission can be thought of as a collection of content that is loaded by the

22

Torque from 10,000 Feet

engine upon request. In fact, in most games, the mission is the primary means of loading the initial content such as the terrain, sky, sun, etc. Subsequently, game setup and gameplay scripts may be used to add and remove content, but we get our start by loading a mission.

Do I Have to Use Missions?


Well, you don't actually have to use this construct, but it is the best way to get the base portions of a level/game/etc. loaded. So, if you are expert enough, you can dynamical1y build the entire level/game/etc., but I do not suggest it.

How Big Can a Mission Be?


It is worth noting that a Torque mission can be extremely large. In fact, I

know that one of the GarageGames employees (Matt Fairfax), as part of some research he was doing, loaded all of the interiors from every level in Quake lJfM simultaneously into a single Torque mission. He mentioned that there was no noticeable dip in frame-rate nor did the engine lag at all. This was in fact a small test of the true power and capabilities of the engine.

Missions in Our Game


Our game wi11 utilize a single mission. It will load a terrain, the sky, a sun (lighting definition), celestial bodies, and various other atmospheric effects. Subsequent to the initial load, we will be using scripts to dynamical1y load and unload content from our mission. That is, we will stay in the same mission but use scripts to build and rebuild levels of the game, without ever reloading the mission.

2.1.13 Event-Driven Simulator (Industry Term)


TCE, like all other game engines, is a simulator. If you are at all familiar with the concept of simulation, you will know that there are different types of simulators. TCE is an event-driven simulator. ]n other words, all engine actions are caused by some kind of event. There are a variety of events that TGE is aware of and which we will discuss as we continue through this book. These events are enqueued into one of three queues (depending on the event type) and then processed by the engine in the order in which they occurred. At this point, the important thing to understand is that events drive the game world and thus all of your game scripting. and coding should be designed with that in mind.

23

-- -_._---.-._---_.. _ - -

Part II

Engine Overview

2. 1. 14 Ticks (TGE Term)


In TGE, time is measured in terms of wall-clock time, that is, multiples of milliseconds (ms). Additionally, TGE measures in simulation time. Simulation time is called tick time or simply ticks. Because TGE is effectively an event-driven simulator, it cannot always guarantee that events will occur on specific wall-clock time boundaries. Tick time provides a new measure of time that is under the control of the engine itself, allowing it to guarantee that all objects will get their allocated number of ticks and that they will be ticked in the proper order. The elegance of this solution trivializes the significance of this problem. Just understand, without a solution to the guarantee problem, it is for all practical purposes impossible to simulate a multiplayer interactive world, not to mention the problem of handling the additional burden introduced by a networked environment. Generally speaking, a standard TGE tick is equal to 32 ms, by default. Events occurring on tick boundaries will normally experience an actual tick time of 32 ms, plus or minus 1 to 3 ms.
Be aware that the granularity of a tick can be changed to suit your own game (or other) needs; i.e., tick times of 2 ms, 16 ms, 64 ms, or even 128 ms are all legal tick times.

~
~

2.1.15 Client-Server Architecture (Industry Term)


It is important to understand the architecture used by a game engine as it affects the decisions you, the game designerjprogrammerjscripter, will make. In the context of a game engine, the term architecture can be loosely translated as, "the organization of the game systems." In other words, "What parts of the engine do what tasks?" TGE implements a client-server architecture. When we talk about a client-server architecture, we're talking about an organization wherein one part of the engine acts as a sort of controller (the server) and the other part of the engine acts as a controllee (the client). While executing, the client and the server may either co-exist in the same executable, execute separately on the same machine, or execute separately on separate machines connected over a network. For a standard client-server architecture, there will always be one server while there may be many clients. The server is aware of all clients, and the clients mayor may not be aware of each other. The client-server architecture is suitable for both single-player and multiplayer games. We will discuss variations on executable "modes" and "interconnects" momentarily.

24

Torque from 10,000 Feet

Chapter 2

Why Use a Client-Server Architecture? This architecture has become common in the game industry for a few reasons. First, because it provides a meaningful and understandable way of dividing labor and resources. Second, because (as stated previously) it is suitable for both single-player games and multiplayer games. This means that a game can be designed for both single play and multiplay without herculean effort. Third, because, in the case of multiplayer games, this architecture scales well for N players, where N can be up to 128 or higher. This architecture does have some drawbacks when writing a single-player game such as unneeded duplication of objects (see Section 2.1.17), and some added control complexity. However, the multiplayer benefits far outweigh these considerations. Also, it cannot be stated too often, having the ability to take a single-player game to the multiplayer arena with few or no changes is well worth the added complexity. The TGE Client-Server Modes and Connection Schemes Torque implements the client-server model using a single executable. That is, whenever the engine is run, it contains both a server and a client. In order to implement different game types, the server or the client can effectively be disabled. In essence, the engine can be run in one of the four modes shown in Figure 2.6.

Figure 2.6.

Modes for running Torque

Single-Player (Industry Term)

Listen Server (Industry Term)

Remote Client (Industry Term)

Dedicated Server (Industry Term)

25

Part II

Engine Overview

The observant reader will point out, "The single-player and listen-server modes look quite similar." You are in fact correct. In fact, in the single-player image, the implication is that there is a server, but it has not yet been activated. This activation will not occur until a connection is requested by the client. Also note, in single-player mode, the server will not accept external connection requests. The listen server, on the other hand, does have an active server, and it will accept both internal and external connection requests. Given these four modes, a game can be interconnected using one of three connection schemes. The connection scheme we select is based on the game type we wish to run. The simplest game type is the single-player game (Figure 2.7a). This is accomplished by running a single instance of the executable on one machine. In this case, the server and client connect via an internal (local) connection. When this connection is requested, the server becomes active. The second game type involves a single executable with an active client and an active server running on one machine as a listen server (Figure 2.7b) One player (the hosting player) uses the local client and a local connection. The remaining players use client-only executables, running on separate machines, and connect remotely to the listen server. This mode is appropriate for LAN (local-area network) parties and other cases where a user wants to host a game while participating. The last game type involves a single executable running as a dedicated server (only the server is active; Figure 2.7c). Multiple client-only executables, running on separate machines, can then connect with this executable, again allowing for multiplayer games. Although this could be used for a LAN party, it is more suited to a professional hosting setup, where your company hosts one or more sessions on a machine used only as a server.

Figure 2.7 .
Client-server interconnection diagrams.

a. Single-Player

b. Multiplayer Listen Server

c. Multiplayer Dedicated

26

Torque from 10,000 Feet

Chapter 2

Master Servers ,Industry Term)

In the two above multiplayer connection schemes, the remote connections may be on a LAN or across the Internet. In the latter instance, another server is required, namely a master server. It is the job of this specialized server to assist clients in locating game servers.
TGE Client-Server Division of Labor

As was noted above, using a client-server architecture allows one to divide both labor and the location of resources (assets). Table 2.2 shows a summarized listing of the labor division between the TGE client and server.
",' Jr.

alent ReIponsIbIIIties
2D sounds capture and pre-process All processing and rendering All Non-authoritative prediction

server ReIponIIbll_
3D sounds Post-process and determine response None None Authoritative calculations and interactions Authoritative calculations and responses Players, vehicles, weapons, etc. (Optional) validation of all content All decisions regarding object creation, deletion, movement, damage, etc.

Table 2.2. Division of labor between TGE client and server,

Sound Input GUI rendering Game rendering Animations

Collision detection Game content

Non-authoritative prediction

Interfaces Ownership of content. Limited to things that do not affect gameplay, such as particle effect calculations

Game decisions and calculations

In short, the client is responsible for all tasks except those that affect gameplay or those that require spatial calculations in the game world.
Client-Server Communications

This book focuses on making a single-player game and thus does not discuss networking in any great detail. However, it is important to avoid forming bad habits. One of these bad habits is direct manipulation of server data/routines from the client and vice versa. Thus, in Chapter 10 we will talk briefly about how to execute server functions from the client and how to execute client functions from the server.
27

Part II

Engine Overview

2.1. 16 Objects flndustry Term)


Throughout this guide, you will see the term object being used to refer all kinds of things, including GUI controls, shapes, interiors, and various scripting elements. This may be confusing, but in Torque, all classes used to implement the game are in fact engine objects. Some objects are accessible via the console, and therefore scripts. Some are only accessible internally (by writing C++). In this book, we are only interested in the former.

2.1.17 Ghosts, Control Objects, and Scoping fTGE Terms)


When we are playing a singleplayer or a multiplayer game, all objects are created on the server and then some of these objects are duplicated on the client. These duplicates are called ghosts (Figure 2.8). The duplication of objects as ghosts on the client(s) is controlled by scoping. Each client that attaches to a server must define a single control object. Generally, this control object is some type of avatar (biped, vehicle, or other), but it may also be a camera. Regardless, this control object is responsible for scoping (Figure 2.9). Scoping, in TGE terms, is the act of determining which objects in the game world are visible, audible, and otherwise required to be present for the current control object to correctly interact with the game world. These objects will be ghosted to the client for that control object and subsequently maintained. This description trivializes the act of scoping to some degree, but it does describe the essence of what it means and what it does. I will repeat it, but for now be aware that your game must have a control object, otherwise it will be unable to render the game world.

Figure 2.8. Ghosts on the client.

Figure 2.9.
Control objects.

28

'-

Torque from J0,000 Feet

Chapter 2

2.1.18 Datablock (TGE Term)


In addition to normal objects. there is a special category of objects called datablocks. Datablocks are special for the following reasons. All datablocks are duplicated from the server to each client. A datablock XYZ on the server with ID 123 is guaranteed to have the same name XYZ and lD 123 on all clients. Datablocks are transmitted to clients at the beginning of a game and not updated after that, making them in effect static. Datablocks have special scripting properties, which we will discuss later. Because the content of a datablock is controlled by the server and not the client, they are an efficient means of preventing cheating (clients modifying their own game abilities and statistics). You may ask, "Why do I really need these datablocks?" and that is a valid question. In answer, please consider the following theoretical example. In game ABC, a multiplayer game, players are allowed to "create" a variety of wheeled vehicles. Each of these vehicles has between four and eight tires, skins, special effects (sounds, dust emitters, etc.), and a rather lengthy list of physical attributes. The complete structure describing these vehicles has a memory size of approXimately 2048 bytes (2 KB). At any time during the game, in which there are up to 32 participants, a player (client) may create a new vehicle. With a client-server architecture, this would require that the server create the object and then ghost all of its data to the player. It is easy to see that in the worst case, where all of the players are within visible range of the other players, the server might have to simultaneously ghost 32 x 2048 bytes of data to each of the 32 clients to inform them of the update. This translates to an update of 2 MB of data that would be reqUired nearly instantly. In addition to all of the move update information and other ghost updates that would be happening, it can be seen that this game would qUickly lag out (halt due to lack of bandwidth). Now, let us reexamine this example, introducing datablocks. The datablock will predefine all of the vehicle data. This datablock is transmitted once and only once (at the beginning of the game), still accruing the 2 MB penalty. However, to dynamically create a new vehicle, we only need to send a small packet of data, including the ID of the datablock, an initial creation position, and some other miscellaneous data. An estimated size for this packet is roughly 64 bytes. Now, our total simultaneous bandwidth requirement is: 32 x 32 x 64 bytes = 64 KB. This is a much more reasonable number and would be easily handled even on a system using a modem.
29

Part 1/

Engine Overview

2.2 Finding Your Assets


Game assets are things such as sound files, graphics files, game models, skins for the models, client scripts, server scripts, etc. Deciding how these assets will be organized is one of the most important decisions we will make while planning our game. I kid you not. How we organize our assets can have a significant effect on our productivity as well as our game's final disk footprint. Unfortunately, deciding on an organizational scheme requires some experience and a plan. So, if this is your first time making a game, it may be a bit hard to do. I suggest you follow the organization used by the FPS Starter Kit to start and then, when you have accrued some experience, draw up your own plan, based on your game's specific needs. Our sample game will use the Standard TGE Kit as a base.

2.2. 1 Finding Assets-TGE FPS Starter Kit


The Frs Starter Kit that comes with TGE has the major directories and directory contents shown in Table 2.3.
Table 2.3.
FPS Starter Kit direc tories.

Directory N.....

eon......
This is the root directory and represents the directory from which the executable was run. This is the highest directory visible to TGE and scripts. It isn't necessarily the same as the root directory on your disk. This directory contains files that are common between games. The intention here is that these scripts, images, models, etc., are reused at least in prototypes and often in final games. This directory contains the built-in tool scripts, GUIs, and other assets. This is the game directory (sometimes referred to as a mod directory) and includes all of the scripts, images, models, etc. used in your game. The results of our effort will be stored in subdirectories of this. This directory contains all of the interface art, GUI definitions, local preference files, and scripts that relate to the client's behavior. This directory contains models, skins, mission definitions, terrain files, and terrain textures. This directory contains gameplay scripts.

.,

lcommon
The organization of these directories is by no means fixed 0 r in any way magical. As I mentioned earlie r, when you becom e more experienced you may begin to modify this structure significantly, perh aps doing away with the "common" directo ry, or incorporating fea tures from other directo ries where it suits you r organizational scheme.

Icreator

Istarter.fps

Istarter.fps/client

Istarter.fps/data

"'I

Istarter.fps/selVer

.I}--
30

Torque from 10,000 Feet

Chapter 2

2.2.2 Finding Assets-Included lesson Kit


Because the assets that come with this guide are quite extensive and shared between many portions of the kit, the asset chart is too large to print in the guide. Please refer to the "Lesson Kit Assets" electronic appendix for a complete listing and discussion of what assets there are and where they live.

2.3 Sim Hierarchy Overview


As noted previously, TCE is in effect an event-driven simulator. This is made quite clear by the fact that the class structure starts with a class aptly named SimObject (simulation object). This class and its children form the "sim hierarchy." The sim hierarchy can be roughly divided as follows.
SimObject. This is the root class for all simulation objects, that is, a][ objects that are used to implement a game. SimSet and SimGroup. 1\No container classes, the latter acting as base class to the GuiControls and to ScriptGroup. ScriptObject and ScriptGroup. TWo classes used to create scripted classes. These special classes give us the ability to associate fields and methods with scripted classes, thus allowing us to neatly compartmentalize our scripts. SceneObject. This class is the root class for all objects to be included in the game scene and adds the concepts of position, rendering, and collision. GameBase. This class is the root to most mission-placeable objects and introduces ticking and datablocks. ShapeBase, ShapeBaseData, and Children. The ShapeBase classes and children are used to display models. These models are used to represent small world objects, players, vehicles, pick-ups, power-ups, etc. These classes all support complex visible geometry/features and an unlimited number of collision meshes. TSStatic. This is a lightweight shape-rendering class that does not incorporate any of the ShapeBase features. It merely renders a shape and encapsulates it in a simple object-oriented bounding box. This is the preferred class for noninteractive shapes that are used to add detail in scenes. Interiors. This class is used to display models that represent any structural object, including such things as buildings, bridges, walls, and other large structures. This class supports standard binary space partitioning of the models. Interiors support portals for more efficient subdivision of rendered spaces. Special Effects. A last set of classes are supplied that do not fit into either the shape or interior hierarchies. These classes are used to provide a wide set of possible special effects, including audio, visual, and physical (as affects avatars and other game objects) effects.

..

31

Part II

Engine Overview

2.4 TGE I/O Fundamentals


Out of the box, TGE supports inputs from mice and keyboards. With a little work, it will support inputs from gamepads, joysticks, and other input devices as well. TGE also supports basic file I/O out of the box.

2.4.1 TGE Device Input Architecture


When we speak of inputs in the context of TGE, we are talking about user inputs from keyboards, mice, joysticks, and other devices. Although it is possible for there to be other types of inputs, the only ones we are interested in are those that would be used to control gameplay. That said, inputs flow into and through TGE as follows (see Figure 2.10): The as (operating system) processes inputs and passes them to the TGE Platform Layer. The TGE Platform Layer identifies and categorizes the inputs, then passes them on to the Game. The Game processes the inputs if it can, or ignores them if there are no defined actions associated with them. Game input processing is the part we are interested in. As can be seen in Figure 2.10, the input is processed as follows:
1. The GlobalActionMap (see below) gets first dibs on the inputs. If it has no mapping for an input, that input is passed on to the curs, or more specifically the Canvas.

2. The Canvas attempts to process an input, but passes it on if there is no CUI control(s) programmed to use said input. 3. Lastly, the input is passed to any active (nonglobal) ActionMaps for processing. If none of the currently stacked ActionMaps is coded to use the input, the input is dropped.
Figure 2.10.

'iorque input/output architecture.


TGE Platform Layer

Mouse

32

Game

Torque from 10,000 Feet

Chapter 2

ActionMaps
ActionMaps are a special class designed to capture and redirect inputs. There are two kinds of ActionMap. There is the GlobalActionMap and the normal ActionMap. The main differences between these are:
GlobalActionMap. This is the daddy of input processors and supersedes all other processing methods. This action map should not be popped from the processing stack (see below). ActionMap. This is a generic action map. It takes lower priority than all other processing methods. These action maps can be pushed and popped from the processing stack as the game's requirements change.

ActionMaps in Our Game


Our game will require some kind of mapping between keyboard and mouse inputs to player movements and behaviors. We will stop briefly and show what these mappings are and discuss how they are attached (indirectly) to the player.

Processing Stack
What the heck is a processing stack, you ask? TGE implements an event queue, which is used to collect all user inputs and various other events. These events are then processed by the engine. The ActionMap is one consumer of these events. Because ActionMaps can be stacked and because they process events on the input queue, I refer to this as the processing stack. In short, an ActionMap not on the processing stack is not catching and therefore not processing input events.

2.4.2 TGE File I/O


TGE has a file manager that maintains a working list of all the files found in the game directory and all subdirectories. This list is created on start-up. Subsequently, the file manager will locate new files that you add and then attempt to load from the console or via scripts. It will also notice when files have been modified and recompile and load them when requested to do so. In short, with TGE you can easily add new files and modify existing content without having to restart the engine. This is a huge timesaver when creating new content and while debugging.

33

Part II

Engine Overview

File I/O and String Manipulation in Our Game


Earlier, when discussing shapes to be used in our game, I alluded to the idea that we would be able to modify the layout of our game. To do that, we will need to create a special level file and then create the scripts to load and parse it. The level file will also specify the starting position of our player, coins, teleport stations, maze blocks, and fireball shooters. By using a separate format, we enable the ability to modify the game and add new levels using a simple text editor. The scripts that do the loading and parsing will exercise several file I/O and string manipulation features. On a side note. we will also be using file I/O to load the contents of our credits screen.

2.5 Move Along ... Nothing To See Here ... Move Along ...
Well. that was fun. That was a very fast and very dirty coverage of many, but by no means all, of the features in the Torque Game Engine. Next, we will break out the FPS Starter Kit and start playing around. In the next chapter, I will introduce you to all of the content creation and placement tools that come (built-in) with the Torque Game Engine. You will get to see their power firsthand and to learn about how they work.

34

Chapter 3 Torque Tools


3.1 What We Are About to Learn
This chapter covers all of Torque's internal (built-in) content creation and placement tools. This includes tools both for building a 3D world and for creation of graphical user interfaces (GUIs). In addition to learning about these very important tools, we will occasionally pause to test out our newly acquired knowledge in exercises. The results of many of these exercises will turn up in the game we will assemble in the final chapter of this guide.

3.2 Torque's Basic Editors


Torque includes two basic editors, the World Editor and the GUI Editor. The World Editor is further broken down into eight tools. In the following pages, I will be using short names for the individual tools wherever it does not create ambiguity (see Table 3.1).
SIIIrt IdftDr
World Editor (WE)

Table 3.1.
This editor is composed of eight subeditors, each one allowing you to modify and save various aspects of a specific mission. This editor can be used to edit existing missions or to create new ones. This editor allows you to modify existing GUIs and to create new GUIs, using a simple dragand-drop interface. Torque's basic editors and tools.

Fll

GUI Editor (GE)

FlO

"

ToaII
World Editor Manipulator

F2

(Manipulator)
World Editor Inspector

This tool allows you to translate, rotate, and scale objects that have already been placed in the world. In addition to prOViding all the capabilities of the World Editor, this editor allows you to view and modify properties of individual mission objects. In addition to providing all the capabilities of the World Editor, this tool allows you to place new objects in the current mission.

F3

(Inspector)
World Editor Creator

F4

(Creator)

35

- - - - ----_.------

._---------

Part II

Engine Overview

.~

Table 3.1 (continued).

__ii;.i;., ;r:';

JJ~'<:

I~'''..:

T"
Mission Area Editor (Area Editor) Terrain Editor FS

>':

\;!.(,
This tool allows you to adjust the boundaries of the current mission and provides a means to mirror the current terrain. This tool provides the ability to directly manipulate the terrain using the mouse as a multi-operation brush. In addition to providing all the capabilities of the Terrain Editor, this editor allows you to load images as terrain files and to apply various algorithmic generators and filters to the terrain. In addition to prOViding all the capabilities of the Terrain Editor, this tool allows you to select any number of textures and apply them using a set of algorithms to determine blending and placement. In addition to providing all the capabilities of the Terrain Editor, this tool allows you to select and subsequently to apply up to six different textures to the terrain.

F6

Terrain Terraform Editor (Terraformer) Terrain Texture Editor

F7

F8

Terrain Texture Painter (Terrain Painter)

Window Menu -7 Terrain Texture Painter

3.3 The World Editor Tools


Let us tackle the World Editor toolset first, as it has the most components and is the most likely place to start when creating a simple mod (modification) or a new game. As we investigate and learn how to use each of the World Editor tools, please use the GPGT Lesson Kit (provided on the accompanying CD) and run the "World Editor Training" mission.

Please note that, while you are editing in the World Editor, you can get help simply by pressing F J. This will bring up a help dialog with descriptions of the tools and their features.

3.3. 1 World Editor Basics


Before leaping into the World Editor tools, let us review some things that hold true for all of the tools. First, we will review the user interface devices. Subsequently, we will discuss the mechanics of movement and viewpoint control, as well as object selection, translation, rotation, and scaling.

3.3.2 World Editor Devices


In this guide, the cursors, menus, and other graphical elements that you encounter in the editors are referred to as devices. Simply stated, these devices provide meaningful feedback to you regarding what action can or should be taken. The terms below are mostly of my own invention, with the exclusion of the appropriately named gizmo.

36

Torque Tools

Chapter 3

3.3.3 Cursors
Table 3.2 explains what each cursor image means.
DevIce

Desatptlon
When the cursor looks like this, it means that the cursor is not over a selectable object. In other words, you are pointing to an empty space. When the cursor looks like this, it means that the cursor is over a selectable object. In other words, you are pointing to an object that can be selected. When the cursor looks like this, it means you have successfully selected an object's gizmo axis in translation mode. In other words, you can move the object around by clicking and dragging when this cursor device appears. When the cursor looks like this, it means you have successfully selected an object's gizmo axis in either rotation or scaling mode. It also appears when you have successfully selected a bounding box face for scaling or rotation.

Table 3.2.

Descriptions of cursors.

No-select Cursor

Select Cursor

Grab Cursor

Rotate/scale Cursor

3.3.4 The Gizmo and Gizmo Scales


The graphic in Figure 3.1 represents the gizmo. The gizmo is a device that is activated when you select one or more objects. It displays the three traditional x-y-z axes. Individual axes are selectable and afford the ability to translate, rotate, and scale. By default, a gizmo axis is dark cyan when not selected and light cyan when the cursor is over it or when it has been "grabbed." Additionally, when a selected gizmo is used for an operation, one of three scales will be shown: the gizmo translation, rotation, or scaling scale.
This scale shows the current position of the object's centroid when you use the gizmo to translate an object. This scale shows the current degrees of rotation around the selected axis when you use the gizmo to rotate an object. This scale shows the current height, width, and depth of an object when you use the gizmo to scale it. <w,h,d> correspond to the x,y,z axes of the gizmo. x: -51.024, y: -127.829, z: 226.473

Figure 3.1.
The axis gizmo.

Gizmo Translation scale

x: 0.000, y: 0.000, z: 1.000, a: 52.519

Gizmo Rotation Scale

w: 1.2000, h: 1.2000, d: 2.144

Gizmo Scaling scale

37

--- - ---

_.

--

--

--

----

Part II

Engine Overview

3.3.5 Menus and Windows


The World Editor provides a set of traditional menus for selecting the current tool as well as other features (see Figure 3.2). Please note that all of the menu options will be covered in Section 3.5.3, "World Editor Menus."
Figure 3.2.
World Editor menus.

Figure 3.3.
Tool windows.

Several of the tools have windows that appear on the right side of the screen (see Figure 3.3). Although these windows have many similarities, it will be better to explain them individually in the respective tool sections below.

3.3.6 Selection Boxes


When selecting a previously unselected object, the selection cursor lets you know when you can select something, and the green selection box (see Figure 3.4) shows which previously unselected object will be selected. Once you have successfully selected an object, the object will be shown with both a red selection box and a yellow selection box (see Figure 3.5). The red box is object aligned, while the yellow box is world aligned. The purpose of the yellow box is to show which objects are selected as a group and will therefore be affected by any actions you take. The red boxes are to show which individual objects in the group selection box are actually part of the selection. Notice that, in Figure 3.5, the leftmost and rightmost characters are selected, while the middle character is not. Once you have successfully selected an object, the selection box will turn blue if your cursor passes over it (see Figure 3.6). Please note that this is not true for drag-select.
38

Torque Tools

Chapter 3

Figure 3.4.
Green selection box.

Figure 3.5.
Red and yellow selection boxes.

Figure 3.6.
Blue selection box.

3.3.7 The Handle and level Grid


Every object in the world displays a handle (see Figure 3.7). The handle has two labels next to it. 1. A number. The number signifies which object this is in the mission object list and is the (server-side) ID for the object. 2. A name. If the name is "(null)", no name has been assigned to this object. Names are optional but very useful for scripting purposes. When an object is selected, a faint grid will appear (see Figure 3.8). The grid is parallel to the world's x-y plane and passes through the selected object at the handle. When multiple objects are selected, the plane passes through the group handle, which is located at the axis crossing point for the group gizmo. This device can be used like a ruler for placing objects accurately. Unfortunately, there is no vertical equivalent.
Figure 3.7.
Object handles.

Selected handle

18:26. (null)
Unselected handle

Figure 3.8.
Level grid.

39

Part II

Engine Overview

3.3.8 Scale Devices


Figure 3.9.

The scale device.

You will see a scale device while editing the terrain and while adjusting terrain parameters (see Figure 3.9). The premise of this device is simple. The 2D scale (line with red dots) represents parameter in two dimensions. Depending on the application, the horizontal spacing may represent elevation, radius, etc. The vertical spacing may represent opacity, blending factor, strength of action, etc. The red dots on the lines are control points. These points can only be moved vertically. All scale interfaces come with a spin box to add or remove control points, thereby increasing horizontal resolution. Please note that you are better off typing in the value you want, because the spinner changes do not take effect unless you edit the textbox.

3.4 World Editor Mechanics


Now that we have familiarized ourselves with the various devices available in the World Editor, let's discuss the mechanics of how we manipulate objects in the mission using the mouse. We will talk about how to move around the mission, switch camera modes and viewpoints, select objects, and use the mouse to manipulate position, rotation, and scale via the gizmo.

3.4. 1 Default Movement and Viewpoint


Table 3.3 gives the keystrokes for moving around the mission and changing camera modes and viewpoint.
Table 3.3.

DescrIption
Moving around

Key(s)
W, A, S, D, SpaceBar (Up, Left, Right, Down, Jump)

Moving and changing viewpoint.

Looking around Zoom Toggle free-camera vs. player view Toggle 1st vs. 3rd POV (in play mode only) Free-camera speed (World Editor Only) Drop character at camera

0+ Motion
E (Zooms when held)

ALT

+C

TAB SHIFT + 1 ... SHIFT + 7 (slowest ... fastest) F7 (play mode after editing only) ALT + W (World Editor mode only) CTRL + F7 (both modes) ALT

Drop camera at character

+ Q (World

Editor mode only)

40

Torque Tools

Chapter 3

3.4.2 Object Selection and Translation


Table 3.4 shows how to use the mouse to select and translate objects.

~::::. OMaJplldn
selection

Adion

Function
Selects: Previously unselected object Selects: Previously unselected object Deselects: Previously selected object Selects: Previously unselected object Previously selected object Please note that the drag box must enclose an object's centroid (red dot) to select the object. Translates: Single previously unselected object Single previously selected object Multiple previously selected objects

Table 3.4.
Selecting and translating Objects.

~ on object
(see Figure 3.lOa) Shift + 151' on object (see Figure 3.lOa)

Figure 3.10.
Object selection actions.

a.

~ on empty space + Drag


(see Figure 3.lOb)

Object Translation without using gizmo

Elr + Drag

b.

3.4.3 Using the Gizmo


As described earlier, the gizmo is the aptly named three-axis device that appears when you select either a single object or a group of objects. The gizmo has three individually selectable "handles" that run along the major axes x, y, and z. These handles gives you the ability to translate, rotate, and scale objects (see Table 3.5).

ft r..:,

FunctIon
To translate (object-axis)

Mouse

ActIon
Drag left/right for for z.

Table 3.5.
Using the gizmo.

151' gizmo axis

x and Y, up/down

To translate (world-axis)

SHIFT

Elf gizmo axis

Drag left/right for x and Y, up/down for z. In this mode, the gizmo aligns to the world axis and confines translation to translation along the selected world axis. Drag left/right. Drag left to grow and right to shrink.

To rotate To scale (single object only)

ALT +

~ gizmo axis

CTRL + ALT + axis

Ell gizmo

41

Part II

Engine Overview

Figure 3.11.
Using the gizmo on single and multiple objects.

Single object gizmo

Multiple object gizmo

Gizmo translations and rotations can be applied to single or multiple selected objects (see Figure 3.11). Rotations are always about the gizmo axis, which is the handle for single selected objects and the group handle for multiple selected objects. Gizmo scaling can only be applied to a single selected object.

3.4.4 Scaling using Bounding-Box Planes


While experimenting, I accidentally discovered that there is another way to scale objects with the mouse. Not only is this method slightly more intuitive, but it also doesn't require the use of the gizmo. "fry the following: 1. 2. 3. 4. Deselect all objects. Find the object you wish to scale and select it. Press and hold CTRL + ALT. Click a bounding-box plane and drag the mouse to scale. You'll notice that the selected side of the bounding box is filled with a medium blue hash.

That is all there is to it! Figure 3.12 shows a selected bounding-box face.

Figure 3.12.
Using a bounding-box plane to scale.

..

42

Torque Tools

Chapter 3

3.5 World Editor (Manipulator) 3.5.1 Starting the Manipulator


1. Start the World Editor by pressing FIl. 2. Start the Manipulator by pressing F2.

3.5.2 The 3D World View Window


The real benefit of the Manipulator tool comes from the fact that you can traverse the world and the 3D world view is not blocked by any dialogs or menus (except for the World Editor menu), giving you an almost-full screen view while you manipulate objects via mouse and hot keys. Upon examination, it can be seen that this tool is very plain (likely as intended). In the sample view in Figure 3.13, we can see the world and its contents. We can apply all standard mouse manipulations as described in Section 3.3, "World Editor Tools".

3.5.3 World Editor Menus


All World Editor tools have a top menu containing the same elements. However, in some tools, certain menu selections will be disabled. Tables 3.6-3.10 give a brief description of each menu and the menus' choices. Some options' descriptions will be deferred until we discuss the specific tool that is affected by said option.
Figure 3.13. World Editor screen (Manipulator mode).

43

Part II

Engine Overview

Table 3.6.

Menu Item
New Mission ...

DescrIption
Clicking this option will generate a new mission based on preset values. This generates the same mission every time.

File menu.

WARNING: This wipes out the current mission. If done at all, it should be done once and only once, before editing. Open Mission ... (CTRL + 0)
Brings up a dialog to allow you to load an existing mission. This saves your current mission.

Save Mission ... (CTRL + S) save Mission As ...

As with "Save Mission ...", this allows you to save your


mission, but in this case, you can specify a name and (existing) directory for the mission file.

Import Terraform Data ... Import Texture Data . .. Export Terraform Bitmap ...

This feature is deprecated and no longer used. This feature is deprecated and no longer used. This choice is enabled by the Terraformer tool. We will discuss it there.

Table 3.7. Edit menu.

Menulteln
Undo (CTRL + Z)

DescrIption
Undo the last operation. WARNING: This does not undo all operations, so back up early and often. Redoes last operation. As with undo, this does not apply to all operations. Standard cut-copy-paste. can be applied to single and multiple objects.

Redo (CTRL + R)

Cut (CTRL + X) Copy (CTRL + C) Paste (CTRL + V) Select All (CTRL + A) Select None (CTRL + N)

Selects all objects (shapes and interiors) in the mission. Deselects preViously selected terrain. This does not deselect objects. causes the engine to relight the current terrain and apply shadow maps. This trips up a lot of beginners. I will discuss this further when we learn about adding interiors. This brings up the World Editor Settings dialog. (Discussed below.) This feature relates to the Terrain editor and will be discussed there.

Relight Scene (ALT + L)

World Editor Settings. "

Terrain Editor Settings ...

44

Torque Tools

Chapter 3

MenuDem
Render Plane Render Plane Hashes Render Object Text Render Object Handle Render Selection Box Plane Extent Grid Size Show Mouse Popup Info Move Scale Rotate Scale Scale Scale Planar Movement

Description
Show plane when objects are selected. Show hashes when objects are selected. Show objects' names and IDs. Show objects' handles (red dot). Show selection boxes. Length by width dimensions of plane (floating point OK). Hash spacing for grid (floating point OK). Show mouse popup scales when moving-rotating-scaling. These values increase or decrease mouse sensitivity for individual mouse actions (move, rotate, scale).

Table 3.8.
World Editor settings. To modify the World Editor settings, click Edit ~ World Editor Settings .. " then change the appropriate setting.

Checked: Object will move along plane when dragged. Unchecked: Object will attempt to follow terrain when dragged.
I I

I
Collide with Object's Bounding Box
Objects Use Box Center

If checked, object can be selected by placing cursor

I anywhere on object's bounding box.

If checked, handle is in object center; otherwise at lower limit of object bounding box.

Axis Gizmo Active Min scale Factor Max scale Factor Visible Distance
I

I Enable gizmo. I
Determine minimum and maximum multiple by which objects can be scaled from original size. Minimum distance within which object handles are visible/ selectable. (This has nothing to do with visible distance during gameplay. Examine the Sky object for that.)
--

Gizmo SCreen len Project Distance


:~

Gizmo axis length in screen pixels. Ray length for selection cursor.

~Y:'

......ultllm

Description
Disable mouse actions (drag, rotate, scale) on current selection(s). This does n.~event changes via the Inspector window although a will show up in the World Editor tree.

Table 3.9.
World menu.

lock Selection

Unlock Selection Hide Selection

Re-enable mouse actions on current selection(s). Hide (Le., do not render) current selection(s).

45

--

-- . _ - - - ------ - - -

Part II

Engine Overview

Table 3.9jcontinuedJ.

Menu Item
Show Selection

DeIcrIptIon
Un-hide previously hidden object(s). Use the Inspector to select these objects. They have a to them in the World Editor tree. next

Delete Selection Camera to Selection Reset Transforms

Delete current selection(s). Move camera to centroid of current selection(s). Un-rotate selected objects that are rotated (Le., align to objects default alignment). Un-scale selected objects that are scaled (Le., scale all objects' dimensions to 100% of default scale). Works for multi-select. This is not the same as Undo. Make currently selected object(s) drop according to drop current rule (see Table 3.10). We will discuss this feature when we discuss the Inspector.

Drop Selection

I
Add Selection to Instant Group Drop at '"

We will discuss these in Table 3.10.

Table 3.10.
'"Drop At .. ." menu item. In the World menu dropdown. there is a group of "Drop at xyz .. ." radioselections (only one can be selected). Before you start placing objects in the Creator. you should understand what these settings are going to do for you.

Menu Item
Drop at Origin Drop at Camera

DeKrtptlon
This causes new or pasted objects to be created at the World Origin. This causes new or pasted objects to be created at the current location of the current camera. You could think of there being three cameras: one in the character's head during 1st POV (Point of View) viewing, a second in the following camera position during 3rd POV, and the third being the actual free-floating camera. Figure 3.14 shows an object dropped in 1st and 3rd POV to clarify this.

Drop at Camera wI Rot

This does the same as "Drop at camera" with the addition that the object will have the camera's rotation. In this mode, new objects are created somewhere below the current camera. This is the default "drop at" mode. I think this mode's title is a bit of a misnomer. It seems that this behaves more in the follOWing fashion: Cast ray from camera eye: On collision with object bounding box, water, or terrain, drop the object at point of collision. If ray extends beyond "Project Distance" (set in World Editor dialog), drop object at camera eye (position).

Drop below Camera Drop at Screen Center

Drop at Centroid

This option allows you to select multiple objects and have the newly created object placed in the virtual centroid of the group.

46

Torque Tools

Chapter 3

....... Drop to Ground

DescrIption
Objects are dropped to the ground at mission center. I wouldn't use this if there is any possibility that there could be an overlapping interior at the mission center, because dropping another interior there will crash the editor.

Table 3.10 (continued). Figure 3.14. Drop at camera fouch!).

The Window menu is probably the most easily understood. It allows you to select which of the World Editor tools you wish to use. The only important thing to remember is that you must use this menu to select the Terrain Texture Painter tool since there is no hot key for it.

3.6 World Editor Inspector (Inspector) 3.6. 1 Starting the Inspector


1. Start the World Editor by pressing FIl. 2. Start the Inspector by pressing F3.

3.6.2 Examining the Inspector


The Inspector tool (Figure 3.15) allows you to select an object and manipulate its script-exposed parameters via text boxes, spinners, radio buttons, checkboxes, etc. These parameters will vary based on the object. Later, we will examine specific parameters for water, terrain, the character, the sky, etc. Now, for the purpose of learning about this tool, we will work with a simpler object, namely the SpawnSphere. The purpose of this object is unimportant at this time. The key thing is that it is easily located and manipulated. To begin, look directly overhead. You should see a gray object. Select it and you should have a view similar to Figure 3.16. Taking a quick inventory of the screen elements, we see the World Editor menu at the top, the 3D World View window which takes up nearly two-thirds of the screen, the World Editor tree window in the upper right, and finally the World Editor Inspector window in the lower right.

..

3.6.3 World Editor Tree


Before we jump into the relatively straightforward World Editor Inspector, let's discuss the World Editor tree and some important organization features it provides. First, expand the list in the World Editor tree window. The initial list is completely collapsed, which doesn't do us a lot of good when we're trying to manipulate objects.
47

Part II

Engine Overview

Figure 3.15. World Editor screen (Inspector mode).

Figure 3. 16. The Inspector screen elements.

48

,~--

Torque Tools

Chapter 3

1. Expand the MissionGroup SimGroup by clicking the [+] next to the text "tltltltI: MissionGroup - SimGroup." See Figure 3.17; numbers may vary from illustration. 2. Expand the PlayerDropPoints - SimGroup. You should now have something similar to Figure 3.17. If for some reason the SpawnSphere entry is not highlighted, please click on it once to select it.

Figure 3.17. World Editor tree window.

Locked Items
You will notice that some entries in the tree have a lock icon next to them. This means that the entry is "locked" and cannot be edited. You may lock an item by creating a dynamic field (see "Inspector-Dynamic Fields" in Section 3.6.6) named "locked" and then setting that field to true. You may unlock an entry by deleting this field, or by setting it to false.

3.6.4 SimGroups
At this point, you may be asking, "What is a SimGroup?" Subsequent chapters in Part III will get into the nitty gritty details about SimGroups, SimSets, and SimObjects. For now, we'll simply describe SimGroups as a means by which we organize objects. This is both useful from an organization sense, i.e., knowing where to find things while you are editing, and for scripting purposes. By predefining a consistent set of SimGroups and by organizing your objects within them, your current job as a mission/level designer will be greatly simplified. Your script writers will thank you also. If that is your job, too, then pat yourself on the back. As can be seen from the current view of the World Editor tree, SimGroups, as well as particular entities (SimObjects), can be nested within SimGroups. In fact, every mission entity is present in this list and will be found nested within a SimGroup. So, how exactly do we place objects within a SimGroup? Let's find out. First, make a duplicate copy of the SpawnSphere. We already have it selected, so all you need to do is type CTRL + C (to copy) followed by CTRL + V (to paste). Alternately, you can use the Edit Menu -7 Copy/Paste operations. Now that you've created a new SpawnSphere, you need to locate it in the World Editor tree. If you've followed the instructions above, you will find the new SpawnSphere at the bottom of the tree (see Figure 3.18). We would much rather have it in the PlayerDropPoints - SimGroup with the rest of the SpawnSpheres. So, let's manually move this one to the correct spot and then learn how to place objects in the right SimGroup the first time.

Figure 3.18.

49

Part 1/

Engine Overview

Figure 3.19.

Moving Existing Objects into a SimGroup (Add-Group or Instant Group)


The new SpawnSphere should already be selected, but if it isn't, please click on it to select it. Now, use the slider on the right side of the World Editor tree and find the PlayerDropsPoints - SimGroup. Select this as the Add-Group through the key/mouse combination: ALT + ~I. The Add-Group should now be selected with a gray background (see Figure 3.19). Now, select the menu item World -7 Add Selection to Instant Group. Voiw! The SpawnSphere is in the PlayerDropPoints - SimGroup (see Figure 3.20). These steps will work in all versions of Torque, but in version 1.4 and later, you may simply drag and drop objects from SimGroup to SimGroup.

Figure 3.20.

Creating Objects in a Preexisting SimGroup (Add-Group)


OK, so that was a hassle. How do we get objects to place in the correct SimGroup when we create them? Simple. You already have a SpawnSphere in your copy buffer, and you already have the Add-Group selected (see above). Paste another SpawnSphere and it should show up in the PlayerDropPoints - SimGroup (Figure 3.21). Easy as pie! The trick is to select your instant group before pasting objects and they will automatically be placed in that SimGroup.

Figure 3.21.

3.6.5 World Editor Key Stroke/Mousing list


Table 3.11 is a summary of operations you may perform on SimGroups and Objects with the mouse and key combinations.
Table 3.11.
Mouse ALT+ CTRL + Action
On SimGroup On SimGroup On Object On Object

Function
Set current Add-Group. (De)select all members in SimGroup. (De)select object(s). Select multiple objects.

I5r CTRL + I5r SHIFT + I5r

3.6.6 World Editor Inspector Window


Now let's address the World Editor Inspector. This is, of course, the window from which this tool gets its name. The purpose of this window is to allow you to inspect and modify parameters for individual objects. If you play around a bit and click on different objects, you will begin to see that different object types have different parameters. For now, we'll address the more common

50

Torque Tools

Olapter 3

values, add new values, and finish off with some tips on using the interface effectively. We will leave a detailed inspection of individual objects' parameters for Chapter 8, "Mission Objects."

Inspector-Common Fields
Table 3.12 describes the common fields used in the World Editor Inspector.
field
Position (X,Y,Z)

Desatptlon
Three floating-point values representing the coordinates of the selected object in world space. Four floating-point values where the first three are multipliers and the fourth value is the angle (in degrees) of the rotation(s). Example: rotation 0 1 0 90.0 means the object is rotated 90 degrees about the y-axis, relative to the worldaxis.

Table 3.12
Common fields in Inspector.

Rotation (X m, Ym, Zm,A)

Scale (X m, Ym, Zm)

Three floating point values representing a relative scaling. The values act as multipliers of the object's default dimension(s) in the indicated axes. Example: scale 1 1 2 means that this object will be twice as tall as the default when loaded into the world. Please note that these values correspond indirectly to those you see when mouse scaling. Mouse-scaling values are actual world dimension.

shapeName (shapes only) interiorFile (interiors only) Object Name

This parameter's name is a misnomer. It actually gives the relative path and filename of the selected shape. This parameter gives the relative path and filename of the selected interior. There isn't actually a parameter tag for "Object Name," but there is an editable text field for it. The text field is located to the right of the Apply button. You can type jUst about anything in this field, though no spaces are allowed. Click Apply to name your object. Please note that objects can be given the same name. We'll leave further discussion of object naming for a later chapter. Just remember that this is how you change it from the Inspector.

Inspector-Dynamic Fields
I won't explain what dynamic fields are yet, but rather I will explain a way that they can be added to objects. To add a dynamic field: select the object to which you wish to add a field, click the Add button found in the Dynamic Fields section of the World Editor inspector window (see Figure 3.22), and
Figure 3.22.

51

Part II

Engine Overview

Figure 3.23.

give the field a meaningful and unique name and an initial value. To modify the value of a dynamic field, follow the same steps as changing the value of any other field. Just modify the contents of the text field next to the dynamic field name and click Apply. To delete the dynamic field click on the garbage-can icon next to the dynamic field (see Figure 3.23), and the field will be removed permanently. This cannot be undone.

3.7 World Editor Creator (CreatorJ 3.7. 1 Starting World Editor Creator
1. Start the World Editor by pressing Fll. 2. Start the Creator by pressing F4.

3.7.2 World Editor Creator Window


The Creator (see Figure 3.24) tool is used to create (or place) new content. From the World Editor Creator, we can select objects to insert into our current mission. Figure 3.25 shows the Creator's top-level folders.

Figure 3.24.
World Editor screen (Creator mode).

52

'----

Torque Tools

Chapter 3

Interiors. Buildings and other interiors. Shapes. Animatable shapes. Static Shapes. Lightweight inanimate shapes. Mission Objects Environmental stuff like the sky, sun, water. Mission stuff like MissionArea, Triggers, and Cameras. System stuff like SimGroups.

Figure 3.25.

World Editor Creator top-level folders.

Placing (Creating) New Objects


Creating new objects is much like pasting objects. Simply 1. move to the location in the mission area where you would like to place the object; 2. look approximately where you want to place the object; 3. find the object you wish to place by looking in the World Editor Creator tree; and 4. click once on the object in the list. Once an object is placed in the world, you can freely manipulate its position, rotation, and scale via the mouse. If, however, you want to change object parameters, you'll need to switch back to the Inspector.

Adding Objects to Creator Tree


When the engine is first started, it creates a list of all files found in the mod directory. Later, when we start the Creator, the Interiors tree is populated with all known DIF files, and the Static Shapes is populated with all DTS files found. In both cases, the original directory hierarchy for the mod is maintained (see "abcshack" sample below). The Shapes tree is populated with ShapeBase objects created in scripts. We'll defer discussing these shapes until Part III, "Game Elements." For now, let's learn how to get basic interiors and shapes into their respective trees.

Adding Interiors to the Creator Tree


Torque needs the following files to create an Interior: DIF. Once an Interior has been properly generated, there will be a file named interiocname.dif, where interior_name is whatever you chose to name your interior object. Graphics fiIe(s). An Interior will have at least one graphics file. By default, the graphics files used lor the Interior need to be located in a directory above the Interior's DIF file or in the same directory as the DIF file.

Spaces in a folder name will make the parts after spaces show up like a subdirectory.
Bobs Room\room.dif

produces:
[-]-0 Bobs

[-]-Q Room

L room

53

---_._---

---

---

--

- - _.-

--~---

Part II

Engine Overview

Figure 3.26.

Example:
1. In the directory "example\gpgt\data\GPGTBase\interiors\abcshack" you will find a file named abcshack.dif. Make a copy of this file and rename it myabcshack.dif.

2. Completely exit the GPGT Lesson Kit, reload it, and start the World Editor Creator again. 3. Now, in the Creator tree, under Interiors -7 gpgt -7 data -7 GPGTBase -7 Interiors -7 abcshack, you will see a new Interior named myabcshack (see Figure 3.26).

Adding Static Shapes to the Creator Tree


Torque needs the following files to create a Static Shape: DTS. Once a shape has been properly generated, there will be a file named shape_name.dts, where shape_name is whatever you chose to name your shape object. Graphics file(s). A shape will have at least one graphics file. By default, the graphics files used for the shape need to be located in the same directory as the shape's DTS file. DSQ(s) (optional). For an animated shape created in 3ds Max, there is a third type of file, containing animation data. For simplicity's sake, this will not be discussed here, other than to note that they may exist. By default, the DSQ file(s) used for the shape need to be located in the same directory as the shape's DTS file.
Figure 3.27.

Example:
1. In the directory "example\gpgt\data\GPGTBase\shapes\markers" you will find a file named dummy.dts. Make a copy of this file and rename it mydummy.dts.

2. Completely exit the GPGT Lesson Kit, reload it, and start the World Editor Creator again. 3. Now, in the Creator Tree, under "Static Shapes -7 gpgt -7 data -7 GPGTBase -7 shapes -7 markers" you will see a new Shape named mydummy (see Figure 3.27). Try placing it. You might be wondering why the object showed up in Static Shapes instead of Shapes. Objects under Static Shapes are lightweight objects (created with TSStatic). Objects under the Shapes tree are created using the ShapeBase hierarchy. ShapeBase adds several capabilities, including animations, sounds, rendering effects, etc. This requries the creation of a datablock. We will discuss creating ShapeBase objects and their datablocks in Chapter 6, "Basic Game Classes."
54

Torque Tools

Chapter 3

3.8 Mission Area Editor (Area Editor) 3.8.1 Starting the Mission Area Editor
1. Start the World Editor by pressing Fll. 2. Start the Mission Area Editor by pressing FS.

3.8.2 The Mission Area Editor Window


In the upper right corner of the screen, you will see a blue and white image (see Figure 3.28). This image represents the mission map. The Mission Area Editor provides the ability to select the size and location of the mission bounds (or area). Interestingly, it also provides a terrain editing feature.

Editing the Mission Area


The Mission Area Editor is very simple to use. Simply click the Edit Area checkbox, and handles will appear on the mission area box. Now drag and resize to your heart's content. You will be able to see the effect of your changes in the 3D World View window also. One thing to keep in mind is that the image is inverted; that is, the top of the image is what most would consider south, the bottom north, and the left and right, respectively, west and east. This could quickly become cumbersome to remember, so the creators of the Area Editor
Figure 3.28.
Mission Area Editor screen.

55

----_/

Part II

Engine Overview

Figure 3.29.
Mission Area Editor details.

EnebIe Area edlIng.

certer the ~selon Area In tsmlIn.

Restz&
hen<Ie&.

Clckln

box to eng.

~selon Area loceIIon Field Of View (FOY) V. end size metrlC5.

provided a device to give you a better hint as to where you are looking when you edit. The device I'm speaking of is the Field Of View (FOV) 'V'. Look at the labeled example in Figure 3.29. Before moving on, there are a couple of things that you should know. You can use the Area Editor window to rapidly relocate your character/ camera. Simply be sure that the Edit Area button is not checked and click in the window. Your character/camera (depending on view mode) will "jump" to that point. If you have made modifications to your terrain using the Terraformer or the Terrain Editor, those changes will not automatically be reflected in the Area Editor image. To refresh the image, do the following. 1. Make your terrain changes. 2. Start the Area Editor and make sure Edit Area is checked. 3. Drag the mission area off center. 4. Recenter by clicking the Center button. The updated terrain should now be reflected in the Area Editor image. The moral of this story is to edit your terrain topography first, then edit your mission area. And do all this before placing interiors, shapes, or other mission objects.
56

---_.

- -------

Torque Tools

Chapter 3

Figure 3.30.
Rotate mirroring plane Cancel mirroring operation

Mirror in Mission Area Editor.

Mirroring destination

Mirroring plane

Mirroring source -

Mirroring the Mission Area


As I mentioned above, the Mission Area Editor also provides what I would label a "terrain editing feature "-namely, the ability to mirror the terrain. This is very useful if you wish to create a balanced (in terms of terrain obstacles) mission area. To use this feature, click on the "mirror" and you will see something similar to Figure 3.30. The application of this tool is simple: select the orientation of the mirroring plane (with <-- --> buttons) and click Apply to mirror copy the source onto the destination.

3.9 Terrain Editor 3.9.1 Starting The Terrain Editor


1. Start the World Editor by pressing Fll. 2. Start the Terrain Editor by pressing F6.

3.9.2 The Terrain Editor Window


When you start the Terrain Editor, you will see a shot like the one in Figure 3.31. This looks very much like the view in the Manipulator, except that there are no windows obscuring your view. However, if you look closely, you'll

57

Part"

Engine Overview

Figure 3.31.
Terrain Editor screen.

notice some odd squares following your cursor around while you move your mouse. These squares are yet another Torque user-interface device, the purpose of which is to give you feedback on what terrain area will be affected when you choose to manipulate it and, to some degree, how it will be affected. Before we jump right into learning how to edit the terrain, let's look at the other two devices on the screen. "fhe Over Vertex Brush Scale I refer to the text beside the label" (Mouse Brush)" in Figure 3.32 as the Over Vertex Brush Scale. The purpose of this scale is twofold.

..

I. It shows how many vertices are currently under the brush. In Figure 3.32,

we have 69 vertices under the brush. 2. It shows the average elevation of the vertices under the brush. The Selected Brush Scale I refer to the text beside the label" (Selection)" in Figure 3.33 as the Selected Brush Scale. The purpose of this scale is twofold.
I. It shows how many vertices are currently selected. (We'll learn about

58

selecting below.) 2. It shows the average elevation of these selected vertices.

Torque Tools

Chapter 3

Figure 3.32.
The Over Vertex Brush Scale.

Figure 3.33.
The Selected Brush Scale.
(r.1'OII
~

1.111 III - I,'

'~'l

,~

I bf

I'., I, <:11 Jill

lJ

'10'1 U

3.9.3 Editing
There are two basic modes for editing via the Terrain Editor:
1. Brush mode. The default mode, which I call brush mode, is a free-floating 9 x 9 vertex brush. You can adjust the shape and hardness of the brush as well as change its size by rough increments. In addition, this mode provides several operations.

2. Selection mode. The second mode, which I use less frequently, but which can do things that you cannot do in brush mode, is what I call the selection mode. In this mode, you select arbitrary blocks of terrain. Then, you can perform a single operation upon them-modify their height via mouse movement.
Editing in Brush Mode

I think it is fair to say that most of your editing is going to be in brush mode, and because it is the default mode, I'll discuss it first. As mentioned previously, you can modify the brush shape, hardness, and size. Figure 3.34 describes the details that are modifiable in the Brush menu.
Shape: There are two basic shapes: a box or a circle (roughly). You may select one or the other.

Figure 3.34.
Details of the brush menu.

Hardness: A soft brush has a "tunable" strength of action across the brush, whereas a hard brush acts at 100% strength across the entire brush.

Size: As can be seen, you may select one of six brush sizes. Take note of the keyboard shortcuts.

59

Part II

Engine Overview

Basic Brush Editing Actions

OK, now that we know about basic brush manipulation, what about the operations? In Table 3.13, let's take a look at the action menu (shown in Figure 3.35).
Table 3.13.
Action menu descriptions. OperatIon
Add Dirt Excavate

Meaning
Raises terrain under brush. Lowers terrain under brush.

* * *

Figure 3.35.
Action menu.

Adjust Height

Temporarily selects vertices under brush. Mouse Up-raises vertices Mouse Down-lowers vertices.

Action II Brush W1ndCl


tI

Select Adjust Selection Add Dirt


EXCON

Flatten Smooth

Sets all vertices under brush to average height of vertices under brush.

Does a nearest-neighbor elevation average on vertices under brush. Sets all vertices to preselected height. (See "Terrain Editor Settings" section for setting this value.) Removes the terrain between the outer edges of the brush. Puts terrain back in spots where it was previously removed. Paints vertex with currently selected texture. (See Section 3.12, ''Terrain Texture Painter.")

Set Height

Adjust Height Flatten Smooth Set Height


Set Empty Clear Empty

set Empty

** **

Clear Empty

Paint Material

* This action is affected by brush hardness settings. ** Not a vertex operation per se. These operations modify the block of terrain between a set of vertices.
Selection in Brush Mode

Paint Material

All right, so what about this other mode, selection? There isn't really much to it. To get into selection mode, just open the Action menu (see Figure 3.35) and click Select. Now, you can select terrain as explained in Table 3.14.

Table 3.14.
Making selections.

Action

Result
Selects vertex.

Previously Unselected Vertex

elf

Previously Selected Vertex CTRL + ~J Previously Selected Vertex

ell

May increase strength of action (see discussion of brush hardness below) if the selection cursor has a stronger value than the currently selected vertex's action strength. De-selects vertex.

60

Torque Tools

Chapter 3

Having selected the terrain blocks that we wish to modify, we can open the action menu and click Adjust Selection. Now, we can ~ 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 deselect all selected vertices, press CTRL + N or click Select None in the Edit Menu. Relative Harel.... Color (Strength of ActIon) Brush Hardness
Hardest (100%) Red Brush hardness has been mentioned several times but not completely Orange Hard (> 50%) explained. When the brush hardYellow Soft 50%) ness is set to Soft, the action strength along the diameter of the brush can Green Softest (almost 0%) 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. If the strength of action is set high, the value change for that part of the brush will be high. This Hord-. attenuation is in relation to the movement of the mouse. The brush gives strength of action feedback through MecllIII-' coloration (see Table 3.15). 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. See Figure 3.36 for examples.

Table 3.15.
Brush hardness and coloration.

Figure 3.36.
Brush hardness results.

Terrain Editor Settings


Earlier, I deferred a discussion of these settings. Now is the time to understand them. The Terrain Editor Settings ... (see Figure 3.37), found under the Edit menu, gives us some additional control beyond brush shape, hardness, and size. Table 3.16 gives further explanation of the settings found in this dialog box.

Figure 3.37.
Terrain Editor Settings dialog box.

eft

61

Part II

Engine Overview

Table 3.16.

r:

DIaIogAru
Soft Selection spline <Radius> Adjust Height set Height scale Height Smooth Factor

Purpose
This spline scale modifies the brush hardness. Left is the center of the brush and right is the outer edge. See "Selection and <Radius>" section. Increment by which the height of fully selected (hard) terrain is adjusted per mouse tick. Height to set selected terrain to when Set Height operation is used. Increment by which height is scaled when using scaling operations. Strength of smoothing operation. Higher values smooth more aggressively but may produce less interesting terrains as a result.

Terrain Editor settings.

Selection and <Radius> Instead of attempting to explain <Radius> with words, I give a pictorial example in Figure 3.38. In the following sequence, I have changed to selection mode and am using a 1 x 1 brush. I then select four separate vertices. Next, after opening the Terrain Editor Settings dialog, I change the radius values to those shown and hit Apply. See how the selection changes?

Figure 3.38.

Example of using <Radius>.

< Radius>

== 1

<Radius>

== 8

<Radius>

== 12

<Radius>

== 14

Slight increase in strength of action (greens becoming more yellow).

<Radius>

== 15

<Radius>

== 16

62

Larger increase in strength of action (green selections are almost entirely yellow).

Torque Tools

Chapter 3

3.10 Terrain Terraform Editor ITerraformerJ


3. 1O. 1 Starting the Terraformer
1. Start the World Editor by pressing Fll.

2. Start the Terraformer by pressingF7.

3.10.2 The Terraformer IAn OverviewJ


Of all the in-game editor tools, the Terraformer is probably the most elaborate and complicated. The shortest explanation of the Terraformer is that it is a tool to algorithmically build terrains. You may ask why you would want to use this tool to build terrains. The number one reason I can think of is that it is a fast way to create interesting terrains. In this section, I provide the following details about the Terraformer: description of Terraformer windows, summary of all operations, rundown on how operations are applied, brief descriptions of the individual operation interfaces, and a list of important Terraformer factoids.

Figure 3.39.
Terraformer screen.

63

------

--------

--- -

----

--------

Part II

Engine Overview

3.10.3 The Terraformer Preview Window


If you are reading this guide from front to back, this will be the first time that you have seen the window in Figure 3.40. You'll note that it is similar to the Mission Area Editor window. In fact, this window displays very similar data. For the purpose of this discussion, we'll focus on the following aspects.

Figure 3.41.

a. Terraformer operations tree.


~I--

1. Center marker. There is a faint Figure 3.40. white + in the preview window. Terraformer window. This marks the center of the map. Every time you apply Terraformer operations, this is where the camera will be moved to. 2. FOV marker. There is a red V that is always in the center of the window. This shows your current field of view, i.e., area in your view rela- ~' ,'" J tive to the map. . ,~ \ 3. Boundary marks. In addition to ,~ \ f the center marker, there are faint horizontal and vertical lines, repre"'- ~.~,...., f ~ senting the boundaries of the cur{Mollse orim)l) P: 69 .IVI): 16~.89351;9 rent heightmap. 4. Heightmap image. Although it may not be obvious at first, the image in the preview window is a translatiDn of the heightmap. The funky coloring can be interpreted very easily. The darker an area is, the lower it is; likewise, the lighter an area is, the higher it is.

~./ . ~

"

~,
(

3.10.4 Terraformer Operations Tree


b. Terraformer operations pull-down menu.

In the lower right corner of the screen, you will find the Terraformer operations tree. There is a button labeled Operations and clicking on this will bring up a pull-down menu with all the operations (see Figure 3.41). 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).

Terraformer Operations
Each of the Terraformer operations has its own settings. These can be accessed in the upper right window. Before we cover these, let's quickly enumerate and describe the general properties of the operations. In his 7hbes editing guide, Editing Maps and Missions in 7hbes 2, Tim Hammock appropriately categorizes the operations as either "generators" or "filters." In addition, I would like to add the category "base". Table 3.17 gives a summary of the base, generator, and filter operations.

64

Torque Tools

Chapter 3

Table 3.17.
Base, generator, and filter Terraformer operations.

Base
General

SUmmary
This is the default operation. It cannot be removed from your list of operations. The values set in this operation are used by subsequent generators and filters.

Generator
ffim Fractal

SUmmary
The random fractional Brownian motion generator (if you were wondering what the acronym means) is a basic terrain generator. It produces rolling hills with various steepness based on settings. It tends to produce smoothly topped hills but can produce jagged peaks. Another fractal-based generator, this tends to produce hills with serrated (or sharp) peaks.

Rigid MultiFractal Canyon Fractal

This fractal-based generator produces a series of troughs (canyons). It can produce shallow to deep canyons that run straight or twist. This generator would probably be impossible to get a handle on without the code. However, a quick peek shows that this generator creates terrain by iteratively adding the scaled sum of a sine and cosine pair with some basic noise for flavor. Just remember that, ignoring the noise element, all terrains produced with this generator have the same base shape. Your choice of settings will determine how this shape is applied to progressively smaller sections of the terrain. I'll give more details below. This operation allows you to import an image file as your terrain heightmap.

Sinus

Bitmap

Filter
Turbulence

SUmmary
This filter erodes and redeposits terrain features and kind of reminds me of the smudge brush applied algorithmically. It seems to erode more than it redeposits. Both of these actions are done in a swirly, turbulent (therefore the name) fashion. This filter significantly alters the look of your terrain. This is a simple nearest-neighbor averaging filter. It will tend to remove jagged areas in your terrain. 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 operation). No smoothing is done for features above the water height.

Smoothing Smooth Water Smooth Ridges/ Valleys Filter


I

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. This filter allows you to adjust groups of like elevations globally. In other words, terrain heights are divided into discretely modifiable groups, from lowest elevation to highest elevation. This is a very aggressive eroding filter. You can rapidly remove materials from sloped areas of your terrain with this. The official docs say this uses a "thermal erosion" algorithm. This is a very weak eroding filter. The official docs say this uses a "hydraulic erosion" algorithm.

Thermal Erosion Hydraulic Erosion Blend

This filter allows you to combine two existing operations via a set of mathematical operations, blending them together. We will look at an example of this shortly.

65

Part !I

Engine Overview

How Operations Are Applied

Operations are applied to the terrain in the order they appear in the list, top to bottom. This means that if you apply two generators in a row, the second generator's results are the only ones that will be seen. More interestingly, you can apply filters in different orders for different results. The best way to learn about these operations is to experiment.

Operations' Settings
I'll give a qUick rundown of the various operations' settings and then set you loose.
General Min Terrain Height (0 .. 500). Defines the lowest possible point in the map. Tools and generators will not be allowed to create terrain elevations lower than this. Height Range (5 .. 500). Defines the maximum difference between min height and max height. Therefore, max height = = min height + range. Water Level. A global value used as input to subsequent filters. It does not place water.

Center on Camera. Sets the map origin to the current camera location.
fBm Fractal Hill Frequency (1..24). Indirectly determines number of hills. Higher values create more hills. Roughness (0.0.. 1.0). Determines roundness of hills. Lower values tend to create more rounded hills, while higher values create taller and more pointy hills. Le., steeper slopes. Detail (very Low..Very High). In terms of visual results, higher values produce more jagged peaks (knife edges). Random Seed. Seed that feeds into random portion of generator. Using the same value for subsequent generations produces the same sequence of numbers. New Seed. Creates a new seed. Rigid MultiFractal Hill Frequency (1..24). Indirectly determines number of hills. Higher values create more hills. Roughness (0.0 .. 1.0). Determines roundness of hills. Lower values tend to create more rounded hills, while higher values create taller and more pointy hills, i.e. steeper slopes.

fBm Fractal Tips: If your height range is large (say 350+). you will tend to have jagged hills. regardless of other settings. With a default height range (300). Very High Detail will tend to create knife-edged hills. even for low Hill Frequencies /8).

66

'---

Torque Tools

Chapter 3

Detail (Very Low.. Very High). In terms of visual results, higher values produce more jagged peaks (knife edges). Random Seed. Seed that feeds into random portion of generator. Using the same value for subsequent generations produces the same sequence of numbers. New Seed. Creates a new seed.

Canyon Fractal
Canyon Frequency (4 .. 10). Number of canyons to produce. Chaos (0.0 .. 1.0). A value of zero will produce very artificial-looking and straight canyons. A value of one will produce squirrelly features, almost unrecognizable as canyons. Random Seed. Seed that feeds into random portion of generator. Using the same value for subsequent generations produces the same sequence of numbers. New Seed. Creates a new seed.

Sinus
Scale (on ..ofO. Although the scale implies there are ranges of values for each control point, values are either on or off. Dragging a control point to the bottom turns it off. Any other vertical position is on. Random Seed. Seed that feeds into random portion of generator. Using the same value for subsequent generations produces the same sequence of numbers. New Seed. Creates a new seed. Control Points. Controls number of points on scale. Type values into this field. More control points mean more detail, i.e., higher levels of subdivision and iteration. As mentioned before, the Sinus generator builds the terrain using a combination of sinusoidal values and noise. If you want to see the underlying structure, set the seed to O. Now, poking around with the control points will produce something that looks like Figure 3.42. Now set the number of control points to 3. Notice in Figure 3.43 that the overall structure is still recognizable.
Figure 3.42.

Sinus generator with seven control points.

67

--------

Part II

Engine Overview

Figure 3.43.
Sinus generator with three control points and various point scales.

Turbulence Thrbulence Factor (0 .. 1.0). Determines strength of action. Lower values mean less displacement and less variation in height. Higher values mean vigorous swirling and modifications to height. Radius of Effect (1..40). Determines filter size. 1 equals a 3 x 3 filter, 2 equals a 4 x 4 filter, etc., up to a 42 x 42 filter. Smoothing Iterations (0 ..40). Determines number of smoothing passes to run. Aggressiveness (0.0.. 1.0). A relative factor, determining how much material to remove. Smooth Water Iterations (0..40). Determines number of smoothing passes to run. Aggressiveness (0.0 .. 1.0). A relative factor, determining how much material to remove. Smooth Ridges/Valleys Iterations (0..40). Determines number of smoothing passes to run. Aggressiveness (0.0.. 1.0). A relative factor, determining how much material to remove. 68

Torque Tools

Chapter 3

Filter Scale. Each control point corresponds to a specific height (see below for calculation). Subsequent applications change these values. Control Points. Determines now many elevation bands there are.
You can make significant and rapid changes to your terrain with this filter. Understanding how this works can be kind of tricky. At first, you might think that the ranges will be based on the Min Terrain Height and Height Range set in the General settings. This mayor may not be true. If your current terrain extends to the lowest and highest points, then, yes. However, let's say your Min Terrain Height is set to 0, but your lowest elevation is 100. Also, Height Range is set to 200, but your highest elevation is only 200 (Le., half the range). Then, the elevation bands are determined as fonows: Lowest elevation: 100 world units Highest elevation: 200 world units Control points: 5 Width of each elevation band: (200 - 100)/5 == 20 world units Resultant elevation bands (left-to-right in scale):
Control Point 1 Control Point 2 Control Point 3 Control Point 4 Control Point 5 100.. 119 meters 120,.139 meters 140.. 159 meters 160.. 179 meters 180..200 meters

Moving a control point is like grabbing all elevations in that band and raising or lowering them by a relative amount. Additionally, there is a pushpull relationship between bands of elevation; that is, by modifying one band, you also (slightly) modify all other elevation bands. Figure 3.44 shows some sample changes so you can judge for yourself. This tool rapidly changes the face of your terrain, so caution is the word. Please note that, by default, the scale comes up looking like Figure 3.44a (only it has seven control points). If left like this, no changes will be made. In Figure 3.44b, we raise the low elevation band as much as possible. Remembering that lighter values are higher elevation, notice that some previously dark regions are now very light. Also, notice that, overall, the total elevation of the m.ip seems to have been lowered. In Figure 3.44c, we lower the high elevation band as much as possible. Lo and behold, previously high areas are now completely dark, but what else has happened? The rest of the map seems to have raised. In Figure 3.44d, we've lowered all bands except for the middle band. As can be easily seen, we've basically said, "make the middle band the highest range."

69

Part 1/

Engine Overview

Figure 3.44. Sample changes using control points.

a.

,.

b.

,
c.

..
~

::::

d.

Thermal Erosion Tips:


For multiple iterations, if a slope falls below the Min Erosion Slope, erosion no longer affects that area. The Material Loss value is a bit misleading. A 100% loss does not mean, "set this value to lowest height." Instead it means something like, "set this value to lowest nearby height." This is a very vigorous filter, quickly removing large quantities of material.

Thermal Erosion Iterations (0 .. 50). Determines number of smoothing passes to run. Min Erosion Slope (0.0 .. 89.0 degrees). Defines a cutoff slope value. What this is saying is, do not apply this erosion to slopes with a current value lower than that set here; i.e, if a slope has a IS degree inclination and this value is set to 45, no changes will be made to that part of the map. Material Loss (0 .. 100). The relative percentage of material that should be removed per pass. Hydraulic Erosion Scale. No effect. Iterations (0 .. 50). Determines number of erosion passes to run. Control Points. No effect.
Hydraulic Erosion Tips:
This is one of those cases where having access to the code shortens research drastically. The scale (filter) is passed in to the erosion method but not used. So, whatever changes you make to it are going to be ignored. Since control points are part of the same mechanism. you can ignore these, too. The only thing you need to modify is Iterations. This sweet little filter fills one duty: erode the channels, or Jaw points, between steep hills. It erodes wide flat basins, too, but the effects are not as noticeable. You've got to admire the person who coded this. To write an algorithm that consistently targets a specific terrain feature for erosion? Brilliant!

L70

Torque Tools

Chapter 3

Blend
The parameters to this filter modify the blending equation above the Apply button. Easy as pie. Just remember that Source A is always the operation prior to this blend. (Yes, it can be a blend of a blend of a ... well, you get the idea) Figure 3.45 shows the recreation of a nice terraformer sample from the 7hbes 2 days. It nicely demonstrates the power of the Blend filter.
Figure 3.45.

The Blend filter.

Step 1: General Min Height: 20 Height Range: 200 Water Level:

Step 3: fBm fractal Hill Frequency: 24 Roughness: 0.000 Detail: Very High Seed: 1588197333

Step 2: Rigid Multifractal Hill Frequency: 1 Roughness: 0.000 Detail: Very Low Seed: 2080079341

Step 4: Blend Factor: 0.358 Source B: 1 Operation: Max

Loading a Bitmap
I have purposely deferred a discussion of loading your own bitmaps until the end. Of all the questions I see asked over and over in the forums, one of the most repeated is, "How do I load a bitmap as my terrain?" As you would imagine, doing this is relatively simple. Once you have the PNG file you wish to use as your terrain bitmap, simply place it anywhere in the current mod directory (gpgt\data\heightFields, for example). Now, in the Terraformer, select the bitmap operation and choose the newly placed bitmap as the operation's source file. Click Apply, and you're done.

Although "loading a bitmap" seems to imply a BMP file, you must actually use PNG files.

71

_.'

_._--

Part"

Engine Overview

Loading a Terrain File


Similar to loading a bitmap is the operation to load a previously created terrain file. When you select this operation, the engine will pop up a dialog from which you may select any currently available terrains. Be warned that this operation will completely replace your current terrain. Also, if you are missing textures that are used in the to-be-loaded terrain (or the textures are in a new location), your terrain painting may start off white. To resolve this problem, please read the section "Fixing Broken Terrain Paths" at the end of Section 3.12, "Terrain Texture Painter."

If you have not yet followed the steps in Section 14.4, "Setting

Up Our Workspace:' 3 10 please do so befor<D~' doing Lesson #

1.,

5 M

R L # 1 (90 P S) aze unner esson ercent tepTerrain for Our Game

Here is the first of several lessons in which we'll apply the massive amount of knowledge we're gaining in a practical situation, building our own simple game step by step. In this first quick lesson, we'll create a terrain for our game to be played out on. Follow the simple steps in this section to get started.

Figure 3.46.
a. Terrain preview.

Copy Required Files


From the accompanying disk, please copy the "\MazeRunner\Lesson_001 \ heightFields" directory into "\MazeRunner\prototype\data".

Generate New Terrain


To generate the cauldron for our game terrain, do the following (see Figure 3.46) . 1. Quit the Lesson Kit and start up the Maze Runner prototype. 2. Start the Maze Runner mission. 3. Start the Terraformer. 4. Use the Bitmap operation to generate a terrain using the file "\MazeRunner\prototype\data\heightFields\mazerunner.png". After applying the generator, the terrain should be shaped like a cauldron. Save the mission.

b. Terraformer settings.

Adjust Spawn Point


Now we have a simple terrain. You might also want to use the Inspector to remove all but one spawn point and to position it at "a 0 100" so we don't have such a long way to fall when we spawn into the mission again. Now, don't forget to save your changes.
72

Torque Tools

Chapter 3

And with that, we have taken the first small step towards making our little Maze Runner game!

3.11 Terrain Texture Editor 3.11.1 Starting the Terrain Texture Editor
1. Start the World Editor by pressing Fll. 2. Start the Terrain Texture Editor by pressing F8.

3.11.2 The Terrain Texture Editor Preview Window


After the Terraformer, the Terrain Texture Editor is probably the second most complicated tool in the World Editor tool kit. Again, we're faced with an array of operations that can be performed, based on various factors and settings. When all is said and done, this tool's main goal is to allow us to place textures on our terrain via selection algorithms and calculations. The end result of said placement can be a very natural- or unnatural-looking landscape. Like the Terraformer, we have the preview window, operations tree, and settings window (see Figure 3.47). In addition, we have a textures list, snuggled between the settings window and the operations tree.
Figure 3.47. Terrain Texture Editor screen.

I-

73

Part II

Engine Overview

3. 11.3 The Texture Editor Textures list 'Loading Texturesj


Figure 3.48.
~."et'r.",...tfer.ull,png

fplIdIilIAerrllinaldet.18Jdetd' .png

errllnaJ9r ...lendJI:IMch.,,IP;
fpaJdlltllAef r.lnal;r a1mdhlllnd ,pg fpsJd."AerrMM:"CorchedJscorched_dk png fpa1detaJt lIrrl!lins" corchedll:corched_I PrIg

Figure 3.49.

Figure 3.50.

Before we can start texturing our terrain, we need to decide which textures will be part of our palette. To load a terrain, simply click the Add Material ... button and select a terrain from the dialog that comes up (Figure 3.48). The Terrain Texture Editor places our textures in layers. The first (topmost) 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. In this case, we have selected the grass texture as our base texture (Figure 3.49). 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). See Figure 3.50. In the case that two textures (besides the base texture) are applied to the same pixel on the terrain, they are blended.

3. 11.4 Terrain Texture Editor Operations


Fractal Distortion (Base Filter) Every texture gets a base filter called Fractal Distortion, the purpose of which is to provide randomness to the way the textures are placed. The interface is very similar to the Terraformer's fErn generator. The major difference is the scale interface. The math behind the filter and the interface controls are somewhat complicated to describe. Honestly, the easiest way to understand what this filter does is to simply play around with it a bit and observe what happens with various settings.

74

Torque Tools

Chapter 3

Place by Fractal The Place by Fractal filter is also complex to describe, and in my opinion, not incredibly important for your day-to-day game-development needs. Here again, if you're interested in learning about this filter, the best way to understand it is to experiment, testing different settings and values, and noting their effects.
I

r-

Place by Height This filter has a simple purpose. It places textures at certain delineated elevation bands and blends them based on the vertical setting for that band. I'll show the result of this in Figure 3.51. Place by Slope As with the Place by Height filter, this filter is 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. Place by Water Level This last filter allows us to place a texture at or below the water level we set in our Terraformer. This is useful if you are building an island or have a large lake, but I suggest avoiding it otherwise. Instead, for smaller bodies of water, you can hand paint using the Texture Painter, which we'll be talking about in Section 3.12. Because it is said that a picture is worth a thousand words, Figure 3.51 includes some sample pictures of the Terrain Texture Editor in use.
Operation: Fractal Distortion Fractal Distortion Settings: Hill Frequency: 20 Roughness: 0.0 Random Seed: 801422093 Control Points: 7 Material: Grass

r
I

l
.

Figure 3.51.

Samples of Terrain Texture Editor.

k l

Base layer of grass only.

75

Part II

Engine Overview

Figure 3.51 (continued).

Operation: Place By Fractal Fractal Mask Settings: Hill Frequency: 16 Roughness: 0.0 Random Seed: 821699541 Control Points: 7 Material: detail1

Operation: Place By Height Height Mask Settings: Control Points: 6 Use Fractal Distortion: true Materials: Grass, detail1

Operation: Place By Slope Slope Mask Settings: Control Points: 7 Use Fractal Distortion: true Materials: Grass, detail1

Grass base with detaill applied by slope. Notice slope setting set to place only on least steep (flattest) areas.

76

Torque Tools

Chapter 3

Operation: Place By Height Height Mask Settings: Control Points: 6 Use Fractal Distortion: false Materials: Grass, detaill, patchy

Figure 3.51 (continued).

Grass base with detaill and patchy set to apply at same elevation (by height). Notice blending of detaill and patchy.

3. 12 Terrain Texture Painter (Terrain PainterJ 3. 12. 1 Starting the Terrain Texture Painter
1. Start the World Editor by pressing Fll. 2. Select Terrain Texture Painter from the Window menu.
Figure 3.52.

Terrain Texture Painter screen.

77

---------

Part II

Engine Overview

3. 12.2 Examining the Terrain Painter


The last of the tools we will examine in this section is the Terrain Painter. Among all the tools, this is probably the most straightforward. If you have successfully loaded the Terrain Painter, you will see something like the image in Figure 3.52. If you have used any tools like Worldcraft, Wally, or any of a number of other content-creation tools, you will be familiar with the concept of a palette, but just in case, I will describe it.
The Terrain Painter Palette

Figure 3.53.
"Load File..... dialog box.

Currently, the palette is limited to six textures. Also, the palette texture "spots" must be loaded in counterclockwise order. In other words, if you tried clicking on the Add ... button in the upper right corner right now, nothing would happen. Try clicking the Add. .. button in the middle left. Load the patchy.png texture. When you click either an Add ... button or a Change... button, the Load File ... dialog pops up (see Figure 3.53). The tool will automatically find files in either of the following two formats:

--

1. Portable Network Graphics [* .png)

2. JPEG [* .jpg) I strongly suggest using PNG files. Also, you should adhere to strict rules regarding the dimensions and color content of your graphics files.
Dimension DPI or PPI Pixel Depth/Colors Alpha Layer
Required: 256 x 256 pixels Suggested: 72 Suggested: 24/16 million Suggested: none

78

On the right side of the screen, you should see a window that looks similar to Figure 3.54. In this image, there are two of six allowed textures enabled. The purpose of this window is to act as a sort of painter's palette for textures. Simply by dabbing your cursor on [clicking on) a loaded texture, you can use that texture to paint the terrain with the now familiar brush. As with the Terrain Editor, you can change the shape, size, and hardness of the brush. In this case, the hardness will affect blending. A softer brush provides a softer stroke,

Torque Tools

Chapter 3

therefore less of the new texture is applied per stroke, with more of the underlying texture showing through. Give it a try. Click on the "patchy" texture and paint some lines, swirls, whatever. Cool, eh?

Figure 3.54.
The palette.

Fixing Broken Terrain Paths IAII White Terrain)


Sometimes, we will find it necessary to move our terrain files (* .ter) and/or our terrain textures. As a result, the next time we relight our terrains, they may render without textures. TGE embeds relative texture paths within the body of the terrain file, so when we move the terrain file, we break these paths. Fixing this problem is relatively painless. Simply follow these steps. 1. Load the mission with the broken terrain. The terrain will be all white (see Figure 3.55). 2. Open the Terrain Painter tool. If you examine the painter palette, you will see that all of the texture slots are blank, even though they do have the texture name listed (see Figure 3.56). 3. One by one, click on the Change ... buttons and relocate the matching texture (as is listed by the blank palette chip) using the Texture Selection dialog (see Figure 3.57). 4. Finally, save the terrain, and your terrain file will be fixed!

Figure 3.55.
Broken terrain; shows up as white.

Figure 3.56.
Blank palette texture slot.

Figure 3.57.
Texture Selection dialog.
bumpO.png bump1,png defl!luft.png grali8lP!i1

petchyjpg send.jpg

79

---- ---

---

- - - - - - - - - - - ------

Part II

Engine Overview

3.13 World Editor Quick Tips 3.13. 1 Manipulator (F 11 + F2J Tips


Translating rotated objects. To move a rotated object. press and hold SHIFT before selecting the object's gizmo. This will force the gizmo to align to the world axes. thus making it easier to move the object about. Quick scaling. Select the object to be scaled. Press and hold CTRL + ALT, then hover the mouse over the side to be scaled. A blue hash will appear. Clicking on this and moving the mouse scales in the selected dimension. Quick move. Select an object with the mouse and then, being sure the gizmo is not highlighted, click on the object's bounding box and hold the left mouse button. Now you can move the object about the world plane in x and y directions. Better placement (snap-to). Thrn on the editor's snap-to by opening the console (N) and typing "snapToggle();". Now, open the WorldEditor Settings dialog (under the Edit menu) and set the Move Scale to the value you want snaps to happen on. While moving objects with the gizmo. movements will snap to this increment.

3.13.2 Inspector (F 11 + F3J Tips


Staying organized. Make sure to add SimGroups before placing objects, and tben be sure to ptace objects in them when populating the mission. Locking objects. To make editing simpler and safer, objects can be locked. A locked object cannot be translated, rotated, or scaled using the mouse. However, changes can still be applied via the Inspector pane. To lock an object, simply select the object in the Inspector and add a dynamic field named locked. If locked is set to true, the object will be mousemodification immune. To unlock the object, delete the variable or set it to
false.

3.13.3 Creator (F 11 + F4J Tips


New Interiors are black (relighting the scene). In versions prior to TGE 1.4, interiors started out black and needed to be "relit" to get their textures. To get the textures to render and to get the shadow for this DIF baked into the terrain, you need to relight the scene. Simply type ALT + L. In version 1.4 and beyond, all newly placed interiors will be draft-lit. This means that the interior will be lit, but no self-shadowing or terrain shadowing will be done. To see the final lighting results, you will still need to relight the scene. Draft lighting was added to save time and give better feedback while editing.

80

Torque Tools

3.13.4 Area Editor (F 11 + F5J Tips


Quick camera/player movement. To quickly move the camera/player to a point on the map, open the Area Editor and click on the preview map. The camera/player will be moved to that point on the map.

3.13.5 Terraformer (F 11 + F7J Tips


Setting map to zero elevation (perfectly flat). To create a map at zero elevation, open the Terraformer, add the operation Terrain File, do not select a terrain file, and apply the operation. Because no terrain file was selected, the loader will flatten the terrain to zero elevation. Setting map to non-zero elevation (perfectly flat). To create a flat map at a set elevation, open the Terraformer, then:
remove all current operations; add a Sinus operation; click on General and set Min Terrain Height to desired height; type a in Height Range (clicking roller buttons does not allow this, only typing); click on previously added Sinus operation; and click Apply.

3. 13.6 Terrain Painter. (Windows -7 Texture PainterJ Tips


Increasing texture count. Search the GarageGames website for a resource titled "8 terrain textures instead of 6" that provides an easy solution for increasing the number of textures the Terrain Painter palette can hold.

3. 13.7 General Editing Tips


Better placement (use the grid). Use the grid like a ruler when placing objects. Don't forget that the grid size can be adjusted in the World Editor Settings dialog. Using this feature in addition to snap-to can simplify placement greatly. Reduce visual clutter while editing. When the number of objects in a mission is significant, too many labels (centroid + object ID) may be visible. To alleviate this, either disable "render object text" (in the World Editor Settings dialog), or reduce the "vi.sible distance" (a field in the Sky object). Exact scaling. When you want to scale an object by an exact factor, use the min/max scale factor settings found in the World Editor Settings dialog. By simply setting these factors to the same number, then applying a scale operation, the object will scale to the exact value.

81

-~-------

Part II

Engine Overview

Stop selecting far objects accidentally. Along with visual clutter, sometimes it occurs that far objects get selected by mouse movements; i.e., while attempting to select a near object, a far object is alternately or additionally selected. Simply reduce the "project distance" for the pointer (World Editor Settings dialog), or reduce the "visible distance" (a field in Sky object). Stop objects falling through terrain during placement. To keep objects from falling through the terrain while you place them, open the World Editor Settings dialog and uncheck "Planar movement", then uncheck "objects use box center". Now sliding objects around with the mouse will be less likely to cause them to fall through the terrain. Speed up scene relight. When editing, we don't necessarily care if the scene lighting is perfect. To speed up scene relights (ALT + L), open the console (N) and type
$pref: :sceneLighting: :terrainGenerateLevel=O;

Now relights will be done with the lowest precision. In general, this is still pretty good, and it may be all right to leave it here. Note that the highest value is 4, but this setting can take 50 or more times the length of time it takes to relight with a setting of O.

3.14 The GUI Editor

3. 14. 1 Starting the GUI Editor


1. Select the GUI you wish to start editing in: Main Menu (for now start here); In-Game (playGui); or other ... anywhere else in your game. 2. Start the GUI Editor by pressing FlO.

3. 14.2 Examining the GUI Editor


If you have just started the GPGT Lesson Kit and then pressed FlO, you will see pretty much what is shown in Figure 3.58 except the Content Editor will contain the Main Menu. The GUI Editor can get confusing quickly if you don't know what you're doing, or if you don't pay attention to what you've done. As Figure 3.58 shows, there are four areas to the GUI Editor. In clockwise order from the top left, they are as follows.

1. Content Editor. This is the place where you will be mouse-interacting with your GUI(s).

82

I... r

~--

~-

----

Torque Tools

Chapter 3

Figure 3.58.

The GUI Editor screen.

2. Content Tree. This will display the hierarchical (parent-child) relationship of the controls in the current CUI. 3. GUI Inspector. This inspector is similar in function to the World Editor Inspector in that it is used to display all the data about a chosen object. In this case, the object is a selected CUI control (a window, button, slider, etc.) . 4. Toolbar (at top, not labeled). The toolbar provides several major functions, which will be described shortly. This all may seem simple enough, but there are a few things to beware of and a few things you should know before you start.

3. 14.3 Things to Beware!


Hosing up is so easy to do. Please understand that it is very easy to completely hose up an interface if you are not cautious. That said, it is an excellent idea to make frequent backups of your project. I can't see jack ... The minimum resolution for editing CUIs is 800 x 600, but if at all possible, I suggest editing at 1024 x 768. You will find that the Content Tree and Inspector are much easier to use and read at this resolution.
83

-_. -

----

~ . ~ ~ - _ . ~ - - -~ - - -

Part II

Engine Overview

I'm stuck! It is easy to get stuck while editing CUIs. That is, you may find yourself in a situation in which you are unable to exit the CUI Editor. This will happen less and less as you become more familiar with the tool and its operation. If you do get stuck, you can kill the application. Duplicate GUIs (backups). If you edit like I do, you may be accustomed to creating backup copies of your files as you work. You may continue to do this, but you should not leave them in the current game directory hierarchy. If you do, TCE will find them. Subsequently, when you go to save, this will confuse the save dialog when it tries to find the best place to save the CUI you are editing. Start and stop the editor from the same parent GUI. The most common mistake I made when I started playing around with the CUI Editor was starting the editor and then later trying to exit after having switched to a different CUI. For example, try the following.
1. Start the CPCT Lesson Kit and press flO. 2. ~ on MainMenuCui and switch to playCui. 3. Try to exit by pressing FlO. 4. Huh? Now the mouse is locked, and the screen didn't change. Hmmm ... 5. Press FlO (should be able to move mouse now). 6. ~J on the button with playCui displayed. 7. Switch back to MainMenuCui. 8. Try to exit again by pressing FlO.
9. Voila! Back to the Main menu.

So what happened there? Well, flO gets us in and out of the editor, but if we have changed the current menu, it exits into the new editor in order for us to test it. The reason the screen seemed to lock up was because the playCui was not properly activated. The important thing to remember is this: when you are finished editing CUIs, switch back to the CUI you started from, and then exit the CUI Editor.

..
3. 14.4 GUI Editor Basics
We will review the user-interface devices. Then, we will discuss the mechanics of control manipulation, CUI navigation, how to add new controls to an existing CUI, and how to create a new CUI.

GUI Editor Devices


As with the World Editor, there are graphic controls that provide you with feedback while editing. There are far fewer of these devices in the CUI Editor, but it is important to give them a quick review (see Table 3.18).

84

Torque Tools

Chapter 3

DevIce

DescrIption
When you have a single control selected, eight black squares will show up. The little squares are handles that allow you to resize the control.

Table 3.18. GUI Editor graphic controls.

Single Select
Similar to the single select is the multi-select. When you have multiple objects selected, each object will have eight squares along the perimeter of the object. The difference is that when the squares are white, it means that you cannot resize. Only dragging is allowed.

Multi-Select

When you have a single control selected and are editing fields in the Inspector, the handles turn gray. If they have a white outline, it means APPLY will take effect if clicked.

APPLY OK When you have multiple controls selected and are editing fields in the Inspector, the handles on all selected controls turn gray. If they have a black outline, it means APPLY will not take effect if clicked. Think of the black outline as a warning. Sometimes it isn't obvious when objects get crowded together. The final device is the Add Parent box, Hierarchy in your GUI is an important concept. In order to create hierarchy (add controls to existing controls vs. to the top parent), you must select an Add Parent. The Add Parent box gives feedback, showing which control is the current Add Parent.

APPLY OK

Add Parent

85

Part II

Engine Overview

3. 14.5 Control Manipulation


Now that we've covered the basic CUI Editor devices, let's talk about how we manipulate controls; i.e., how do we resize, move, etc. See Tables 3.19, 3.20 and 3.21. Unlike the World Editor, the CUI Editor has no menu, so you have to use hot keys to cut, copy, and paste. Naturally, these hot keys are the same in both editors.

Table 3.19.

Moving and resizing (mouse and keyboard).

Mouse Resizing

an object(s) in either the Control Editor or the Content Tree. (Multiselect only works in the Control Editor.) AND

Elf

Elf a handle and drag.


Mouse Moving
an object(s) in either the Control Editor or the Content Tree. (Multiselect only works in Control Editor.) AND

Elr

Elr a selected object(s) (not on a handle) and drag.


Keyboard Moving
an object(s) in either the Control Editor or the Content Tree. (Multiselect only works in Control Editor.) AND (Up, Down, Left, or Right) Arrow Key moves one pixel in selected direction. OR

Elf

SHIFT + (Up, Down, Left, or Right) Arrow Key moves ten pixels in selected direction.

Table 3.20.

Moving and resizing (Layout menu).

Align left (CTRl + l) Align all selected controls' left edges to left edge of leftmostselected control.

~:".:<~~.:

. .~:
,'1 '..::...--:-

.. _-- .. _---.
-.I

: ~ ~-:::f,:~~;~.:
~-=-.
1

:-.--..:.-.,. .
:

:;'.

--.-.~.

---- ....

-,_.' ...

_-. ::
After

Before

86

Torque Tools

Chapter 3

Align Right (CTRL + R) Align all selected controls' right edges to right edge of rightmostselected control.

. ~ . _ .

~.1' _'J:\~'

Table 3.20 (continued).

z~:---: 0_.-_.._--!.':!-: ~!~~.'

:~:-.

.
0

....

0---0

--.--:

;~._--

.,...----.--- ----. .-'..


~ ;;~ ~.

Align Top (CTRL + T) Align all selected controls' top edges to top edge of topmostselected control.

..

Before

.~.~.

"',',.: ~h..

..--.--.

I '''"
I

After

Ii. . /; ',. ._._.

e~,

t!'

Align Bottom (CTRL + B)


Align all selected controls' bottom edges to bottom edge of bottommostselected control.

.-----.----.-...l .-.-. .---;-::-.-.-. .--.--...-.-.

Before

" rXIril~~~'f.~ :.' ...~. "..,-.~ 7"'~"


~

~ILl;~ ,~""Il..{ -.! .. _ ."!':li ' . .

',:r, '",'~~ , ~ " . '>.


1 .....

J.

After

Ii

....., ~- ltC

,..:. ",..... <.-.-.-~-.--.


"

.
I

.~;

"

.\'

,'.- '.

.,
Before

.-----.-----..... . . . .--.--. .-.-. .

~~:'~.t."

, ' " _ ..

._"-.

,.

,,,,..
I

,.

~~<...
' ,

Ii
I

' .

-,

After Center Horizontally


centers all selected controls horizontally within rectangle defined by edges of outermostselected controls.

Before

After

87

Part II

Engine Overview

Table 3.20 (continued).

Space Horizontally Evenly spaces controls horizontally within bounds of leftmost andrightmost edges.

.--.--.
. ; II, .r , ; .

, .~ ';~~'." .Il~I,.

~.--.

II t;;;r".-:,.

".

.~~-.
..... .

'(t~ I ' ..

Before

'

. --.~.

II
Space Vertically Evenly spaces controls vertically within bounds of topmost and bottommost edges.

___ .---.-,-. ,.r,,

II ' . .
After

,'. ~.

.--.~. '~~ i"

",...

,_.1. /.).

Before Bring to Front Raises control so that it displays on top (in front) of any siblings.

After

Before

After

send to Back Lowers control so that it displays below (behind) any siblings.

Before

After

88

Torque Tools

Chapter 3

Cut Copy Paste (to current Add Parent)

CTRL + X CTRL + C CTRL +V

Single or multiple controls OK. Single or multiple controls OK. Please note that, if controls are selected, the paste will happen (to currently selected Add Parent), but you will not see the pasted . objects if they are not normally visible unless selected.

Table 3.21.
Cutting. copying. and pasting.

Add Parents
I've mentioned the term Add Parent a few times now, but to be absolutely clear, I'm going to discuss it one more time. In order to add a new control as a child of another control (parent), you must have selected the parent control by right-clicking it. If properly done, the control that you wish to be the parent will get a yellow and a blue outline (the yellow might look green). Now, any added controls will automatically become children of the Add Parent control. There is no mouse-only method of moving a child into a parent. You'll either have to:
1. cut, select Add Parent, and paste, or

2. edit the CUI file by hand later.

GUI Navigation
In order to edit an existing CUI, we need to know how to get to it; i.e., we need to know how to load a CUI into our Content Editor. If you have been following this guide in order, you have already done this. However, even if you have, there are a few ways to do it.
If you want to edit the Main menu, simply start the CPCT Lesson Kit and open the CUI Editor.

If you want to edit the playCui, simply start the CPCT Lesson Kit, load the "World Editor Training" mission, and open the CUI Editor. What, however, do you do if you want to edit a CUI that isn't easy to get to work with the Load Mission dialog, for example? Let's say we want to add a label to the existing Create New CUI dialog. How would we get to it if we started editing in the Main menu? Assuming that you are at the Main menu: open the CUI Editor (FlO), and
Figure 3.59.
Create New GUI dialog.

Elf NewCuiDialog from the middle pull-down (above Content Editor).


New Cortrol
640 x4lllJ

At this point, the Create new CUI dialog should be visible in the Content Editor (see Figure 3.59). That is basically it. Just select whatever CUI you need to edit, and there you are.

89

Part /I

Engine Overview

Figure 3.60.
Content Tree.
~ 1224: MllnMllnuGul OulChunkldBitmlp(
12215: . GulElitm .pButtonCtrl

Before we move on, get yourself back to the MainMenuCui and close the CUI Editor (FlO).

3. 14.6 Adding Controls to an Existing GUI


Adding a new.control to an existing CUI is very simple. That is, adding the graphical portion is simple. We'll cover hooking scripts to your new controls a little later. For now, do the following. Start the CPCT Lesson Kit. Open the CUI Editor (FlO). Expand the Content Tree by clicking on the [ + ].

1:227: Gl.lIBitmlpButtonCht

1220: - OlliBitrnlpBlitlonChl
1231: OuiBitmlpButtonCtrl 1233: .OulBHmllpButtonCbl 1235: .Oui8ilmlllpButlonCtrl
1237: . GuiBltm.lp811tfonCtJI

1:230: OuiBitmlp8uttQnCtri

Figure 3.61.
New button.

~r on MainMenuCui (top of tree) to select it as your Add Parent. Your Content Tree will look something like Figure 3.60. Also note that the main window now has a yellow and a blue outline, meaning it is the Add Parent.

5 on the New Control button and select CuiButtonCtrl from the pop-up
list. A new button will appear in the upper corner of the Content Editor. Drag it so that it is on the right side of your Quit button (see Figure 3.61). In the Inspector, give your new button the name "My First Button." ~f on APPLY and verify that the button now appears in the Content Tree and that it has a name (Figure 3.62). Now, in the Inspector, make the command equal to "quitO;" and ~J on APPLY again (see Figure 3.63). Now, to save your work select File ~ Save CUI, select MainMenuCuLgui, then ~r on the Save button in the dialog (see Figure 3.64).

Figure 3.62.
"My First Button."

Figure 3.63.
Quit command.

Figure 3.64.
Saving the GUI.

90

Torque Tools

Chapter 3

At this point, your changes to the GUI are final. Let's test it. Get out of the GUI Editor (FlO). Exit the GPGT Lesson Kit. Restart the GPGT Lesson Kit. What happened? If you followed the instructions above, you placed spaces in the name of your new button. This is a no-no. If you restarted without deleting the DSO files (as instructed), the old menu is now showing, and the new button is not shOWing. If you deleted the DSO files and then restarted, the splash screen hung when switching to the main menu. In the former case, you can simply press the Quit button and keep reading. In the latter case, open the console by pressing the tilde (N) key, and then type qu it; followed by ENTER. All right, so we've killed the game, but we still have to fix our problem. To do so, follow these steps.
1. Open the file "gpgt\client\Interfaces\mainMenu.gui".

2. Search for "new GuiButtonCtrl(My First Button) {". 3. Replace it with "new GuiButtonCtrl(MyFirstButton) {". 4. Start the GPGT Lesson Kit. S. Click your new button, and the GPGT Lesson Kit quits.

If you are observant, at this point you have a big question. Namely, why did the button (seem) to move from where you put it to somewhere else? That is, we placed it near the Quit button, but then when we ran the app it, well, ... it moved! This brings us to the important discussion of horizSizing and vertSizing.

horizSizing and vertSizing


In general, these settings define how a control will be resized or repositioned when the control's parent container is resized. As a general rule, you can assume that the root container (the Canvas) will have a starting size of 640 x 480, and it (and all of the controls it contains) will be resized/repositioned from this state. As any container is resized, all of its child controls are resized and/or repositioned according to the horizSizing and vertSizing properties of each control. If any of those controls are containers with children of their own, they too will be resized and/or repositioned in the same fashion. This behavior cascades down the parent-child tree of controls. This provides a basic layout capability. The basic settings for these two properties are: center, relative, left/top, right/bottom, and width/height. Each is explained below.
91

----- ---

--

Part II

Engine Overview

Center. This setting will center the control in its container. Only the control's position is altered-the control's extent (width and height) remains the same. Relative. When this setting is applied, the control in question will be resized and repositioned to maintain the same size and position relative to the parent container. For example, if the parent doubles in size, so will this control; additionally, the space between the control and the parent's borders will double. Left/Top and Right/Bottom. These settings only affect position. Extent is unaffected. Simply put, the change in size of the parent is applied to the distance between the control and the specified edge of the screen. This means that the control will maintain its distance from the opposite edge. Width/Height. These settings result in changes to the extent of the control only. The difference in size of the containing control is applied directly to the extents of the control itself.

3.14.7 Creating a New (Parent) GUI


Now, we'll learn how to create a new GUI. I warn you in advance that there is more to this topic than might seem apparent. For now, I'll demonstrate the mechanics of creating a new page. In later chapters, I'll go into greater detail on how the GUI system works. Let's create a new dialog box. The dialog will have a label and a single button.

Figure 3.65. Creating a parent.

The Parent Start the GPGT Lesson Kit. Open the GUI Editor (FlO). Select File ~ New GuL In the dialog that comes up, rename the GUI to MyFirstGui (no spaces) and ~ on Create (see Figure 3.65).
That's it for creating the parent. Now let's add some controls.

The Dialog If you have not yet read through Section 3.14.6, "Adding Controls to an Existing GUI," please stop and do so. If you have, then do the following.
Add a new GuiWindowCtrl control and, using the Inspector, modify the following parameters as indicated. Name: MyFirstWindow position: 200 100 extent: 350 250

92

Torque Tools

Chapter 3

Select the MyFirstWindow control as the Add Parent. Add a new CuiTextCtrl control and using the Inspector modify the following parameters as indicated. Name: MyFirstLabel position: 145 5 extent: 60 20 text: My First Cui (notice the spaces) Add a new CuiButtonCtrl control and, using the Inspector, modify the following parameters as indicated. Name: MySecondButton position: 150 100 extent: 50 50 command: Canvas. setContent

(mainMenuGui) ;

text: Cool Save this CUI under the name MyFirstCuLgui in the "gpgtjclientjinterfaces" directory. Now, quickly test your new CUI by pressing FlO. Note that earlier I warned you not to do this. In fact, it is OK to do so, but you must understand what is happening. We're not really trying to quit the editor. We want to temporarily suspend it so we can test our CUI. It just so happens that this suspending quits the CUI Editor if we press FlO while editing the same page we entered the CUI Editor on. It can be confusing. Your new dialog should look like the one in Figure 3.66. The next step is hooking up our dialog so we can load and unload it in the CPCT Lesson Kit.

Figure 3.66.
Creating a dialog box.

3. 14.8 Loading New GUls


Now that we've successfully created our first dialog box, let's make it available in the CPCT Lesson Kit. In other words, let's use it. If you have been following the guide in order, you have already created a new button in the Main menu. Using what you have already learned, please make the following changes to the button. Select the existing CuiButtonCtrl control named MyFirstButton and, using the Inspector, modify the following parameters as indicated. command: Canvas. setContent (MyFirstGUI) ; text: Open Dialog Save the CUI.

93

Part /I

Engine Overview

Let's test it by pressing FlO to exit the CUI Editor.

~J on the relabeled button Open Dialog should start our new dialog CUI.
~ on the button Cool should return you to the Main menu. Notice that I said should above. If you quit the CPCT Lesson Kit between the sections "The Dialog" and "Loading New CUIs, " then when you ~f on the Open Dialog button, nothing will happen. Why? Because it didn't get loaded. In order for any CUI to be available, it needs to be loaded before you try to wake it. This loading is done in various places. The organization of CUI loading scripts is beyond the scope of this section, but for completeness, I'll show you how to get your new CUI loaded. Open the file "egt\client\init.cs" and search for the following code.
II Load up the shell GUIs
exec("./interfaces/mainMenu/loader.cs") ;

Modify the code so it looks like this:


II Load up the shell GUIs exec("./interfaces/mainMenu/loader.cs"); exec("./interfaces/MyFirstGUI.gui") ;

Save the file. Restart the CPCT Lesson Kit. Now, you can switch back and forth between the newly created dialog and the Main menu.

3. 14.9 Summary
We started this chapter by learning that TCE provides two basic editors, the World Editor for editing the game world, and the CUI Editor for editing interfaces. Next, we summarized the eight tools contained in the World Editor. After the introduction, and for the bulk of the chapter, we worked our way through the individual World Editor tools.

The Manipulator. A full-screen editor made for tweaking the scene arrangement. The Inspector. A partial-screen editor created for tweaking the properties of existing objects in the scene. The Creator. A partial-screen editor with an object selection tree used to create new objects. The Area Editor. A partial-screen editor used to adjust the mission boundaries, to mirror the terrain, and for quick navigation of the camera/player position within a mission.
94

Torque Tools

Chapter 3

The Terrain Editor. A partial-screen editor using various brushes to directly manipulate terrain geometry. The Terrafonner. A partial-screen editor containing algorithmic base, generator, and filter elements used to create (or import) complex terrain geometries. The Terrain Texture Editor. A partial-screen editor utilizing sophisticated algorithmic generators and filters to paint the terrain with stunning detail. The Terrain Painter. A partial-screen editor utilizing a palette and brushes for making modifications to generated terrain textures, and/or for the wholesale editing of terrain textures by hand. Having completed the long World Editor tools discussions, we ended the chapter with a detailed walk-through discussing the use of the GUI Editor. In this discussion, we learned about creating and saving new interfaces. We learned about placing controls and modifying their dynamic scaling and anchor behaviors. Finally, we made a simple interface to exercise and cement our newfound knowledge. At a few points along the way. we took the time to make use of TGE editors/tools to create content for the prototype of the game we will be completing at the end of this guide. All in all. we accomplished quite a lot in this chapter. At this point, you should already be able to open the kit. start the editors, and with some confidence in your results, poke about and start to create the worlds in your mind.

95

..

_----- - - - -

Chapter 4 Introduction to TorqueScript


This chapter offers an introduction to the Torque Game Engine scripting language, often referred to simply as TorqueScript. Besides introducing the TorqueScript language itself, this chapter will provide a foundation to build on when discussing other TGE topics. Before starting, please understand that it is assumed you are familiar with some basic programming concepts. You do not need to be a guru, but having a basic familiarity with C/C++ and object-oriented programming principles will greatly facilitate learning TorqueScript.

4.1 TorqueScript Concepts and Terminology 4.1.1 To Script or Not To Script?


A frequent question I've seen in the forums is, "Do I need to use scripts?" No, you really don't need to use the built-in scripting language if you don't want to, but I'm 99 percent sure that, once you do start to use TorqueScript, it will be apparent that scripting in Torque is by far easier and more efficient than coding and recompiling every change you wish to make. Even though the engine is written in C++ and assembly language, most of your game will be programmed in TorqueScript, denoted by files with the * .cs extension. The advantages of using a scripting language instead of coding everything in C++ are that your game does not have to be recompiled every time you make a change; it's a more-targeted, higher-level game language than C++; and you don't have to worry about memory management. A popular misconception is that scripts are slow; this is not necessarily true. TorqueScript is compiled into byte-code before being executed and is surprisingly fast. While C++ code will always be faster, the flexibility of scripting is superior for most gameplay-related functions. Perhaps you doubt me? Or, maybe you aren't familiar with scripting in general. In either case, let's talk about scripting, and I'll see if I can set you on the right course.

..

What Is Scripting?
What exactly is a scripting language? What can you do with scripts? And why are they used so much in modern game development?
97

---

---

-- - - -

------

Part II

Engine Overview

Scripting languages are programming languages designed to enable scripting. Scripting is the act of using preexisting [engine] components to accomplish new tasks. In other words, we use a scripting language to access features in the engine and then use those features to provide a game experience. Generally, scripting languages are interpreted, not compiled (like c++ and other languages). This makes scripted tasks somewhat slower than compiled tasks, but we make this trade in order to gain flexibility and visibility, as well as ease of use. Because scripting languages also allow you to modify your program without having to recompile it, we are able to rapidly prototype and repair code. This speeds development significantly. Often, scripting languages allow you to write code without worrying about nitty-gritty details like data types or memory management. This is both a boon and a bane. It is a boon as it simplifies many programming tasks, but a bane because it allows us to make mistakes that a strict compiled language and its compiler would find. Given the above, it should be easy to see why scripting languages are used so heavily in modem games. However, if you are not convinced, consider these possible uses for a scripting language.

Prototyping. During the development of your game, you will often need to test out ideas. New gameplay features might come up, or features in the original design might not work as well as envisioned and will need to be modified or replaced. To be efficient, you will need the ability to quickly test all these ideas, so you can decide whether to keep or modify each of them. Creating these quick tests is called prototyping. Scripts are great for prototyping because they are so quick to code with, test, and modify. After you prototype in script, performance-critical functionality can be ported over to C++ for final inclusion in the game. Debugging. Scripts are great for debugging, too. Because scripts are easily modified (on the fly), you can identify problems, address them, and retest without having to recompile and, in some cases, without even having to restart the game. Scripts can also be used to quickly create test units that stress other pieces of code to identify otherwise hard-to-find problems. Game customization and tweaking. The look and feel of a game's interface and many of its gameplay mechanics normally go through tweaks and revisions during the course of development. Thus, it is best to place most code related to these areas in script. This helps to rapidly test different looks and gameplay behaviors, as mentioned in the point on prototyping, above. There is a side benefit to developing your game this way as well. Having code related to your interface and gameplay in scripts allows ehdusers to customize your game to their liking (but only to the extent you choose to allow them when playing the official version of your game). Also, these types of script changes are the basis of many game mods. Mods are
98

Introduction to TorqueScript

Chapter 4

very popular in games like Unreal, Quake, and Ihbes. Taking Ihbes 2 as an example, War 2002, Renegades, and Team Aerial Combat are all script-based mods. From a coding perspective, mods can be very simple to implement when working with games that incorporate flexible scripting languages and use them for much of the game's code. Implementing your game in such a fashion can obviously be a big draw for potential players that are interested in playing and/or creating mods. This allows user communities to breathe new life into your game, extending its shelf life.

Writing non-performance-critical functionality. Really, any piece of functionality that won't have a big impact on performance can be coded in script. Writing render pipelines in script isn't a good idea, but writing code to modify the behavior of an existing pipeline is perfectly feasible and quite common.
Scripting makes sense in many more situations. The inclusion of scripting languages is a powerful feature of modern game engines, and developers are wise to leverage the advantages of scripting at every sensible opportunity.

4. 1.2 Features We Need


If you accept that scripting is useful, what should you be looking for in a scripting language? What functionality should it provide? At minimum, a scripting language for use in a game should provide the following features.

Basic programming-language features. The scripting language should provide all of the basic features' common to modern programming languages, such as powerful variable types, basic operations (addition, subtraction, etc.), standard control statements (if-then-else, for, while, etc.), and subprograms (functions, file inclusion). Access to engine structures. This is a critical feature. For a game engine's scripting environment to be of use, it must provide some kind of interface to manipulate the core engine functionality and structures. The scripting system should allow access to the rendering, audio, physics, AI, and I/O systems. It must also allow the creation and deletion of objects and the definition of new functions .
Some other (very) nice features to have are the following.

Familiar and consistent syntax. Ideally, the syntax of a scripting language is familiar, meaning it is similar to the syntax of a language many programmers are already familiar with, for example, C or C++ . Object-oriented functionality. Object-oriented programming has been a revolution in the art and science of software engineering. Scripting languages that provide object-oriented functionality offer many benefits, including the following.
99

Part 1/

Engine Overview

Encapsulation. Provides a means of limiting access to code and data (not directly supported in TorqueScript). Inheritance. Provides a means of creating new objects from the definitions of existing engine objects and/or scripted objects. Polymorphism. Allows us to override the default behavior of derived object code, whether the object is derived from engine objects or other scripted objects. On-demand loading and scoping. Why have all the code in memory at once when it can be loaded as needed? Besides saving memory, scripting languages that allow the dynamic loading and unloading of pieces of code also make it easy to override a program's functionality on the fly. Means of speeding up scripted code. As noted above, scripted code is not usually compiled-it is simply interpreted at run time. A feature that many common scripting languages (Perl, Tcl, VBScript, Java) provide is the ability to compile scripts into byte-code. This byte-code is then executed on a virtual machine. The benefits of this are size and speed. Byte-code is (normally) smaller than and executes faster than interpreted code.

4.2 What about TorqueScript?


All right, enough generalities. What is Torque's scripting language like? TorqueScript is a strong and flexible language with syntax similar to C++. It provides all of th~ features listed above, including those on the "would be nice" list. The remainder of this chapter is dedicated to script-only functionality. Please note that all the pertinent functions both for scripting and for exposing engine features to the console are covered in the "TorqueScript Quick Reference" appendix.

4.2.1 The Console and Sample Scripts


In order to facilitate your learning experience, many sample scripts are included with the GPGT Lesson Kit. These scripts are organized by chapter. Furthermore, all labeled scripts (labeled in the text of this document) can be run from the console simply by typing the supplied function name. For example, the following sample script:
/ /bt99 () ;

echol "Torque Rocks" ); echo ( 1 + 1 );

100

can be run by typing bt 9 9 () ; into the console command line and then pressing ENTER.

Introduction to TorqueScript

Chapter 4

Loading Loading Loading Loading Loadina Loadina Loadina Loadina Loading Loading Loading Loadina Loading Loading Loading Loading Loading Loading Loadi ng Loading Loadi na Loading Loading Loading

compiled compiled compiled compiled eomp,led compiled compiled compiled compiled compiled compiled compiled compiled compiled compiled compiled compiled compiled compi led compiled eompi led compiled compiled compiled

script script script script script script serlpt serlpt script serlpt script script serlpt script script serlpt serlpt script scn pt script seri pt script script script

egt_base/el eat_base/el egt_base/el eat_base/el eat_base/el eat_base/el eat_base/el eat_base/el egt_base/el egt_base/cl egt_base/el egt_base/el eat_base/el eat_base/el eat_base/el egt_base/el egt_base/el egt_base/el egt_base/el eat_base/el eat_base/el eat_base/el egt_base/el egt_base/el
d~fault

ent/ui /Pl ayGui . gui . ent/ui /ChatHud. aui . ent/ui/playerList.gui. ent/ui/mainMenuGui.gui. ent/ui/aboutDlg.gui. ent/ui/startMissionGui.gui. ent/ui/JoinServerGui.gui. ent/ui/endGameGui.gui. ent/ui/loadingGui.gui. ent/ui/optionsDlg.gui. ent/ui/remapDlaaui. ent/ser'i pts/el i ent. es.
ent/scri pts/mi ssionDoWllnload. C5. ent/scripts/serverConnection.cs.

Figure 4.1. Script console.

ent/seripts/playerList.es. ent/seripts/loadingGui.es. ent/seripts/optionsDlg.es. ent/seripts/ehatHud.es. ent/seri pts/messageHud. es. ent/seripts/playGui.cs.


ent/scripts/centerPrint.cs.

ent/seri pts/game. es. ent/seripts/default.bind.es. ent/eonfi g. es.

Binding server port to

IP

UDP initialized on port 0 Encine initialized .. 18 ,_ ~_~

eon.oIe~

To bring up the console, first, start the GPGT Lesson Kit, then hit the tilde key (N) in the upper left corner of the US-standard keyboard (next to the 1). The console will come right up (Figure 4.1).

Some of the sample scripts rely on the presence of datablocks. Thus, it will be necessary for you to first load the "3D Lessons" mission, before running them. As a reminder, the prewritten scripts will print a warning if you are not running the lessons mission:

Note: If you are not running the Lesson Sampler Mission, some examples may not work. Please click 'Start Mission .. ' from the GPGT Main Menu and select '3D Lessons' mission.

4.2.2 The Sample Script Console


In addition to prewritten scripts, you may at any time bring up a special application supplied with the Lesson Kit, the Sample Script Console. This application has an editor window where you can type (or paste) a script and then execute it at the push of a button. The sample console will execute yout script and show you the results. To start this application, just run the Lesson Kit and press the Sample Script Console button.
101

Part II

Engine Overview

4.3 TorqueScript Features


This scripting language has the following features.

Type-insensitive. In TorqueScript, variable types are converted as necessary and can be used interchangeably. TorqueScript provides several basic literal types, which are described later in this chapter.
/ /btOO ();
i f ( "1. 2"
}
==

1. 2 )

echo ( "Same, TorqueScript is type-insensitive H ); else ( echo( "Different, what!?H );


}

The code above will echo "Same, TorqueScript is type-insensitive." Case-insensitive. TorqueScript ignores case when interpreting variable and function names.
/ /btOl () ;

$a = "An example H; echo($a); echo ($A) ;

This code will echo "An example" twice. Statement termination. Statements are terminated with a semicolon as in many modern programming languages (C ++ , Java, JavaScript, etc.).
$a
=

"This is a statement H;

If you do not include the semicolon at the end of a TorqueScript statement, an error will be echoed to the console.

102

Full complement of operators. The complete list of TorqueScript's operators is given in the appendix. TorqueScript provides all the basic operators common to most programming languages, along with a few more advanced operators. Very complete set of supplemental string, math, and other functions. In addition to the built-in operators, TorqueScript comes with a very complete set of console functions that handle various string, math, and other operations. Table 4.1 lists some of the most commonly used functions. Please note that these functions are fully described later in the book (in Chapter 9, "Game-Setup Scripting," and Chapter 10, "Gameplay Scripting"), and a quick-reference with complete syntax, description, and sample usage is provided in electronic form with this guide.

Introduction to TorqueScript

Chapter 4

StrIngs
getSubStr strcmp strlen strstr Itrim stricmp stdwr strupr rtrim stripChars strpos trim strchr stripTrailingSpaces strreplace

Table 4.1

Commonly used Torque console functions.

Words, Records, fields


detag getFields getTag NextToken restWords firstWord getRecord getWord removeField setField getField getRecordCount getWordCount remove Record setRecord getFieldCount getRecords getWords removeWord setWord

Files
expandFileName filePath getFileCRC fileBase findFirstFile isFile fileExt findNextFile isWriteableFileName fileName getFileCount

Vec:tan
VeetorAdd VeetorLen VeetorSub VeetorCross VeetorNormalize VeetorDist VeetorOrthoBasis VeetorDot Veetor5cale

Matrices
MatrixCreate MatrixMulVeetor MatrixCreateFromEuler MatrixMulPoint MatrixMultiply

.
getRandom

Random Numbers

getRandomSeed

setRandomSeed

Math
mAbs mCeil mFloor mSin mSqrt mAcos mCos mLog mSolveCubic mTan mAsin mDegToRad mPow mSolveQuadratic mAtan mFloatLength mRadToDeg mSolveQuartic

103

--- - - - - - - - - -

Pan II

Engine Overview

Full complement of control structures. As with any robust language, TorqueScript provides the standard programming constructs: i f -thenelse, for, while, and switch.
/ /bt02 () ;
for($a=O;'Sa<S; echo(Sa};
}

$a++)

Functions. TorqueScript provides the ability to create functions with the optional ability to return values. Parameters are passed by value and by reference (see Section 4.3.5 for a detailed description and examples). Provides inheritance and polymorphism. TorqueScript allows you to inherit from engine objects and to subsequently extend or override object methods (see Section 4.3.6 for a detailed description and examples). Provides on-demand loading and unloading of functions. TorqueScript supports a very cool feature that allows you to load and unload functions as needed (see Section 4.3.8 for a detailed description and examples.) Provides namespaces. Like C++, TorqueScript supports the concept of namespaces. Namespaces are used to localize names and identifiers to avoid collisions. This means, for example, that you can have two different functions named do It () that exist in two separate namespaces, but which are used in the same code (see Section 4.3.9 for a detailed description and examples). ' Compiles and executes byte-code. As a bit of icing on the cake, the TorqueScript engine compiles scripts prior to executing them, giving a speed increase as well as providing a point at which errors in scripts can be reasonably found and diagnosed. This compilation is done just-in-time and results in p-code, which is not the same as compilation of C++ or C, which result in machine code. With this overview of TorqueScript's features, we can begin taking a detailed look at how TorqueScript works. We'll start by examining how TorqueScript handles the basics-variables, operators, and control statements. With these topics covered, we'll move on to cover in detail the more advanced features of TorqueScript.

4.3. 1 Variables
Variables come in two flavors in TorqueScript: local and global. Local variables are transient, meaning they are destroyed automatically when they go out of scope. And what is scope? Scope is a term used to refer to the block of code a variable is defined in. For example, if we have a function, and
104

Introduction to TorqueScript

Chapter 4

we declare a local variable inside of that function, the local variable will be destroyed as soon as the function is done processing. When this happens, we say the variable has "gone out of scope." So, local variables only exist in their local scope-the function they are defined in. A piece of code inside a different function is not able to see the local variable. Global variables, on the other hand, are permanent and exist throughout the entire program they are defined in. TorqueScript specifically marks local and global variables with special characters so that they are easy to tell apart. The syntax is as follows.
%local var = valuel; $global var = value2;
. .

~,n ~ience

compule'-

In TorqueScnpt, vanabIes do not need to be declared before you use them. If a piece of code attempts to evaluate a variable that was not previously created, TorqueScript will declare the variable automatically.
/ /bt03 () ;

classes, we are taught time and t' . th t I b I Ime again ago a variables are bad.

fort 0 ; %a < 5 ; %a++ ) echo( '\%a == 'f , %a );

Used to replace or circumvent a feature of the language you are programming in, they are bad. In languages like C, we have the ability to pass values between various levels of scope (either file or function) using pointers and references. As a scripting language, TorqueScript does not support these constructs: everything is passed by value. Instead, the global variable construct is supplied. Its purpose is to make data available across any and all scopes and contexts. In short, globals are not bad, and you should use them while writing scripts for Torque.

echo( "%a

==

"

%a );

Let's take a closer look at what this code does. On its first pass through the loop, the above code creates a new variable named %a. It must do so because %a has not yet been created when the loop tries to use it for the first time. 1. The echo () command inside the loop will print the value contained in the variable %a four times, echoing the values"", 1, 2, 3, 4, and 5 as the loop iterates and %a'S value increases. '''' is known as the null string. The first time through the above loop, %a is not yet defined, so TGE prints the null string. 2. After the loop finishes, %a will be echoed once again, by the line after the loop. That is a basic description of how local and global variables work in TorqueScript. However, we have not yet discussed the rules for naming variables. Variable names may contain any number of alphanumeric (a .. z, A .. Z, O. 9) characters, as well as the underscore C) character. However, the first character in a variable's name cannot be a number. You may end variable

lOS

Parr II

Engine Overview

names with a number, but if you do, you must be especially careful with array names. For further explanation, see "Arrays" in Section 4.3.2. Lastly, local and global variables can have the same name but contain different values. The following code will echo GPGT , GPGT 1, GPGT 2, and
GPGT 3.

/ /bt04 () ;
$a="GPGT";

for ( 0 ; %a < 4 echo ( $a ,


)

%a++ ) %a );

4.3.2 Data Types


TorqueScript implicitly supports several variable data types: numbers, strings, Booleans, arrays, and vectors. Each type is detailed below.
Numbers

Nothing mysterious here. TorqueScript handles your standard numeric types.


123 1. 234 l234e-3

OxcOOl

(integer) (floating point) (scientific notation) (hexadecimal)

Strings

This is for string data.


"abed" (string) 'abed' (tagged string)

Standard strings, in double quotes, behave as you would expect. Try these examples:
/ /btOS () ;

echo("Hello!"); echo("1.5" + "0.5");

Strings that appear in single quotes, 'abed', are treated specially by TorqueScript. These strings are called tagged strings, and they are special in
106

Introduction to TorqueScript

Chapter 4

that they contain string data but also have a special numeric tag associated with them. Tagged strings are used for sending string data across a network. The value of a tagged string is only sent once, regardless of how many times you actually attempt the sending. On subsequent sends, only the tag value is sent. Tagged values must be detagged when printing. Try the following examples.

I Ibt06 () ;
$a="This is a regular string"; $b='This is a tagged string'; echo("Regular string: " Sa); echo("Tagged string: " $b); echo("Detagged string: " , detag($b)

);

Now that we know how to name strings and assign them values (normal or tagged), let's take a look at the special string operators TorqueScript offers.
String Operators

There are four string operators.


@

TAB
SPC

NL

(concatenates two strings) (concatenation with tab) (concatenation with space) (newline)

You may find it odd that the last line shows a blank. This is because, although we have created the tagged string, it has not been transmitted to us. You can only detag a tagged string that has been passed to you.

To concatenate two strings means, simply, to stick them together. For example, if we concatenate the strings "Hi" and "there", we end up with a big string reading "Hithere". The basic syntax for these string operators is "stringl" op "string2".

I Ibt07 () ;
echo("Hi" echo("Hi" echo("Hi" echo("Hi" @ "there."); TAB "there."); II Note: TAB prints as SPC "there."); NL "there.");
in console

1\

Escape Sequences
There is one last area you need to know about in order to work with strings in TorqueScript: escape sequences.
\n

\r

(newline) (carriage return)

107

Parr II

Engine Overview

\t

(tab) (reset to default color) (push current color on color stack) (pop color from color stack) (two digit hex value ASCII code) (backs lash)

\cO .. \c9 (colorize subsequent text)

\cr \cp \co \xhh


\\

As in C, TorqueScript allows you to create a new line and tabs using the tried and true backslash character. These are called escape sequences. Escape sequences are used to indicate to the string-processing system that a special character is being read. Additionally, for data that is printed to the console and GUIs, you can colorize by using \cn, where n is a value between 0 and 9, representing a predefined set of colors.
/ /bt08 () ;

echo ("\c2ERROR! ! !\cO => OOpS!N);

The code above prints the line ERROR! !! => oops! with the first part in red and the second part in black. Going into detail about console output colorizing is beyond the scope of this chapter, but a little experimentation will go a long way toward helping you understand how the system works.

Booleans Like most programming languages, TorqueScript also supports Booleans. Boolean variables have only two values-true or false.
true (l ) false (0)

Again, as in many programming languages, the constant true evaluates to the number 1 in TorqueScript, and the constant false evaluates to the number O. Be careful, however, when comparing numeric values to the Boolean values true and false: only the values 1 and 0 will compare correctly. That is, in TorqueScript, the following statement will echo o.
echo( 100 == true);

J08

Numbers, strings, and Booleans: those are the basic data types in many programming languages, and TorqueScript supports them aI\. Next, we'll look at higher-level variable data types: arrays and vectors.

..

_-_ .... _ - - - - -

Introduction to TorqueScript

Chapter 4

Arrays
It is a common misconception that TorqueScript does not support multi-

dimensional arrays. This is not true, as the code below shows. The reason many people get confused about multidimensional 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) with commas or underscores.
$MyArray[N] $MyMultiArray[N,N] $MyMultiArrayM_N (one-dimensional array) (multidimensional array) (multidimensional array)

You must understand that in TGE all variables are eventually interpreted as strings. Furthermore, square brackets are removed, and commas are converted to underscores during the interpretation process. Underscores remain untouched. The real purpose of the brackets, commas, and underscores is that they function as "composers;" i.e., they help build the string from its various components. This is where the power of TorqueScript's arrays comes in to play. Consider the following code.
/ /bt09 () ;

$TestVarEDO = 10; $substring = EDO; echo($substring); II prints EDO echo($TestVar[$substring]); II prints 10

/f'Theu,e of ~esquare

What we have done here is use the square brackets to compose a variable name on the fly. There are a couple more things to know about TGE arrays.
1. $a and $a [0] are separate and distinct variables.
/ /btlO () ;

$a = 5; $a[O] = 6; echo("$a == echo("$a[O]

bracket operator to concatenate (compose) variable names on the fly is very useful in TorqueScript. but it should only be used when the usage does not obfuscate or otherwise render the script unreadable to others.

"

Sa) ;

", $a [0] ) ;

Run this code, and you will see that $a and $a [0] are distinct in the output. 2. $MyArrayO and $MyArray [0] are the same. It may be surprising, but TorqueScript allows you to access array indices without using the common bracket [] syntax.

We will in fact take advantage of this scripting feature in the guide, but I will explain my reasoning before doing so.

109

Part II

Engine Overview

/ /btll () ;

$MyArray[OJ = "slot OH; echo ( $MyArrayO ); $MyArray(lj = "slot IH; echo ( $MyArrayl ); $MyArray(O,Ol = "slot O,OH; echo ( $MyArrayO_O );

Now that we have a basic understanding of arrays, it's time to move on to vectors.

Vectors
This helpful data type is used throughout Torque.
"1.0 1.0 1.0 1.0 H (4 element vector)

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. There is a whole set of console operations for manipulating vectors. Also, vectors are taken as input for many game methods. In the following example, two vectors are added together using the console function VectorAdd () .
/ /bt12 () ;

$srcRay = "1.0 0.0 1.0 H ; $destRay = "1.0 6.0 H ; echo( VectorAdd( $srcRay , SdestRay ) );

Remember, TorqueScript does not support pointers or references. This means that all functions return values. In the above code, VectorAdd () is taking two vectors as inputs and returning a new vector as an output. We could alternately write the above code as follows.
$srcRay = "1.0 0.0 1.0 H ; SdestRay = "1.0 6.0 H ; SresultVec = VectorAdd( $srcRay , $destRay ); echo ( $resultVec );

Either of the above code snippets will output 1 6 1, which represents the vector < 1 , 6 . 1 > , the result of adding the vectors < 1.0 , 0.0 . 1.0> and < 1.0,6.0>.
110

Introduction to TorqueScript

Chapter 4

Bad Vector Math A common mistake among beginning Torque scripters is something like the following.
echo( "1 2 3" + "4 5 6" );

II Wrong!

The inexperienced scripter might expect the resultant output to be 5 7 9. Instead, the output will simply be 5. Why? Because the built-in operators only look at the first element of each vector. To correctly add (or otherwise manipulate) vectors, use the supplied vector functions (full syntax given later and in the appendices): VectorAdd, VectorCross, VectorDist, VectorDot, VectorLen, VectorNormalize, VectorOrthoBasis, VectorScale,

and VectorSub.

4.3.3 Operators
A complete listing of TorqueScript's operators can be found in the "TorqueScript" appendix. Refer to the appendix for detailed information. In general, operators in TorqueScript behave very similarly to operators in C-derived languages. However, there are two commonly encountered caveats when working with TorqueScript's operators. Syntactically, the ++ and -- operators are only post-fix operators (i.e., ++%a; which is a pre-fix operation, does not work; only %a ++, which is a post-fix operation, will work).
$a = 15; echo ( $a++ ); II Prints 16

String comparisons are of the following form:


$= ! $= (string equal to operator) (string not equal to operator)

..
In Torq ueScript, the equivalent of a for strings is the null string"". However. one has to be very careful when using the comparison operators. If you use the numeric operator == to compare zero (0) and a null string (""). you will get a return value of true.

echo( 0 == "" ); \\ Will print 1 to console.


However. if we use the string comparison operator $=. the same comparison will return false.

echo( 0 $= "" ); \\ Will print 0 to console.

111

Part II

Engine Overview

4.3.4 Control Statements


We'll now take a look at TorqueScript's control statements-branching and looping structures. TorqueScript supports all the common control statements.

Branching Structures
We compare three branching control statements.
if-then-else. The general structure of the if-then-else statement is

the following.
if(expression) statements; else { alternate statements;

Things to know: Brackets ({ }) are optional for single-line statements. (Many programmers find it more helpful to always use brackets.) Compound if-then-else-if-then-... statements are perfectly legal. (Many programmers find switch statements easier to read for large blocks of related i f cases). switch. The general structure of the switch statement is as follows:
switch (expression) case valueD: statements; break; case valuel: statements; break; case valueN: statements; break; default: statements;

Things to know: swi tch only (correctly) evaluates numerics. There is a special statement, switch$, for strings.
1 J2

Introduction to TorqueScript

Chapter 4

break statements are superfluous. TorqueScript will only execute matching cases. In TorqueScript, switch statements are no faster than ifthen-else statements. swi tch$. This statement behaves exactly like the switch statement with one important exception: it is only for strings. If you are a C or C++ coder, you may be used to taking advantage of the fall through in a swi tch statement. For example, in C. the following code will print the same message for values I, 2, and 3, but not for 4:

Looping Structures
We look at two looping control statements.
for. The general structure of the for loop is the following.

for(expressionO; expression1; expression2) statement(s);

Here is an example.

II C code switch ( val) ( case 1: case 2: case 3: printf( "Hello" ); case 4: printf( "World\n" );
In this sample, the cases 1 through 3 will print Hello and fall through case 4 to print World\n, in the end producing Hello World\n. You cannot do this in TorqueScript. In the following similarly structured example, nothing will print for cases I or 2, and we will get Hello for 3 and World for 4:

Ilbt13 () ;
for(%count = 0; %count < 5; %count++) echo(%count); (

As you can see, this is identical to the for loop in C++ .


while. The general structure of the while loop is the following.

while (expression) statement(s);

II TorqueScript
Here is an example. switch ( %val )
(

IIbtl4 () ;
%count = 0; while (%count < 5) echo(%count); %count++;

case 1: case 2: case 3: echo( "Hello" ); case 4: echo( "World" );


Remember: swi tch statements do not fall through in TorqueScript.

Again, this is very similar to the looping structure in C++ .

113

Part II

Engine Overview

As you can see, TorqueScript supports the standard set of control statements and handles them very similarly to familiar languages like c++ . In the next section, we continue our detailed examination of TorqueScript's standard features. We'll be looking at how TorqueScript handles functions (it's similar to C++, but more flexible).

4.3.5 Functions
Basic functions in TorqueScript are defined as follows.
funct ion func name ( [argO] , ... , [argn] ) statements; [return val;]

Here is an example.
//echoRepeat() ;
function echoRepeat (%echoString, %repeatCount) { for (%count = 0; %count < %repeatCount; %count++) echo(%echoString);

echoRepeat("hello!", 5);

The code above will echo the string hello! five times to the console. TorqueScript functions can take any number of arguments, each separated by commas. Functions may return a value by using the return statement, just as in C++ . Things to know:
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 its 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. However, TorqueScript provides packages (see Section 4.3.8), which can get around this problem. For functions defined in TorqueScript, if you call a function and pass fewer parameters than the function's definition specifies, the unpassed parameters will be given an empty string as their default value. Similarly, if you pass too many parameters, the extras will be dropped.
114

L_

Introduction to TorqueScript

Chapter 4

For functions defined in C++, if you call a function and pass fewer parameters than the function's definition specifies, the engine will complain, and the call will fail. The same goes for passing too many arguments. TorqueScript supports recursion, and it behaves just as in C++. The following example is a rewrite of the echoRepeat (l function we used above, but this version uses recursion instead of a for loop:
//echoRepeatRecurse();

function echoRepeatRecurse (%echoString, %repeatCountl if (%repeatCount > 0) ( echo(%echoString); echoRepeatRecurse(%echoString, %repeatCount--);

echoRepeatRecurse("hello!", 5);

4.3.6 Objects
Having covered the basics of the language, it's time to examine some of TorqueScript's more powerful details. In Torque, every item in a game is a SimObject, or a subclass of SimObject, and all of these objects can be accessed via script. For example, Player, WheeledVehicle, and Item are all accessible via script, although they are defined in C++ . Objects are created in TorqueScript using the following syntax (see Table 4.2).
%var = new ObjectType(Name : CopySource, argO, <datablock = Datablockldentifier;> (existing_fieldO (existing_fieldM (dynamic fieldO (dynamic fieldN
);

... , argn)

InitialValueO;] InitialValueM;] InitialValueO;] InitialValueN;]

Let's create a first object, with no initialization.


115

Part II

Engine Overview

,
Table 4.2
Definitions of object syntax elements.

SynIu EIem8It
%var
new

Dealptian
The variable where the object's handle will be stored. A keyword telling the engine to create an instance of the following ObjectType. Any class declared in the engine or in script that has been derived from SimObject or a subclass of SimObject. SimObject-derived objects are what we were calling "game world objects" earlier in this book. Any expression evaluating to a string, which will be used as the object's name. The name of an object that is previously defined somewhere in script. Existing field values will be copied from CopySource to the new object being created. Any dynamic fields defined in CopySource will also be defined in the new object, and their values will be copied. Note that if CopySource is of a different ObjectType than the object being created, only CopySource's dynamic fields will be copied. A comma-separated list of arguments to the class constructor (if it takes any). Many objects (those derived from GameBase, or subclasses of GameBase) require datablocks to initialize specific attributes of the new object. Datablocks are discussed in Section 4.3.10. In addition to initializing values with a datablock, you may also initialize existing class members (fields) here. Note that if you wish to modify a member of a C++-defined class, the member must have been exposed to the console.

ObjectType

Name (optional) CopySource (optional)

argO, ... , argn (optional) datablock

existing_ fieldN

dynamic_ fieldN

Lastly, you may create new fields (which will exist only in script) for your new object. These will show up as dynamic fields in the World Editor Inspector.

II

Create a SimObject

wlo

modifying any fields

$example_object

new SimObject() ;

Then create a second object using an initialization block.

II
};

Create a SimObject
=

wi

dynamic fields {

$example obj ect

new SimObj ect ()

a new field

"Hello world!";

116

Now let's create a datablock definition.

Introduction to TorqueScript

Chapter 4

II Create a StaticShape using a datablock datablock StaticShapeData( MyFirstDataBlock ) { shapeFile = "-/data/shapes/player/player.dts"; junkvar = "helloworld";
};

Then make an object that uses that definition.


new StaticShape()

dataBlock = "MyFirstDataBlock";
position = "0.0 0.0 0.0"; rotation = "1 0 0 0"; scale = "1 1 1";
};

In the Expert Tip on p. I 1 1, I mentioned that == and $= will return opposite results when the operands are a and "". Specifically, == will compare them as being equal and $= as not equal. This is important to remember when you check for the nonpresence of a dynamic field. The safest way to check for a non present field is the following. $y = new StaticShape() { dataBlock = "MyFirstDataBlock"; position = "0.0 0.0 0.0"; rotation = "1 0 0 0"; scale = "1 1 1";
};

if ( $y.myField == 0 ) { echo( "myField is not initialized


}

(not present}" );

This is the safest method of comparing, because it will continue to compare correctly for: field not initialized, field set to 0, field set to "". In other words, we can forget (or goof) later and preinitialize the field with a value equivalent to logical false and this code will still work. Of course, if you are really sharp and don't make mistakes, you can compare for un initialized fields as follows:

if( $y.myField $= "" ) { echo( "myField is not initialized


}

(not present)" );

Just remember that this comparison can fail if the field is later preinitialized to O.

117

----

--- ------

Part II

Engine Overview

Handles and Names


Every object in the game is identified and tracked by two parameters.
Handle. Every object is assigned a unique numeric 10 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.

Fields and Console Methods


TorqueScript object fields and console methods are the equivalents of C++ object members and methods. Objects instantiated via script may have data members (referred to as fields) and functional methods (referred to as console methods). In order to access an object's fields or console methods. one uses the standard dot notation, as in C++ .

II II II II

Note: The scripts below assume we have an object with a handle of 123, and a name of AName Directly access via handle

123. field_name = value; 123. command 'name () i

II II

Directly access via name

AName.field name = value; AName.command name();

II II II

Indirectly access via a variable containing either a name or a handle

%AVar.field_name = value; %AVar.command_name();

To get a picture of how this works for real, do the following. Start the GPGT Lesson Kit. Run one of the missions.
118

Introduction to TorqueScript

Chapter 4

Start the World Editor Inspector (press Fll). Switch to camera view (press ALT + C; on a Mac, you may need to select Camera View from the Camera menu) and select the character (hold down the right mouse button to look around; drag the mouse down until your player comes into view), Give the character a name, such as myGuy (type myGuy in the textbox next to the Apply button, and then click the Apply button). Open the console (press the
N

key).

Then, run the following sample.

I Ibt16 () ;
$player_name = "myGuy"; $player_id = $player_name.getID(); echo( $player name.position ); echo( $player_name.getID() ); echo( "myGuy".getID() ); echo( myGuy.getID() };

In the above example, getI D() returns the unique ID of the player object.

Dynamic Fields
In addition to normal fields (object fields exposed to script by the engine),

Dynamic fields, if created, are only visible by the server. When objects and datablocks are sent to the clients, only those fields that are exposed by the engine will be sent to the clients. This can be a little confusing until you understand that the engine does not have any context for dynamic fields and thus cannot send them across the network. If you find yourself needing to add new fields to existing objects or datablocks, and if you want them to be transmitted to clients, you may either write networking scripts to

TorqueScript allows you to create dynamic fields. Dynamic fields are associated ~~i~~~~~~~~ua~:y with a single instance of an object and can be added and removed at will. recompile the engine. Unfortunately, these 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 are both advanced an empty string, and no dynamic field will be created. However, if you try topics beyond the scope .of this book. For to write to an object field that doesn't exist, TorqueScript will automatically . h ~nOW'Justremember: create a matc h'109 dynamlc fi e ld for teo b'Ject, an d " aSSIgn It th e va Iue you Dynamic fields are indicated,
Ilbt17();
~
not networked!

II new_var will not be created because we are only II 'reading' it


echo( $player id.new_var );

II new_var2 will be created and initialized to "Hello"


$player id.new var2
=

"Hello";

echo( $player ld.new_var2 };


119

- - - .. _ - - - - - - - - - - - -

Part II

Engine Overview

4.3.7 Console Methods


In addition to supporting the creation of functions, TorqueScript allows you to create methods within the scope of the console (not requiring you to use C++ to add them). These are called console methods and are like functions, except that they are associated with a specific namespace (see Section 4.3.9, "Namespaces "). function classname: :method name (%this, statements; [return val;] [argO], ... , [argn]) {

Table 4.3.

Syntu Element
function classname: : func name %this

Delatptlon

.' .~:1 ,,:,'

Definitions of console method syntax elements.

A keyword telling TorqueScript we are defining a new function. The class type this function is supposed to work with. The name of the function we are creating. A variable that will contain the handle of the "calling object." Any number of additional arguments.

.. .

At a minimum, console methods require that you pass them an object hanle. You will often see the first argument named %this. People use %this .~ (contains object ID of object calling this method), but you can name it anything Y0U want. As with console functions, any number of additional When a console arguments can be specified separated by commas. Also, a console method method is called by the engine. or on may optionally return a value. 'Being associated with a namespace, console methods may be called on the handle or name of an object' d of . . " . the ID an mstance 0 f any 0 b"Ject m th e l l ' on an mstance means at namespace. a mg teo b~ect IS passe h automatically as the that the method is called using dot (.) notation in one of the following three first argument. ways:

~
~
or

II Aname is the object's name AName.methodName( [ arguments] );

II 123 is is the object's numeric 123.methodName( [ arguments] );

10

or
120

Introduction to TorqueScript

Chapter 4

II %var contains the object's name or ID %var.methodName( ( arguments] );

Here are some examples.


function Goober: :hi (%this) (. echo("Goober Hello n, %this);

Assuming our player handle is 1000, if we type:


1000.hi();

we get the following.


<input> (0): Unknown command hi. Object (1000) Player->ShapeBase->GameBase->SceneObject-> NetObject->SimObject

What has happened is that Torque has searched the entire hierarchy of Player and its parent classes, looking for a function called hi () defined in the context of one of those classes. Not finding one, it prints the above message. To demonstrate that Torque does search the class hierarchy of Player, try the followlng next.
function NetObject: :hi(%this) echo("NetObject Hello n, %this);
)

Typing:
lOOO.hi();

we get the following.


NetObject Hello 1000

Next, if we define:
function Player: :hi(%this) echo("Player Hello n, %this); Parent: :hi(%this);
)

111

Part II

Engine Overview

we can type:
1000.hi();

and get the following:


Player Hello 1000 NetObject Hello 1000

Defining subsequent console methods with the same name as prior console methods overrides the previous definition permanently, unless the redefinition is within a package (see Section 4.3.8, "Packages").

Do you see what happened? Torque found Player: : hi () first, but we also wanted to execute the previous definition of hi () . To do this, we used the Parent:: keyword. Of course, not finding a ShapeBase instance, which is Player's literal parent, Torque then searched up the hierarchy of the chain until it came to the NetObject version. Lastly, we can force Torque to call a specific instance as follows.
NetObject: :hi(1000);

gives us:
NetObject Hello 1000

and:
ShapeBase: :hi(1000);

also gives us:


NetObj~ct

Hello 1000

since there is no ShapeBase instance of hi () defined.

4.3.8 Packages
Packages provide dynamic function polymorphism in TorqueScript. In short, a function defined in a package will override the prior definition of a same named function when the package is activated. Packages have the following syntax.
package package_name() { function function_definitionO() [statemE?nts; ]

122

Introduction to TorqueScript

Chapter 4

function function definitionN() (statements;]


)
};

Things to know: The same function can be defined in multiple packages. Only functions can be packaged. Datablocks (see Section 4.3.10) cannot be packaged. Packages can be activated as follows.
Act~vatePackage(package

name);

Packages can be deactivated as foUows.


DeactivatePackage(package_name)i

Packages are managed on a stack. Each caU to ActivatePackage (package_ name) pushes its argument onto the stack, and it is always the topmost package that will be active. The easiest way to get a feel for packages is with an example. The following example is the most detailed we've looked at so far in this guide, but don't worry. It will make perfect sense when we're done. The following code has been provided with the GPCT Lesson Kit. Simply start the Lesson Kit, open the console (N), and follow the instructions below.
Iitest-packaqes( N ); II N

==

0,1, or 2

II Define an initial function: demo() II function demo() ( echo ("Demo definition on);

II Now define three packages, each implementing II a new instance of: demo() II package DemoPackagel ( function demo () ( echo("Demo definition In);
};

123

Part II

Engine Overview

package DemoPackage2 { function demo() { echo("Demo definition 2 H


};

);

package DemoPackage3 { function demo() { echo ("Demo definition 3 H ) ; echo("Prior demo definition was=>H); Parent: : demo () ;
} };

function test_packages(%test num) switch (%test_oum) { II Standard usage case 0: echo("----------------------------------------H); echo("A packaged function overrides a prior H); echo ("definition of the function, but allows H); echo("the new definition to be \'popped\' HI; echo("off the stack. H); echo("----------------------------------------H); demo () ; ActivatePackage(DemoPackagel) ; demo () ; Acti~atePackage(DemoPackage2) ; demo () ; DeactivatePackage(DemoPackage2); demo(); DeactivatePackage(DemoPackage1); demo ();

124

II Parents case 1: echo("----------------------------------------H); echo("The Parent for a packaged function iSH); echo("always the previously activated H); echo("packaged function. H); echo("----------------------------------------H); demo(); ActivatePackage(DemoPackage1); demo(); ActivatePackage(DemoPackage3);

Introduction to TorqueScript

crlapler 4

demo(); DeactivatePackage(DemoPackage3); DeactivatePackage(Demopackagel) ; echo("----------------------------------------") ; demo () ; ActivatePackage(DemoPackagel) ; demo(); ActivatePackage(DemoPackage2); demo() ; ActivatePackage(DemoPackage3); demo() ; Deactivatepackage(DemoPackage3); DeactivatePackage(DemoPackage2); DeactivatePackage(DemoPackagel)i

II Stacking oddities case 2: echo("----------------------------------------"); echo("Deactivating a \'tween\' package will"); echo("deactivate all packages \'stacked\' after"); echo (" it. ") ; echo("----------------------------------------"); demo() ; ActivatePackage(DemoPackagel); demo(); ActivatePackage(DemoPackage2); demo();
DeactivatePackage(DemoPackage~);

demo () ;

The standard way to use a package is to define a previously defined function inside the package, activate it as needed, and then deactivate it to go back to the default case for the function. To see this in action, type:
test_packages(O);.

TorqueScript provides a useful keyword, Parent::. By using the Parent:: keyword in a packaged function, we can execute the function that is being overridden. To see this in action, type: tes t _packages (1) ; .
It is important to understand that packages are, essentially, stacked atop each other. So. if you deactivate a package that was activated prior to other packages, you are in effect automatically deactivating all packages that were activated after it. To see this in action. type tes r_packages (2) i.

125

Part II

Engine Overview

Things to know: Packages may define new functions. Remember that when you deactivate a package, these functions become undefined. The Parent: : keyword is not recursive, i.e., Parent: : Parent: : fun () is illegal. Again, deactivating packages activated prior to other more recently activated packages deactivates all subsequently activated packages.

4.3.9 Namespaces
As previously mentioned, namespaces are provided in TorqueScript. The way they work is quite simple. First, all objects belong to a namespace. The namespace they belong to normally defaults to the same name as their object's class name. Players belong to the Player: : namespace, vehicles to the Vehicle:: namespace, etc.
II Player class namespace
Player: :

Also as previously mentioned, these namespaces provide separation of functionality, such that one may have functions with the same name but belonging to separate namespaces. To use one of these functions, either you must manually select the appropriate namespace, or in some cases this is done automatically for you. It is important to understand that the :: is not magical in any way. In fact, you can create functions with : : in their name. This doesn't mean they belong to a namespace. If the expression prefixing the : : is not a valid class/ you have done is create a unique name. namespace name, in effect,

all
{

II Not really namespaces


function Verl:: dolt ()
};

function Ver2:: dolt ()


};

Now, there is more to namespaces that you need to understand, but before we can address that, we need to learn about some other topics. So, we will revisit namespaces below in the appropriately titled Section 4.4, "Datablocks, Objects, and Namespaces Revisited."
126

Introduction to TorqueScript

Chapter 4

4.3.10 Datablocks
Of all the features in TorqueScript. datablocks are probably the most confus- . ing. To make things worse, they are central to the creation of most objects, which means you need to understand them relatively early.
"Data blocks are special objects that are used to transmit static data from server to client" (from engine.overview.txt).

This definition, although true, doesn't really tell us much. Some searching turns up additional definitions.
A datablock is an object that contains a set of characteristics which describe some other type of object" (from Joel Baxter, in the GarageGames forums).

Better, but this is still a little blurry on the purpose and use of datablocks.
"A databfock is a(n) object that can be declared either in C++ engine code. or in script code ... Each declared datablock can then be used as a "template" to create objects ... "(from liqUid Creations. Scripting Tutorial #2).

Very good. So, datablocks are templates, and we use them to create new objects with the attributes specified by the template. But how do we do this? WelL for the answer to that question, you'll have to wait. First, we need to discuss a few other important topics, and then we will revisit datablocks and give them the thorough coverage that they deserve.

The Object-Datablock Connection


The Torque novice may stumble along for a bit. playing with the examples that are provided with Torque. Eventually, the question arises, "Why are some objects made with datablocks and others not?" The answer, from a practical standpoint, is because otherwise you won't have a working game; specifically, any GameBase object, or subclass of GameBase, must be made with a data block, otherwise the script will not compile. To understand the philosophical reasons, we first observe that objects placed in the game world will fall into three broad categories. The object does not have much associated data andlor has few parameters. 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. The object has a lot of data or parameters, but it is OK for these datal 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
127

....

-._-------

Part"

Engine Overview

class of objects that could benefit from using datablocks. Why? How? Recall that, unlike normal objects, you are only allowed to have a single instance of anyone datablock. Furthermore, objects that are created from datablocks all share the same instance of that datablock. I can sense that some folks will be shaking their heads at this point, so let's look at Table 4.4, which should clarify the relationship. In the code snippets in Table 4.4, we make two physical zones, independent of each other. For each, we needed to specify all field values. We also made two StaticShapes. Each StaticShape has unique attributes, but they both share one datablock, which is used to describe the model they render and (as we'll see later) many more attributes. Now, let's examine the creation of non-datablock-created objects in detail, followed by datablock-created objects.
Table 4.4.

Comparison of non-datablock-based and datablock-based objects.

NonDatablock. . . . Object
Created directly from a C++ class in the console Contains fields May contain dynamic fields

Detablock-...... Object
Created directly from a C++ class in the console Contains fields May contain dynamic fields Requires an additional datablock field, which is assigned a previously defined datablock.

new PhysicalZone( firstPhysicalZone ) ( position = "371.851 322.83 218"; rotation = "1 0 0 0"; scale = "1 1 1"; velocityMod = "1"; gravityMod = "1"; appliedForce = "0 0 0"; polyhedron = "10 10 10 1 0 0 0 -1 0 0 0 1";
);

datablock StaticShapeData ( SimpleTargetO ) { category = "Targets"; shapeFile = "-/data/_./simpletarget.dts";


};

new StaticShape( firstTarget) ( dataBlock = "SimpleTargetO"; position = "360.17 325.775 219.906"; rotation = "1 0 0 0"; scale = "1 1 1";
);

new PhysicalZone( secondPhysicalZone ) { position = "671.851 125.83 218"; rotation = "1 0 0 0"; scale = "1 1 1"; velocityMod = "1"; gravityMod = "1"; appliedForce = "0 0 0"; polyhedron = "10 10 10 1 0 0 0 -1 0 0 0 1";
};

new StaticShape( secondTarget) { dataBlock = "SimpleTargetO"; position = "460.17 325.775 219.906"; rotation = "1 0 0 0"; scale = "1 1 1";
};

128

l_

Introduction to TorqueScript

Chapter 4

Creating Non-Datablock-Based Objects


I've provided the syntax for creating objects previously, but let's go ahead and create some variations of non-datablock objects to clarify the use of that syntax. We will use physical zones (p-zones) in all our examples. new Physical Zone () {
};

The above example creates a p-zone but doesn't specify a name or any of the parameters; therefore, it will take the default value provided by the C++ class's constructor. new Physical Zone (SpeedupZone) position = "0 0 0"; velocityMod = "2";
};

~ - - . - - - -.. . . .
~

The above example will create a p-zone named "SpeedupZone," positioned at < 0,0,0 > . This particular p-zone will multiply the player's velocity by two when the player enters the zone. new PhysicalZone(SpeedupZone2 position = "10 10 10";
};

SpeedupZone)

The above example will create a p-z,one named "SpeedupZone2," positioned at < 10,10,10 >. Aside from position, which has been redefined, it will inherit (by copying) all the fields in the previous datablock definition, SpeedupZone. However, the only field that will be different from the default is posi tion. Thus, the above p-zone creation statement, using inheritance, is equivalent to the following p-zone creation statement, not using inheritance. new PhysicalZone(SpeedupZone2) position = "10 10 10"; velocityMod = "2";
};

We will talk about physical Zones (p-zones) in Chapter 8, "Mission Objects," but for now, let me say that a pzone is a rectangular object that can be placed in the world to change physical characteristics in that zone. For example, a p-zone can be used to change the graVity and/or apply a force and/or modify an object's current velocity when the object passes into or through the area encapsulated by the

Creating Datablock-Sased Objects


Like non-datablock-created objects, when we create new instances of datablock-created objects, we can inherit (copy) fields from previously defined datablock-created objects.
129

Part II

Engine Overview

In essence, I'm saying that the syntax rules for object creation are universal. To assure you of this, I will show you two examples of datablock-created objects, one normal and one with inheritance.
new StaticShape(TestTarget) position = "0 0 0"; rotation = "1 0 0 0"; scale = "1 1 1"; dataBlock = "SimpleTargetO";
};

The above example creates a StaticShape named "TestTarget." It defines the position, rotation, and the scale. Additionally, it tells the engine to use datablock "SimpleTargetO" to initialize this object's datablock. Subsequently, this object will always be associated with the datablock "SimpleTargetO."
new StaticShape(TestTarget2: TestTarget) position = "0 10 0";
};

The above example creates another StaticShape. This one is named "TestTarget2." It inherits all the fields of TestTarget and overrides the position. The important thing to understand is that it shares datablock "SimpleTargetO" with the other instance of StaticShape, "TestTarget"; i.e, we have two instances of StaticShape that share one instance of the datablock "SimpleTargetO."

Declaring Datablocks
So far, we have clarified the connection between objects and datablocks. We have demonstrated that only a single instance of any datablock can be created and shared between any number of datablock-using objects. We have shown that the rules for creating objects are the same between those objects that use datablocks and those that do not. The only thing remaining for us to discuss is the declaration of datablocks. So, let's get to it. We declare datablocks similarly to the way we create objects. Datablock declaration syntax is as follows.
II In TorqueScript datablock DataBlockType (Name [: CopySource]) category = "CategoryName"; [datablock fieldO ValueO;]
[datablock_fieldM
130

ValueM; ]

Introduction to TorqueScript

Chapter 4

[dynamic fieldO [dynamic fieldN


};

ValueO; ] 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 in Table 4.5.
Syntax Element
datablock DataBlockType

DeIcrIptIon
A keyword telling the engine that this is a datablock object. Any datablock class declared in the engine that has been derived from GameBaseData or a subclass of GameBaseData. Any expression evaluating to a string, which will be used as the datablock's name. A previous datablock definition from which to inherit values.

Table 4.5.
Definitions of datablock declaration syntax elements.

Name

CopySource (optional)

category

A keyword that tells the engine where to place this object in the World Editor Creator Tree (see Chapter 3, "Torque Tools"). If the CategoryName does not exist in the tree, it will be created. You may initialize any and all existing fields in the datablock.

datablock fieldM

As with objects, you may add fields to the datablock that are
dynamic - fieldN not defined in the C++ version. Unlike objects, however, once defined, these values are static.

Now, let's do a few examples.


datablock StaticShapeData( MyTargets ) category = "Targets"; shapeFile = "-/data/shapes/targets/simpletargetO.dts";
};

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 will be drawn using the shape file located at /data/shapes/targets/simpletargetO. dts."
"N

datablock StaticShapeData(SimpleTargetO StartHidden = 1;


};

MyTargets)

131

Part 1/

Engine Overview

The above example creates declares a datablock of the type StaticShapeData named "SimpleTargetO" that inherits all the data from MyTargets. In addition, this declaration adds a new variable named "StartHidden" and sets it to 1.

Accessing Datablock Fields


Remember that datablocks are SimObjects, and we can access (read) their fields like any other object. However, changing a datablock field after the datablock is created and transmitted to all clients will have no effect on the client copies of the field(s) you have changed. You may only get useful results from changing datablock fields in a singleplayer game, because both the client and the server are sharing the same databIOCk. In all other scenarios, you should consider the datablock object to be a read-only object. /-You might be using this guide in a classroom setting, or in another Instructional venue. In that case, you might be using the console to load files containing datablocks, scripts, etc. Later, when using the Lesson Kit on your own, you might be surprised to find that the data blocks you were experimenting with are suddenly gone.

q)
,

The thing you must remember is that things that you do in the console are transient and rgenerally) do not affect the setup of the kit. Thus, if you qUit and reload, any files you brought into context by loading via the console are now not loaded.
So, to ensure that datablocks, scripts, etc. are loaded, you must modify the appropriate loader to bring them in.

For example, by default, the datablocks for a mission are loaded in the function onServerCrea ted (), located in the file game.cs under the current game's "seNer" subdirectory (e.g. examplejmyGamejseNerjgame.csj. Simply add an exec statement to the list of others you see there to load your datablock-containing file, and YOU'll be back in business.

Maze Runner Lesson #2 190% StepJ-Loading Datablocks


As we work on the Maze Runner game, we are going to need several datablocks and the accompanying scripts that were created for your use in this game and in your future creations. So, let's take the time now to get them loading. From the accompanying disk, do the following. 1. Copy the "\Base\Scripts\GPGTBase" directory into "\MazeRunner\ prototype\server\scripts". 2. Now, edit the function onServerCreated () in the file "\MazeRunner\ prototype\server\game.cs" to look like the following (bold lines are new or modified).
132

L.

Introduction to TorqueScript

Chapter 4

exec("./marker.cs") ; exec("./player.cs"); exec("./GPGTBase/loadGPGTBaseClasses.cs") ;

Figure 4.2
Creator directory.

In the above script, we are loading all of the GPGT base datablocks (classes) after all the other datablocks that FPS normally includes. We also add the data files that go with the datablocks. To test for a successful load, simply start the kit and load the "MazeRunner" mission. Then run the Creator tool, and you should have directories in the Creator as in Figure 4.2.

Mise
_ TeslSn.pes

t
_

BueStl.peBue B.seltem BlISe St.ti cS tl iI Pe

4.4 Datablocks~Objects, and Namespaces Revisited


For every SimObject in Torque, there is a namespace. Additionally, namespaces are chained. This means that, when the engine starts to search for something in the namespace, it begins at the entry point associated with the current object and seeks upward through all the parents' namespaces until it either finds what it is looking for or fails out. "Yes, yes," you say, "we've covered this, but how do we use this feature?" To answer that question, we'll look at some examples, starting with the simple stuff.

Bipeds

L
-

BilSePI.yer

bueVetlicles BoxC.r 8oxHove'r

4.4. 1 Object Namespace Hierarchies


When we wish to create a new method for the namespace of an object, we do something like the following.
function GameBase::Dolt(%this) { echo ("Calling StaticShape::Dolt() ==> on object" SPC %this);

The function Do I t is being declared in the GameBase namespace. This means that we can call this function on any object created from the GameBase class or its children. Here is an example.
//btl8(a) ;
%myTarget = new StaticShape( CoolTarget ) { position = "0 0 0"; dataBlock = "SimpleTargetl";
};

%myTarget.Dolt();
133

Part II

Engine Overview

Assuming the ID in %myTarget is 100, the above call would produce the following output in the console.
Calling StaticShape::DoIt() ==> on object 100

You'll notice a couple of things. 1. When we called Dort () , we did so without passing an argument explicitly, but when the console message printed, it did in fact get an argument with the value 100. 2. The one argument Dort () does take is named %this. Regarding number 1, because we used the handle to call the function [%myTarget. DoI t () ], the ID of this object gets passed implicitly to the function (see Expert Tip, page 120). That said, all of the following calls will produce the same result. Note that because this sample is in a function and because we create a new object each time we run it, the ID of the object will change for every run.
/ /btlS (b) ; %myTarget.DoIt(); StaticShape: :DoIt(%myTarget); CoolTarget.DoIt(); "CoolTarget".DoIt(); 100. Dort () "100" . Dort () StaticShape:':DoIt(lOO);

As you can see, there are various ways to call the same function, all of which are useful in different scenarios. Please note that, in the cases where we use the name of the object, the name will be passed as the ID. Torque automatically does look-ups for names; thus, in most cases, names can be used interchangeably with IDs, as long as the names are unique. We've discussed the most basic use of namespaces. Now let's talk about datablock namespaces.

4.4.2 Simple Datablock Namespaces


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 method for the ItemData namespace, we can do something like the following.
134

Introduction to TorqueScript

Chapter 4

function ItemData:: GetFields ( %ItemDbID ) ( echo (nCalling ItemData: :GetFields () ==> on object" SPC %ItemDbID); echo (n category =>" SPC %ItemDbID.category); echo (n shapeFile :>" SPC %ItemDbID.shapeFile); echo (n mass =>" SPC %ItemDbID.mass); echo (n elasticity =>" SPC %ItemDbID.elasticity); echo (n friction =>" SPC %ItemDbID.friction); echo (" pickUpName =>" SPC %ItemDbID.pickUpName);

The function GetFields is being declared in the ItemData namespace. BaseItem is an instance of ItemData.
II from GPGT Lesson Kit = Item.cs (edited) datablock ItemData ( BaseItem ) { category = "TestShapes"; shapeFile = "-/data/GPGTBase/shapes/markers/dummy.dts"; mass = 10.0; elasticity = 0.05; friction = 0.7; pickUpName = nDefault Item"; l;

We could call our new function on BaseItem as follows.


/ /bt19 () ; ==>Baseltem.GetFields(); Calling ItemData: :GetFields () ==> on object Baseltem => TestShapes category shapeFile => gpgt/data/GPGTBase/shapes/markers/dummy.dts => 10 mass elasticity => 0.0498534 friction => 0.698925 pickUpName => Default Item

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 onCo11ision (), onAdd (), create (), etc., are not called on instances of objects. They are called on the datablocks of instances of objects that use data blocks. This is crucial, because we can do some very special things with datablocks and their namespaces.

us

Part II

Engine Overview

When the engine calls a method that is scoped to a datablock, the engine will always pass the datablock 10 as the first argument and the object 10 as the second argument.

function CrossbowAmmo::doIt( %OB, %Obj ) echo( "DB: " , %OB , "Obj: " , %Obj );
}

Also, we can manually call console methods scoped to data blocks in three ways.

$ammo = new Item ( ) { datablock = CrossbowAmmo;


}

II 1 - Direct call, must pass Datablock and Obj ID II Output: DB: 123 Obj: 456.
Crossbowammo: :doit( Crossbowammo , $ammo ); II 2 - Call on datablock name, must pass Obj ID

II

Output: DB: Crossbowammo Obj: 456.

Crossbowammo.doit( $ammo ); II 3 - CalIon stored datablock ID, must pass Obj ID II Output: 123 Obj: 456. $OBIO= $ammo.getOatablock(); $OBIO.doit( $ammo );

4.4.3 Inserting Datablock Namespaces (ClassNamej


Datablocks provide a hook with which to manipulate the namespace calling sequence. The hook is the className field. It works as follows.
datablock ItemOata(CrossbowAmmo)

className
};

"Anuno";

136

Introduction to TorqueScript

Chaprer 4

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 ~ etc. We could define two functions as follows.
function Ammo: :onPickup(%AmmoDB, %AmmoOBJ, %Picker, %Amount) { echo ("Calling Ammo: : onPickup () ==> on arruno DB" SPC %AmmoDB); %AmmoDB.DoIt();

function Ammo: :Dolt(%AmmoDB) ( echo ("Calling Arruno: : DoIt () ==> on ammo DB" SPC %ArrunoDB);

Then we could collide with an ammo item. This would then automatically call the onPickup () callback, and we would expect to see the following message (assuming the datablock ID is 66).
Calling Ammo: :onPickup () ==> on ammo DB 66 Calling Ammo: :Dolt () ==> on ammo DB 66

This powerful feature allows us to insert a special namespace that we can use for several different datablocks. In other words, we could define two more ItemData datablocks as follows.
datablock ItemData(FlamingCrossbowAmmo)
className = "Ammo";
};

You cannot legally specify a className that is the same as the current datablock name.

datablock ItemData(ExplodingCrossbowAmmo)
className = "Ammo";
};

We would then have the structure shown in Figure 4.3. Later in our code, objects derived from the three different datablocks CrossbowAmmo, FlamingCrossbowAmmo, and ExplodingCrossbowAmmo can all use the same onPickup () and Dort () functions as declared in the Arruno: : namespace. This cuts way down on the amount of code we need to write.
137

Part II

Engine Overview

ItemData

Figure 4.3. Sharing namespace with className keyword.

Ammo

FlamingCrossbowAmmo

CrossbowAmmo

ExplodingCrossbowAmmo

4.4.4 Namespace Inheritance?


You might wonder at some time whether namespace hierarchies can be inherited. The answer is no. If we do this:
datablock ItemData(CrossbowAmmo) II
) ;

datablock ItemData(FlamingCrossbowAmmo II
) ;

CrossbowAmmo)

the namespace calling sequence for CrossbowAmmo will be CrossbowAmmo ItemData ~ etc., and for FlamingCrowssbowAmmo it will be FlamingCrossbowAmmo ~ Item Data ~ etc. (see Figures 4.4 and 4.5). If we want FlamingCrossbowAmmo to use the CrossbowAmmo namespace, we have to do the following.
~

datablock ItemData(CrossbowAmmo) II
) ;

Figure 4.4. CrossbowAmmo namespace not inherited.


ItemData

Figure 4.5. CrossbowAmmo namespace added.


ItemData

Figure 4.6.

className inherited, then overridden.


ItemData

CrossbowAmmo CrossbowAmmo FlamingCrossbowAmmo

Ammo

SomeOtherAmmo

t
FlamingCrossbowAmmo

CrossbowAmmo

138

FlamingCrossbowAmmo

"

Introduction to TorqueScript

Chaprer 4

datablock ItemData(FlamingCrossbowAmmo)

II ... className:: "CrossbowAD!mo"; II


);

Please note that if you do define a className field in a datablock, subsequent children datablocks will copy that value to their own className field unless it is overridden in the child's definition, as follows (see Figure 4.6).
datablock ItemData(CrossbowAmmo)

II ... className II
};

"Ammo";

datablock ItemData(FlamingCrossbowAmmo

CrossbowAmmo)

II ... className = "someOtherAmmo"; II


);

4.4.5 A Parting Reminder IDatablock versus Object Namespaces J


Before closing this chapter, I want to take a moment to remind you that, when you create new objects that use datablocks, the majority of the functions that are called by the engine are called on the databtock of the object, not the object itself. I've seen questions time and again in the forums that have their root in confusion about this topic. $0, save yourself a headache later and make sure you get this idea down firmly!

4.4.6 Helping Yourself


The console supplies a few helpful functions and method that can be used to get extra information about objects and the functions that are available to you.

dump () and tree ( )


If you have an object or a datablock and want to know what fields it has and what methods are scoped to it, type the following in the console (assuming the ID of the object or datablock is stored in $Obj).
$Obj.dump(); J39

Part II

Engine Overview

To see a listing (inspector) of all the objects that are currently loaded, type:
tree ()
j

This will bring up a special debugging tool that functions much like the Inspector.

4.5 Summary
Jt has been a long chapter, but you made it through. It is doubtful that anyone could fully absorb all of the infonnation presented in Ihis chapter after just one reading. So, while you work with Torque and encounter problems, use this chapter as a resource, revisiting sections that were not clear on the initial pass. To recap, and as a reference, here is what we covered.
First, we talked about what game-engine scripting languages are and why they're useful. We talked about the features a good scripting language should have and discovered that TorqueScript has all of them. With the introductory analysis out of the way, we dug into the meat of TorqueScript, studying each of the features of the language in detail. We talked about TorqueScript's variables at length-studying variable naming and scoping and the numeric, string, Boolean, array, and vector datatypes. Continuing with the detailed overview of the language, we looked at TorqueScript's operators, control statements, and functions. We then covered how to use objects in TorqueScript, looking at their handles and names, fields and commands, dynamic fields, and console methods. . Next, we quickly introduced packages, namespaces, and datablocks. We covered these sections briefly at first, needing to understand more about the interaction between the engine and the script console before we could go into further detail. After a detailed look at the engine-console interface mechanisms in Torque, we came back to datablocks, objects, and namespaces. For datablocks in particular, we found out how datablocks and objects are related to each other and found out how to declare datablocks. Studying namespaces, we learned that they can be tricky but discovered object namespace hierarchies, learned how to create simple datablock namespaces, and then became datablock namespace masters. We ended with a quick discussion of how to help yourself, covering a few more features of Torque that enable debugging.

140

Game Elements

Part III

-------------------

-----

Chapter 5 Torque Core Classes

All right! We've finished going through the engine overview and now it is time to jump into the guts of some important TGE classes. As was previously mentioned, at its core, TGE is an event-driven simulator. This simulator has defined a hierarchy of classes, based on the aptly named class SimObject. In this chapter, we will be inspecting the SimObject class, and some of the other core classes. Each of these core classes is a major branch in the SimObject hierarchy, off of which many other classes hang. We will discuss those (hanging) classes in the subsequent chapters. The following specific classes are covered in this chapter. SimObject. The root class for all other SimObjects. Understanding this class is fundamental to understanding how TGE classes interact. SimDataBlock. The base datablock class. We have already discussed this class, but we will revisit it to ensure that we are ready to move on to subsequent classes. SceneObject. The base class for almost all scene-placeable objects. GameBase and GameBaseData. These (otherwise minor) classes represent the first object-datablock pairing and act as parents to all subsequent classes with this kind of relationship. Please note that the dotted line in Figure 5.1 indicates that there is a class between the two connected classes that we are not discussing.

5.1 SimObject
5. 1. 1 SimObject Features
SimObject has the following features. Identification Object name (alphanumeric) Object ID (numeric) Group ownership Saving Save to file
143

-----------

Part III

Game Elements

Self-Documentation Object information dumping Classification Class Name Object l'ype (a bitmask) Destruction

5. 1.2 SimObject Description


As one would expect of a root class, this class forms the basis for the organization and usage of all subsequent classes. Its major responsibility is to track standard data about an object, such as the object's name, ID, what SimGroup it belongs to (if any), and what type of object it is. It also handles saving itself to file, deleting itself, scheduling actions on itself, and dumping a list of console methods and fields associated with itself.

5. 1.3 Name and ID, Please ...


An object will always have a unique ID and may optionally have an alphanumeric name. Furthermore, objects may be referenced by name or by ID. ID referencing is the preferred method because it is unambiguous. Multiple objects may share the same name, and references by name always retrieve the first object found to have the specified name. Examine the following code to see how using names instead of IDs can lead to confusion.
/ /tsOO () ;

II The following code demonstrates the issue that occurs II when giving multiple objects the same name.
%objO = new SimObject( test); II a SimObject named 'test' %isSame = ( %objO == test.getID() ); echo( "%objO == test.getID() => ", %isSame ); %objl = new SimObject( test ); %isSame = ( %objO == test.getID() ); echo( "%objO == test.getID() => ", %isSame ); %isSame = ( %objl == test.getID() ); echo( "%objl == test.getID() => ", %isSame );
144

Torque Core Classes

Chaprer 5

The following results for the above code show that the engine finds the last instance of a named object when searching by name.
%objO %objO %objl test.getID() => 1 test.getID() => 0 test.getID() => 1

5. 1.4 Class Name and Type Information


Every object is created from a class, and every class has a unique class name. This information can be retrieved via script and is useful for categorizing objects. Additionally, every object stores information about its inheritance structure, that is, its type.
/ /tsOl () ;
%obj = new Player( SuperGuy ) datablock = BasePlayer;
);

II will echo ==> Player echo ( %obj. getClassName () II will echo ==> SuperGuy echo( %obj.getName() );

);

II will echo ==> PlayerData echo ( %obj.getDatablock() .getClassName() II will echo ==> BasePlayer echo ( %obj.getDatablock() .getName()

);

);

What about type information? In TCE. each mission-placeable object derived from SimObject has the abifity to store and retrieve a mask value that shows the object's inherHance structure. For example. a WheeledVehicle, being far down the chain, will have bits for WheeledVehicJe, Vehicle. ShapeBase, and CameBase set. Why are there no bits for the hierarchy between SceneObjeet and SimObject? It is implied. You cannot place an object that is not a SceneObject, and SceneObject is a child of SimObject. The actual bit values are declared in objectlypes.h, but to make scripting simpler, they are exposed as named variables (done in main.cc). The following types are currently defined.
145

Part III

Game Elements

$TypeMasks: :StatiCObjectType $TypeMasks::TerrainObjectType $TypeMasks: :WaterObjectType $TypeMasks:: MarkerObjectType $TypeMasks: :ShapeBaseObjectType $TypeMasks: :StatiCShapeObjedType $TypeMasks::ItemObjedType $TypeMasks: :VehicieBlockerObjectType $TypeMasks:: ExplosionObjectType

$TypeMasks: :EnvironmentObjectType $TypeMasks: :InteriorObjectType $TypeMasks: :TriggerObjectType $TypeMasks: :GameBaseObjectType $TypeMasks: :CameraObjectType $TypeMasks: :PlayerObjedType $TypeMasks: :VehicleObjedType $TypeMasks: :ProjectileObjectType

To check the type of an object, we use the getType () method and use bitwise operators to compare return value against the above masks.
/ /ts02 () ;
%obj = new Vehicle() { datablock = BoxCar;
};

if( %obj.getType() & $TypeMasks::VehicleObjectType ) ( echo ("Yup, it's a vehicle ... ");
}

else ( echo("Sorry, but that is not a vehicle ... ");

%obj.delete(); %obj = new Player() { datablock = BasePlayer;


};

if( %obj.getType(} & $TypeMasks::VehicleObjectType ) ( echo ("Yup, it's a vehicle ... ");
}

else ( echo("Sorry, but that is not a vehicle . .. n);

146

%obj.delete();

Torque Core Classes

01apter 5

Object type masks are used in a variety of other ways, so you might want to bookmark this page.

S. 1.5 Saving and Deleting


Removing an object from the world is as simple as telling that object to delete itself.
%obj.delete(); II Ahh! I kill myself ... ;)

Objects are able to clean up their own fields and otherwise cleanly remove themselves from the world. However, as a general rule, objects do not automatically delete other objects that they may logically own. Fortunately, there are callbacks and SimGroups to help us out here. These are both topics for a later chapter, so for now just read on.

5. 1.6 Dumping Information


At the end of Chapter 4, we introduced the dump () function. This function is introduced by the SimObject and can therefore be called by any child of this class. The dump () function prints all the following information associated with an object to the console: Engine-registered console methods. All methods registered as being associated with the dumping object's class or one of its parents. Console-registered console methods. All scripted methods associated with the dumping object's class or one of its parents. Member (nondynamic) fields. Fields permanently exposed for this class (by the engine). Tagged (dynamic) fields (for this object). Fields created in the dumping object during or subsequent to its creation.
~.

You will probably use this function quite a bit, so let's give it a quick try to get you started.
/ /ts03 () ;
%obj = new SimObject(); %obj.dump() ;

s. 1.7

Group Membership
147

We have not discussed them yet, but Torque has two (base) container classes, SimSet and SimGroup. The latter has a special property, wherein any object

Parr III

Game Elements

stored in a SimGroup is guaranteed to only be stored in that SimGroup and no other SimGroup. SimSets offer no such guarantee. We will discuss this in some detail later, but for now, let's just remember that an object can only be in anyone SimGroup (container) at anyone time. Given this restriction, it is possible that we might want to know what SimGroup our object is in. If we have the name of an object, or if we have its 10, we can simply "ask" the object what container it is in.
%obj ,getGroup ()

The above code will return either -1, or a nonnegative numeric value. If the value -1 is returned, the object is not stored in a group; otherwise, the numeric ID that is returned is the ID of the SimGroup container that currently "owns" this object.

5. 1.8 SimObject Methods


SimObjects have several useful built-in methods, described in Table 5,1.
Table 5. r
Summary of SimObject methods,

MeIhod
delete () dump() getClassName () getGroup () getID () getName () getType () save (fileName) schedule () set.Name(newName) Delete this object.

~.,.

"

.',

'\',.

Dump information about this object to the script console. Return this object's C++ class name. Get the 1D of the group this object is stored in, or else retum -1. Get this object's numeric ID. Get this object's alphanumeric name. Get this object's type bitmask. Save this object to the file specified in fileName. Described later in Chapter 9, "Gameplay Scripting." Change the name of this object to value in newName,

5.2 SimDataBlock

5.2. 1 SimDataBlock Features


SimDataBloek features include the following. Initialization Seoping
148

L.

Torque Core Classes

Chapter 5

SimDataBlock is the root class of all datablock classes. We have talked about datablocks quite extensively already. However, I would like to quickly revisit a few important datablock features, ending with a lead-in to the topic of callbacks.

5.2.2 Datablock-Object Pairing


Remember that almost all GameBase-derived objects are paired with a likenamed SimDataBlock-derived class. Table 5.2 shows the current complete (alphabetic) list of pairings.
DetabIock CIa8I
CameraData DebrisData ExplosionData FlyingVehicieData fxLightData GameBaseData HoverVehicieData ItemData LightningData MissionMarkerData ParticieData ParticleEmitterData ParticleEmitterNodeData

Object CI8II
Camera Debris Explosion FlyingVehicie fxLight GameBase HoverVehicie Item Lightning MissionMarker - none ParticleEmitter ParticleEmitterNode

DetBbIock aPathCameraData PathedlnteriorData PlayerData PrecipitationData ProjectileData ShapeBaseData ShapeBaselmageData SimDataBlock SplashData StatiCShapeData TriggerData Vehicle Data WheeledVehicieData

Object

a..

Table 5.2
Datablock-object pairings.

PathCamera Pathedlnterior Player Precipitation Projectile ShapeBase - none - none Splash StatiCShape Trigger Vehicle WheeledVehicie

5.2.3 Namespace Rules


Chaining and Building
All SimObject-derived classes have a namespace calling chain. SimDataBlockderived classes add to the namespace chain in two ways. First, they add the name of the datablock to the chain. Second, they have a mechanism for adding an additional namespace by using the className keyword.

The className keyword should not be confused with a c1ass's name (from SimObject). It serves a different purpose and does not affect the output of

getClassName () .

149

Part Iii

Game Elements

datablock PlayerData( myPlayerDatablock ) I className = myPlayerDataBlockParent;


};

The above datablock produces a namespace chain like the following. myPlayerDataBlock
~

myPJayerDataBlockParent

PlayerData

...

The class name above could have been any string not already in the chain. I chose "myPlayerDataBlockParent" so that the hierarchy would be clear, but I could just as well have called it "Freddie" and gotten the following chain.
myPlayerDataBl~ck ~

Freddie

PlayerData

...

We can later use this datablock to build an instance of the Player class as follows.
%myPlayer = new Player( TorqueDude ) ( datablock = "myPlayerDatablock";
) ;

Noninheritable
In standard TGE, namespaces are noninheritable in the console. This means that, if we create a new datablock myPlayerDatablock2 and inherit (copy) the fields from myPlayerDatablock, as follows:
datablock PlayerData( myPlayerDatablock ) { className = myPlayerDataBlockParent;
};

datablock PlayerData( myPlayerDatablock2 : myPlayerDatablock ) ( II Copies: className = myPlayerDataBlockParent; II from myPlayerDatablock

II
) ;

the new datablock will not have the myPlayerDatablock name in its names pace. Instead, its names pace will look like the following. myPlayerDataBlockl
~

myPlayerDataBlockParem

PlayerData

...

It does inherit the added namespace specified by className, but the parent

datablock namespace is lost.


150

Torque Core Classes

Chapter 5

Scoping
As a direct result of this namespace business and due to the way TGE is designed, we can create console methods (functions scoped to a console class) as follows. function rnyPlayerDataBlockParent: :Dolt( %theDB , %optionalArgs, II
};

... ) {

As can be seen from this example, the function Do I t () has been scoped to the myPlayerDataBlockParent namespace, using the namespace resolution operator: :. Thus, we now refer to Dort () as a console method (or meth~d' for short). ~ This (noncallback) method takes a minimum of one argument and may ~ R---b---""" emem er have as m~ny additional arguments as. we deem neces.sary. The req.uired that when the ., argument IS often named %this, but m our example It has been gIven the engine automatically more meaningful name %theDB. Why? Well, as you probably recall, when the calls a method on a method is called (properly), the engine will pass, as the first argument, the datablock, the method ID of the datablock associated with the object that caused the method to be is referred to as a fi d callback. All callbacks re . scoped to data blocks receive two default We can certainly call methods directly, or on a datablock name/ID if we want to, but most of these methods are called by the engine as the result of arguments, not the some event. Recall (from earlier) that methods called as the result of some single argument a event are called callbacks. regUlar datablock method gets. The first argument is the ID of the datablock, and 5.3 SceneObject the second argument is the I D of the object 5.3. 1 SceneObject Features that the callback is being called for. SceneObject features include the following: Torque implements a great number of Transforms callbacks. For the Position most part. we do not Rotation discuss them in this guide. However, those Scale . callbacks that will Transform affect our efforts to Forward vector write a single-player game will be discussed. Collision Detection

Volumes Object box World box

151

- - - - ----- - - -

Part /II

Game Elements

5.3.2 SceneObject Description


A SceneObject is an object capable of appearing in a scene. It can be rendered. It may be moved, rotated, and scaled. It may be collided with, and it takes up space within the game world.

5.3.3 Position, Rotation, and Scale


All SceneObject-derived objects provide three basic fields. Position. A three-element floating-point field describing the object's initial placement position in the world. Rotation. A four-element floating-point field specifying the shape's rotation as a quaternion.
Scale. A three-element floating-point field specifying the x-y-z scaling factors for a shape.
%obj = new Player ( Blockman ) { position = "0 0 0"; II start at world-zero rotation "1 0 0 0"; II quaternion giving zero rotation scale = "1 1 2.5"; II 2.5 times as tall as standard version
};

These fields are used during the creation of an object to set the object's initial position, rotation, and scale. Not a big surprise. The real surprise comes later, if you try to modify these fields directly. If you are using the Inspector to make these changes, they will always take effect, but if you are using scripts, your results will vary. This is because some objects regularly mark these fields as dirty and retransmit them to the client ghosts while other classes never mark them as dirty so the changes go unheeded. This is not a bug. You are not supposed to modify these variables directly, but rather use access methods. These access methods are described in Table 5.3.
Table 5.3.

M
getPosition () getScale () setSca1e(newSca1e)
Returns the object's current position. Returns the object's current scale. Sets the object's scale to newScale.

Access methods.

The access methods described might not seem like enough. This is because the makers of the engine have combined the position and orientation information into a composite vector called a transform.
152

f----

Torque Core Classes

Chapter 5

5.3.4 The Transform


An object's transform is a composite vector containing both position and rotation information. "posX poxY posZ rotX rotY rotZ rotTheta" The access methods used to get and set transform are defined in Table 5.4.
Method
getTransform () setTransform(newTransform)

De8crlption
Returns the object's transform vector. Sets the object's transform to newTransform and marks this information as dirty so that all ghosts are updated.

Table 5.4.
Access methods to get and set transform.

In the following example, we want to translate an object by 10 meters along the world x-axis. Using a couple of string functions we extract the position and rotation vectors as well as the rotation theta about the rotation v~etor. Then, we add" 10 0 0" to the position vector. After re-assembling the .~ vector, we translate the object's position by passing in the new transform For some objects, toasetTransforrn() call. Simple. Itlsactually possible to modify the object's position %rnyTransforrn = %obj.getTransform(); field and then to %rnyPosition = getWords( %rnyTransform, 0 , 2 ); rescale the object to its %rnyRotationVec = getWords( %myTransforrn, 3 , 5 ); current scale. This will %rnyRotationTheta = getWord( %rnyTransforrn, 6 ); cause the scale and position to be marked II Move shape +10 in X direction as dirty. allowing us %rnyNewPosition = vectorAdd( %rnyPosition , "10 0 0"); to move an object that might otherwise %obj.setTransform( %myNewPosition ); ignore even the transform update. One The methods getWords () , getWord ( ) , and vectorAdd () will be example of this is the described in Chapter 9, "Gameplay Scripting". ParticleEmitterNode object which we will discuss in Chapter 8, 5.3.5 Collision Detection "Mission ObJects." This class introduces the ability to interact with the world via collisions. I don't generally SceneObjects can collide with other objects and can be collided with. Collision encourage people to detection and response is a complex and advanced topic, which we won't be use hacks, and this is a hack. but this little able to cover in detail; however, as with callbacks, those collisions that we tip is really quite useful. need to discuss will be discussed briefly prior to writing any of the required

~.

code for our game.


153

-----_._-

..

Part III

Game Elements

5.3.6 Object Boxes and World Boxes


Every scene object has an object box and a world box. These two boxes serve unique purposes. The object box is an object-oriented box whose coordinates are relative to the object's centroid. The extents of this box are the non-scaled < x y z> bounds of the shape. The purpose of this box is to provide an unscaled basis for bounding and scaling calculations done in script. The world box is a (world) axis-aligned bounding box. The coordinates of the world box are real-world and do not need to be translated or scaled. This box tells us (approximately) how much space a shape is taking up in the world and where. It is useful for placement calculations and obstacleavoidance checks, among its many other uses. We can get these useful bits of data with these methods in Table 5.5.
Table 5.5. Methods for getting object- and world-box data.

Method
getObjectBox()

DescrIption
Returns the six-element floating-point vector representing this object's object box. The first three values represent the lower left corner, and the latter three values represent the upper right corner. Returns the six-element floating-point vector representing this object's world box. The first three values represent the lower left corner, and the latter three values represent the upper right corner. Returns the three-element floating-point vector representing the center of this object's world box.

getWorldBox ()

getWorldBoxCenter()

5.3.7 The Forward Vector


A frequently seen beginner's mistake is to assume that the forward vector and the rotation vector from the object's transform are the same. They are not the same, and you should not treat them as such. Each of them has a separate purpose and use.

It is frequently important to know which direction a shape is facing. We can

retrieve this information by asking for the object's forward vector. The forward vector is a normalized vector representing the orientation of the shape's y-axis relative to the world axes (in Torque, + z is up, + Y is forward, and +x is left). The getForwardVector () method provides a quick means of retrieving this value.
%playerFacing = %player.getForwardVector(); echo("Player's forward vector is:" SPC %playerFacing );

154

Torque Core Classes

Chaprer 5

5.4 GameBase and GameBaseData 5.4. 1 GameBase Features


GameBase features include the following:

Ticking Datablocks

5.4.2 The Foundation Game Classes


All GameBase objects are built using datablocks; thus, it is not surprising that the majority of what this class does is focus on datablock functions. It is also the first object to experience ticks. This is just something to put under your hat for now, but it is important to know. Only GameBase objects and their children are ticked. Datablocks are used to store static data as well as to scope many important methods and callbacks. In order to allow us to access the data these objects contain, we first require a method of obtaining an object's datablock. Of course, Torque supplies us a method to do this. Given that we know the object for which we want the datablock, we can get that object's datablock as follows.
%myDataBlock = %obj.getDatablock()i

Additionally, we may change an object's datablock at any time with a call like the following.
%obj.setDatablock( Blockman2
)i

What exactly does changing the datablock do for us though? Well, the obvious thing it does is change the source of subsequent datablock data retrievals; i.e., datablock values retrieved (by us and by the engine) in the future will get their content from the newly specified datablock. This is pretty cool, but there is another more important (and more subtle) thing that this does. By changing the datablock of an object, we are effectively changing that object's namespace (the console method calling chain). Consider the following code.
function BlackMan: :dait( %DB ) { echo ( "In BlackMan: :doit(H SPC %DB SPC ")H )

155

Part III

Game Elements

function BlockMan2: :doit( %DB ) { echo( "In BlockMan2: :doit(" SPC %DB SPC "I" ); %obj
=

new Player( BlockMan I

II
};

%obj.doit(); II Calls BlockMan: :doit %obj.setDatablock( BlockMan2 ); %obj.doit(); II Calls BlockMan2: :doit

If you stop and think about it, this is an extremely powerful tool and can be used for some heavy-duty coding. Note also that objects that render a shape will render the new shape as defined by the new datablock, so this is a quick way to change an object's entire mesh.

5.5 Summary of Core Classes


This is a rather short chapter, but it is very important because these classes form the basis for almost all scripting that we will do in the future. In almost every gameplay-related script we write, we will touch at least one of these classes' features.

156

Chapter 6
Basic Game Classes

6.1 Shape and Interiors


In this chapter, we will discuss all of the fundamental classes that are used to create models in our game world. Excluded from this discussion are any classes that are normally used as avatars. Torque supplies a large set of classes used to display two fundamental categories of models: shapes and interiors.

6. 1. 1 Shapes
In TGE, shapes are normally nonstructural objects. More exactly, shapes should not be used to represent an object that must have both an interior and an exterior that can be accessed via another shape. The reason for this is simple: shapes have only exterior collision. Shapes are created and rendered either with the children of ShapeBase or ShapeBaseImageData, or with TSStatic. In this chapter, the two children of ShapeBase that we will be discussing are the following.

Item. Used to represent interactive items like coins, pickups, and powerups. StaticShape. Used to represent objects that are stationary or have limited movement/interaction capabilities.
We will defer a discussion of the following ShapeBase children classes until the next chapter because they are normally used as avatars and require special attention.

Player Vehicle, WheeledVehicle, HoverVehicle, and FlyingVehicle


Additionally, we will discuss the following high-level topics in the next chapter.

GameView/POV. We discuss how the interactions of several classes combine for our GameView and determine the point of view. Inventories. A nearly universal construct is the inventory. We will discuss the basic elements of the one that is included with this guide.
157

Part III

Game Elements

6.1.2 Interiors
Interior lighting and shadowing ;s pretty nice, but if you wish to have more control over this, and if you want these lights to affect nonstatic objects like the player, you should pick up the Torque Lighting Kit for TGE or consider moving up to the Torque Shader Engine.

tr

Interiors are used to display models that represent any structural object, including such things as buildings, bridges, walls, and other large structures. The motivation for this name comes from the fact that these objects can have an actual inside. This type of model supports arbitrary collision with both inside surfaces and outside surfaces. The class used to represent interiors implements a standard BSP collision scheme. Thus, it supports dividing models/meshes into n-dimensional convex partitions that can be entered. Additionally, interiors can use portals to cull hidden geometry. Some other features supported by interiors are self-shadowing, terrain shadowing, and light maps. Interiors will self-shadow and, when the relighting phase executes, the engine will back a shadow texture into the terrain based on the location of each interior. Interior shadowing and lighting are accomplished with the use of precalculated light maps. The basic exporter produces pretty nice light maps. Additionally, there is a radiosity exporter available for creating smoother lighting. Most of what you will need to know about interiors is art-based and includes such things as placing portals correctly, creating BSP-acceptable geometry, adding lights and textures, and preparing multiple level of detail (LOD) versions of meshes.

6.2 ShapeBasejShapeBaseData
These are the root classes in the ShapeBase class hierarchy. The ShapeBase class itself cannot be used to create objects in the world. It should be considered a "virtual" class. Instead, use the children classes. ShapeBaseData is the datablock class associated with ShapeBase.

6.2. 1 ShapeBase and ShapeBaseData Features


ShapeBase and ShapeBaseData have the features shown in Table 6.1. As can be seen, these classes have a significant burden for providing shape functionality. As a side effect, ShapeBase-derived objects have a significant network weight. Thus, if you do not need any of the features in Table 6.1 for a shape, consider using TSStatic instead (see Section 6.5).

6.2.2 Rendering
In order to be rendered, a shape must provide a model (mesh). Additionally, we might wish to allow a shape to be cloaked and/or to render an environmental map. These features are provided by the ShapeBaseData datablock.

158

Basic Game Classes

Chapter 6

category
Rendering

Features
Environmental mapping Cloaking Fading Hiding Skinning Damage level tracking Damage states Self-repairing Invincibility Damage flashes and whiteouts Explosions

Table 6.1.

Damage

Energy

Energy level tracking Recharging Mass Density Drag Velocity Impulses

Physical Parameters

Eye Transforms Camera Settings


Field of view Point of view Range and angle limits Four threads Four independent threads Shape-to-shape Image-to-shape

Animations Sound Mounting

In the following example, we are creating a StaticShapeData datablock named "FadeEgg":


II Fade Egg from Rendering datablock StaticShapeData( FadeEgg
{

category = "LessonShapes" shapeFile = "-/data/Shapes/Lessons/GeneralLessonShapes/egg.dts";


};

159

Part III

Game Elements

Later, we can create an instance as follows.


%theEgg = new StaticShape datablock FadeEgg;
};

Environmental Mapping
If we set the "emap" datablock parameter to true, the shape will use the environmental mapping texture specified for the sky object, if it was specified.

Cloaking
ShapeBase-derived shapes have the ability to cloak. When a shape is cloaked, it is reskinned with the cloakTexture specified in its datablock. Furtherore, this skin is rendered at a fixed overall alpha (specified in the engine). The cloakTexture does not need an alpha channel for the cloak to succeed, however, if the fixed alpha used by the engine is not low enough, you can further reduce it by using a cloakTexture with an alpha channel. Shapes that are cloaked behave just like uncloaked shapes in all other respects. In order to cloak an object, first define a datablock with a cloak texture, as follows.

~-----If you choose to not specify a

cloakTexture

and then you cloak a shape, that shape will get a default white texture. This is actually pretty nice and gives a reasonable cloaking effect. You might consider trying this before working too hard on a special texture for cloaking.

~
~ ~
};

datablock StaticShapeData( CloakEgg ) { category = "LessonShapes"; shape File = "-/data/Shapes/Lessons/GeneralLessonShapes/egg.dts"; cloakTexture = "-/data/Shapes/Lessons/GeneralLessonShapes/testskin.png";

Then, having created an instance, enable cloaking as follows.


%theEgg.setCloaked(true);

Fading and Hiding


ShapeBase-derived shapes have the ability to fade in and out of view as well as to be hidden. While a shape is fading in or out, its collision mesh is still active. In fact, once a shape is completely faded out, its mesh is still active and can still be collided with. You must hide an object to disable its collision mesh.
160

Basic Game Classes

Chapter 6

Fade this egg from view, over a 1.5 second period, starting immediately %theEgg.startFade( 1500 , 0 , true ); Schedule the egg to be 'hidden' in 1.6 seconds (disables collision mesh) %theEgg.schedule( 1600 , setHidden , true );

II II

II II

Skins
ShapeBase-derived shapes are allowed to have multiple skins. In order to use this feature, the skins to be used for a shape must follow some simple rules. First, a texture (skin) is required to group the skins for this shape. It has a name of the form base. setName. suffix.
base. The engine looks for this special prefix and uses it to 'group' textures

by setName. setName. This string identifies the skins that are in this group. suffix. This is any acceptable TGE image format: PNG, lPG, etc. (see Appendix for complete list). Subsequently, any textures to be included in the set for multiskinning must have names of the form setName. skinName. suffix.
skinNarne. This is the optional part of the skin name and is used in the setS kinName () method (see below). This name can be any arbitrary string you wish to use. Skin names are stored as tags.

For example, if you wish to have a shape with three skins, you could use the following textureNames.
base.skin.png skinO.skin.png skin1.skin.png skin2.skin.png

II II II II

Apply this texture to the shape. First Skin Second Skin Third Skin

Subsequently, we could change the skin for a shape as follows:


%obj.setSkinName("skin2");

II

automatically converted to a tag

II

OR

%obj.setSkinName('skin2');

II

The tag itself.

The question will arise, "Can I do this for multiple textures on the same mesh?" Yes, you can have multiple texture groups on one mesh, but when you
161

---

--------_.. - - - - - -

Pare Iii

Game Elements

flip one texture, all the other textures will revert to their base texture. So, if you need to change multiple textures, you might want to consider using IFLs (image file lists) as an alternative to multiskinning. In fact, IFLs may be better anyway if: you wish to animate a texture rapidly, and/or you wish to change the texture on only a small part of the shape, and you are willing to give up one animation slot (per playing IFL).

6.2.3 Damaging, Disabling, Destroying, and Exploding!


ShapeBase-derived objects can be damaged, disabled, and eventually destroyed. Upon destruction, a shape may continue to render, or it may explode and leave behind debris.
Damaging

To allow a shape to take damage, we must define some key values in our datablock:
datablock StaticShapeData ( SelfHealingBlock ) (

//

...

maxDamage = 100; disabledLevel = 80; II Disabled at 80 or greater points destroyedLevel 100; II Destroyed at maxDamage repairRate = 0.05; II Repair @ 1.6 points per second

) ;

What we have said here is that this object can take up to 100 damage points and that it should be considered disabled at 80 points and destroyed when it hits 100. If we wished, we could set either of these values higher than maxDamage, which is the same as saying "cannot be disabled" or "cannot be destroyed," respectively. The last value repa i rRa te tells the engine to apply 0.05 points of "repair" every tick until damage equals zero.

Setting Up Repairs
This seems pretty simple so far, but a few things need to be clarified. Although we have specified values for damage, our shape will not do anything automatically. We are responsible for applying damage, changing the damage state of the shape, and setting the repair rate. Until the repair rate is set by calling setRepairRate (), a shape will not self-repair.
162

l_.

Basic Game Classes

Chapter 6

Thus, when we create our object, we want to use setRepairRate () to enable self-repair. An ideal place to do this is in the shape's onAdd () callback: function SelfHealingBlock: :onAdd( %OB , %theShape ) (
%theShape.setRepairRate( %DB.repairRate );

Damaging
Later, we may wish to apply damage to our shape. To do so we would use code similar to the following. %theShape.applyOamage( %someOamage );

Repairing Manually
In addition to a damage method, a method is supplied to repair a shape as follows. %theShape.applyRepair( %someRepair ); There is, however, a slight trick to making this work. If we have chosen to allow our shape to self-repair (by calling setRepairRate () with a nonzero value), we cannot apply repairs at a greater rate than the specified rate. In other words, if you want to repair an object that is able to self-repair, you will need to do the following.
II Turn off self-repair %theShape.setRepairRate( 0 ); II Do the repair %theShape.applyRepair( %someRepair ); II Turn self-repair back on

%theShape. setRepairRate (%theShape.getOatablock() .repairRate ); Of course, if a shape is not automatically repairing, then we simply call applyRepair (), and we're good to go.

Damage States
It is up to us (through the use of scripts) to take responsibility for tracking the

damage level of our shape and for setting its damage state. What is a damage state, you ask?
163

---------

--------- ----

Part III

Game Elements

..-----In addition to the

th~es~nda~ states, players (which we have not yet discussed) "d d" can a Iso b e ea, which is equivalent to being disabled and destroyed.

~
~
~

A normal shape can be in anyone of three (damage) states: enabled, disabled, or destroyed. A sample method to deal with this might look like the following:

functlon ShapeBase: :determineDamageState( %theShape %curDamage = %theShape. getDamageLevel () ; %disabledDamage = %theShape. getDa tablock () . disabledLevel; %destroyedDamage = %theShape.getDatablock () .destroyedLevel; if( %curDamage >= %destroyedDamage ) ( %theShape.setDamageState( Destroyed); else if( %curDamage >= %disabledDamage ) %theShape.setDamageState( Disabled );
}

else ( %theShape.setDamageState( Enabled );

Invincibility
It is possible to make a shape invincible, either permanently or temporarily. To do so permanently, we use the is Invincible keyword in the datablock.
datablock StaticShapeData( InvincibleBlock : SelfHealingBlock) / / ... islnvincible true;

};

If we only want this invincibility to be temporary, we can use the setInvincibleMode () method.
%theShape.setInvincibleMode( time, speed);

This method works as follows. The shape on which this is called will be invincible for a period of time specified by the floating-point value time, as measured in seconds. The screen will flicker blue if it is the control object that has been made invincible. The flickering effect is used to indicate to a player that his or her avatar is invincible. Furthermore, this flicker rate will change and the flicker will become increasingly translucent as the time elapses.

164

Basic Game Classes

Chapter 6

The rate of this flicker is controlled by the floating-point value speed. apeed can be between 0 and 1. If it is set to 0, there is no flickering. If it is set to 1, the flickering is very fast. Generally, lower values are nicer.

Postdestruction Rendering
This leads us to a final damage topic, which is postdestruction rendering; thaI is, does the shape render subsequent to destruction? This, too, is determined by the datablock.
datablock StaticShapeData( ExplodeGears ) ( I I ... renderWhenDestroyed false;

) i

In this instance, we have instructed the engine to stop rendering the shape when it is in the "destroyed" state. Unfortunately, there is a catch. Even if the engine stops rendering the shape, the collision box will remain active; i.e. colHsions will still happen. Therefore, if you wish to entirely remove the object from interaction, you should either delete it subsequent to destruction or hide it.

Damage Flashes and Whiteouts


What are damage flashes? Welt in your game, the player may at some time take damage or be blinded by a bright light. In order to express this concept to the person playing your game, you can use the following console methods.
II Show red-haze to imply massive damage to player %cam = %player.client.camerai II We need the camera 1D to do flashes %cam.setDamageFlash( 1.0 ); II Show Whiteout to imply slight and temporary blinding. %cam = %player.client.camera; II We need the camera 1D to do whiteout %cam.setWhiteOut( 0.5 )i

These two effects can be applied together. It is important to understand that these methods must be called on the camera for the effect to be shown. Calling this on other shapes has no effect. Lastly, there is also a blackout function in the engine, but it is not hooked up with a console method. However, if you have the source code, hooking this up would be as easy as 1 ... 2 ... 3.
165

Pare il!

Game Elements

Explosions
If you have specified an explosion datablock for your shape and the shape is destroyed (setDamageState () is called with the argument Destroyed), the shape will create an explosion object at the current location of the shape. The explosion will then play and delete itself when finished. It is that simple. In addition to the explosion field, there is an underwa terExplosion field. This field is used to specify an alternate explosion that should be played when the shape is destroyed underwater. If no underwaterExplosion is specified and the shape is underwater, then the normal explosion will be played.
datablocl< StaticShapeData ( ExplodeGears ) { / / ... explosion "GearsExplosion"; underwaterExplosion "GearsUnderwaterExplosion";

};

Debris As with explosion datablocks, if a debris datablock has been specified for your shape and the shape is destroyed, the shape will create a debris object at the current location of the shape. Debris represents the refuse left behind by a destroyed shape. Debris can behave in a wide variety of ways and therefore merits its own discussion. If you are interested. please skip ahead to Chapter 11, "Special Effects."
datablocl< StaticShapeData( ExplodeGears ) { / / ... debris = "GearsDebris";
};

6.2.4 Energy
ShapeBase-derived objects can have energy. This energy can be used for various purposes such as powered movement, weapons, vehicles, etc. Initially, shapes start out de-energized (energy level == 0). We may choose to provide an initial charge at creation time and/or to enable recharging. Before we can do either of these, however, we must set up the datablock as follows.
datablock StaticShapeData( FireTube ) ( / / ... maxEnergy 20; rechargeRate 0.05; II 1.6 points per second

L'~

) ;

Basic Game Classes

Chapter 6

The above datablock tells the shape that its maximum energy is 20 points and that, when the energy is below maximum, it will recharge at a rate of 0.05 points per tick (about 1.6 points per second). As with self-repair, we need to enable recharging with a method call. To give a shape an initial charge, and then to enable recharging, we can do the following.
function FireTube: :onAdd( %DB , %theShape ) ( II Start with maxEnergy %theShape.setEnergyLevel( %DB.maxEnergy );

II Enable recharging %theShape.setRechargeRate( %DB.rechargeRate );


6.2.5 Physical Parameters
Being in the world, most shapes will need the ability to interact. In real-world terms, interactions are based on physics. As this is only a simulation of reality, a minimal set of physical parameters is supplied for all shapes via a shape's datablock. All shapes have the concept of mass, density, and drag. These can be considered unitless, but it is often nice to treat mass and density as metric units (kilograms and kilograms per cubic meter, respectively).
datablock PlayerData ( BlockManPlayer ) ( / / ... mass = 90; II Kilos density 10; II Kiloslcubic meter drag 0; II Unitless 'air' resistance

Unlike with self-repair and manually applied repairs, we may manually add energy to our shape even if it is recharging.

) ;

Velocity At any time, a shape may be in motion. Thus, it is handy to have a means of getting and setting the current velocity of a shape.
%obj.getVelocity(); %obj.setVelocity( velocity);

Impulses
If we wish, we can apply an impulse to any shape with mass. An impulse is an application of force, causing an instantaneous change in velocity.
167

---

--~

---- .

--------

Part III

Game Elements

Take a look at the following example to see how we apply an impulse:


II Give this player a whack (lOx mass) straight up
%objectMass
=

%player.getDatablock() .mass;

%impu1seVector = vectorScale( "0 0 1" , %objectMass * 10 );

%player.applylmpulse( %obj.getWorldBoxCenter() %impulseVector );

Varying Impulse Position


The astute reader will notice that the impulse method takes a position vector. The question that arises in the curious mind is, "What happens if I apply ~ an impulse to a position that is not in the center of a shape?" The answer: Applying impulses Results may vary. to StaticShapes and to items with the The reason to allow an off-center impulse is to allow us to spin an object. sta tic parameter However, only vehicles will spin. All other classes will ignore any offset and set to true, will do treat the impulse as if it is applied to the shape's centroid.

nothing. These shapes cannot be moved by impUlses.

6.2.6 Eye Transforms and Vectors


In addition to the transform and the forward vector inherited from SceneObject, ShapeBase and children provide the following positions and vectors.
Eye point. A point in three-space, representing the position of the shape's eye. Eye vector. A vector representing the pointing direction of the shape's eye. Eye transform. A transform, not for the shape but for the shape's eye.

Each of the above quantities are available if the mesh used by the shape has defined a skeletal node with the name "eye." To acquire these quantities, we use the following methods.

II Eye Point echo( %obj.getEyePoint() II Eye Vector echo( %obj.getEyeVector()

);

);

II Eye Transform echo( %obj.getEyeTransform()


168

);

Basic Game Classes

Chapter 6

It is possible to call these methods on a shape without an eye node, aS~he engine will use the shapes centroid as the eye in this case. Just be aware ~ that this is what is happening. ~ -------.,. We have not . talked about 6.2.7 Camera Settings POVyet, but if you

The ShapeBase camera settings are part of a larger discussion that encompasses the GameView and the player's point of view, so we will come back to . . thIS class when we talk about those tOPICS.

6.2.8 Animations
ShapeBase-derived shapes have the ability to run up to four simultaneous animations. These animations can be any of the supported animations: nonblended (absolute) skeletal, blended skeletal, image file list, and visibility. These animations are applied in the order of the threads they occupy, which is important to keep in mind for blended skeletal animations.

Cyclic Animations
TGE supports the concept of a cyclic animation. A cyclic animation is nothing more than an animation that cycles. When an animation cycles, it progresses as follows: frame 0, frame 1, ... frame n, frame 0 ... , ad infinitum until paused or stop.

are at all familiar with games, you will already know that a camera can be in 1st POV (looking through the eyes of the player) or in 3rd POV (somewhere external to the player). The above eye quantities are all relative to a 1st POV viewpoint, so if your game is running in 3rd POV, all three quantities will be unchanged by the movement of the camera; i.e., the eye will still be in its 1st POV position, and the eye vector will not track the camera. To learn more about this topic, see the camera discussion in Chapter 7, "Gameplay Classes."

Playing
To play an animation, we must have the name of the animation and a free thread to play it in. If there were already an animation present in thread 0, the playing script shown would In this sample, we've decided to play an animation named "someAnimation" normally stop that in thread O. As soon as this statement is executed, the animation will begin animation and start the new animation. to play and will continue to play until it hits the end of its sequence. Upon For the exceptions, hitting the end of its sequence, an animation can do one of two things. If it is see noncyclic, it will stay in the "playing" state and hold on the last frame of t \ D e "Anima,~ion animation. If it is cyclic, the animation will start over at the first frame of , O_d_d_l_tle_s_. .../ the animation.
%obj.playThread(

o,

"someAnimation" );

169

Part III

Game Elements

Direction
Animations have the concept of a direction. They can be played forward or in reverse. All animations start playing in the forward direction. To change an animation's direction, we use the method below.
%obj.setThreadDir( 0 , true ); II Play thread 0 FORWARD

II OR
%obj.setThreadDir( 0 , false); II Play thread 0 REVERSE

Pausing and Stopping


So far, we know how to play and reverse a thread, but what if we need to pause our thread or stop it entirely? Both of these options are available to us. We can toggle pause; i.e., if the thread is playing it will pause, and if it is paused it will start playing again.
%obj.pauseThread( 0 ); II Toggle pause for thread 0

~'#We can also stop an animation.


%obj . stopThread ( 0 ); I I Stop the animation in thread 0

Stopping an animation resets the joints affected by this animation to their pre-animation positions; i.e., the animation transforms' are no longer applied. You need to do this if you want to re-pose a noncyclic thread that has reached its end.

__

Animation Oddities
It is worth noting that, when using the animation methods to control animation threads, there is some latency involved. So, you may run into some strange issues while playing threads.

Noncyclic Threads Remain in Play State at End of Sequence


When a noncyclic animation is played, it eventually completes. However, TGE does not automatically stop the thread. Instead, the thread remains in the "play" state. If you have a noncyclic thread that you wish to "re-play," you would think you could simply type:
%obj.playThread( 0 , "someAnimation" );

Unfortunately, this will not work. Nor will the following.


%obj.stopThread( 0 ); %obj.playThread( 0 , "someAnimation" );

170

--------------

Basic Game Classes

Chapter 6

Instead you'll need to do one of two things. You can schedule a stop after starting the thread, as follows. %obj.playThread( 0 , "someAnimation H ) ; %obj.schedule( time, stopThread, 0 ); II time > animation length {n ms Otherwise, you'll have to delay the restart as follows. %obj.stopThread( 0 ); %obj.schedule( 100 , p1ayThread,

o,

"someAnimation H

);

Damage Animations
All ShapeBase-derived objects will automatically play two different animation sequences based on the shape's damage state.

"Visibility" Sequence
The first of the two sequences that is auto-played is the "Visibility" sequence. This should be a blended animation. It will assume one of two positions; Le. it is either off or on and does not actually play an animation sequence. When the shape's damage state is not destroyed, the shape plays position zero (0) of this sequence. When the shape is destroyed, it plays position one (1) of the thread.

"Damage" Sequence
The second of the two sequences that is auto-played is the "Damage" sequence. This sequence can be blended or nonblended. This thread plays as follows. IfdamageLevel >
=

destroyedLeve1,

By now, you've seen the method schedule() a few times and have probably begun to wonder what it is. Although we will discuss this in Chapter 10, "Gameplay Scripting," let me summarize what it is now. The schedule () method is used to schedule either a function or a method call in the future. The variety we have used thus far schedules methods. In this example, we scheduled a method named stopThread to execute in time milliseconds. This method will be called on the object %obj that scheduled it and will be passed a O. That's it.

If damageState = = "Destroyed," play "Damage" sequence position zero (0). If damageState ! = "Destroyed," play "Damage" sequence position one (1) . If damageLevel < destroyedLevel,playthreadatpositiondamageLeve1 / destroyedLevel.

In short, this sequence advances as damage is accumulated, until the shape is destroyed. This thread/sequence is used to create a damage effect on shapes and may involve IFLs, geometry animation, visibility animations, etc.
171

Part 1/1

Game Elements

6.2.9 Sound
ShapeBase-derived objects have the ability to control up to four simultaneous sound threads. Sounds themselves are declared using audio profiles (AP) and audio descriptions (AD) (see Chapter 11, "Special Effects"). Playing a sound declared with the audio profile named "SomeAudioProfile" is as simple as the following.
%obj.playAudio( 0 , SomeAudioProfile );

How this sound plays is up to the AP and the AD. It may play forever or it may play only once. However, if we wish to stop this sound from playing, we can do so with the following code.
%obj.stopAudio( 0 );

6.2.10 Mounting
ShapeBase-derived shapes have the ability to mount other shapes and ShapeBaseImages. In total, eight shapes, eight ShapeBaseImages, or any combination of up to eight total can be mounted to any single shape. Mounting is tracked through the use of mount slots. Mount slots should not be confused with mount nodes.

Mount slots are the indices into the shape's mount list. Mount nodes are positions on the shape corresponding to named joints/ nodes in the model. These names are mountO ... mount31 (TGE supports a maximum of 32 mount nodes).
To clarify the difference between nodes and slots, let's look at the images in Figure 6.1 from one of my own game prototypes. In the game, eight shields are attached to this tower, all of them attaching to the mountO node (Figure 6.1 a). In Figure 6.1 b, you can see three of the shields. In Figure 6.1 c, three of the shields have been attached to mountO.

Figure 6.1 a.

Tower with mountO node.

Figure 6.1 b.

Individual shields with mountPoint node.

172

Basic Game Classes

Chapter 6

The important takeaway is that, although all three shields are attached to the same node (mounW), they are each in their own slots. Assuming they were mounted from innermost to outermost shield, those slots would be Slot O-Inner Shield, Slot I-Middle Shield, Slot 2-0uter Shield.

Figure 6. 1c.
Shields mounted to tower at mountO.

Things to Know
You should be aware of the following. Mounted shapes and images will translate and rotate with the node that they are mounted to.
If a mount node is animated, the shape/image mounted to that node will follow the node through its animation.

Multiple objects/images can be mounted to the same mount node but not in the same slot. Images do not have collision meshes and will therefore not collide with objects when the shape they are mounted to moves. Shapes that are mounted to other shapes retain their collision meshes. There is a pretty hefty set of console methods dedicated to dealing with mounting tasks. We will not be covering them all here, but never fear, they are all listed in the "Console Fields and Methods" Section of Appendix A with descriptions that should clarify their purposes. For now, we'll do a simple example showing what it takes to mount a shape to a shape, and then an image to a shape.

Mounting Shape-to-Shape
In the following examples, we will be discussing two shapes, shapeA and shapeB. In all instances, shapeB will be mounted onto shapeA. For the mount to succeed, shapeA must have a numbered mount node (Le., mounW ... mount3l) defined in the DTS file. Additionally, shapeB must have a node named "mountPoint" (also defined in the DTS file). Given this, mounting is as simple as the following.
%shapeA.mountObject( %shapeB , 10 );

Once this code executes, %shapeB should now be attached to %shapeA at mount node 10. However, if shapeA does not have a mountlO mount node, or if shapeB does not have a node named "mountPoint," then the mount will
173

Part III

Game Elements

probably either be shapeB center to shapeA center or shapeB center to shapeA foot (this happens with bad mounts to the player). Assuming that the mount worked, shapeB will now translate and rotate with shapeA's 'numbered mount node. This means that any translation or rotation of the numbered mount node (including those caused by animations of the node) will rotate and translate shapeB. Additionally, shapeA's collision box remains active and will record collisions. Well, that's all fine and dandy, but some time in the future, we may wish to detach these two shapes from each other. To do this, simply use the following code.
%shapeA.unmountObject( %shapeB );

Mounting Image-to-Shape In the following examples, we will be discussing the mounting of a ShapeBaselmageData data block (Image) to a ShapeBase object (Shape). We will refer to the Shape as shapeA and the Image as imageA. To be absolutely clear, imageA is being mounted to shapeA. As with shape-to-shape, shapeA must define a numbered mount node (Le., mounta ... mount31) in its DTS file, and imageA must define a mount node named "mountPoint." As an additional requirement, the datablock definition for imageA must specify which numbered node in shapeA it will mount to. In other words, every ShapeBaseImageData datablock predefines which numbered mount node it can attach to.
datablock ShapeBaseImageData ( imageA ) (

I I ...
mountPoint
};
=

15; II ONLY mounts to mount mode 15

Having properly made our DTS files and having declared a databJock for imageA with a mountPoint field, we mount the Image to the Shape as follows.
II Mount imageA to shapeA on mount mode 15, II using slot 0 (of 8)
%shapeA.mountlmage( %imageA , 0 );

174

If you examine this code closely, you will notice three things. First, imageA is being mounted to shapeA. Second, when we called mountImage (), we passed it the name of the Image datablock as the first argument. Remember that Images are datablocks, and datablocks each have a unique ID. Also, remember that TGE can use

Basic Game Classes

Chapter 6

either IDs or names. Thus, as long as the name is unique (as it is for all datablocks), you are guaranteed to get the proper object, which is in this case the imageA datablock. Third, the second argument to the mountImage () method is O. When mounting an image to a shape, we must specify the slot that the mounting will be recorded in. This is important because, if by some chance you mount two images to the same shape and the second image uses the same slot as the first image, the first image will be dismounted. Images can be mounted to the same numbered node on a shape, but the mount information must be tracked in different mount slots. Finally, to detach imageA from shapeA, we use the following code.
%ShapeA.unmountlmage( %imageA );

6.2. 11 Miscellaneous-CRC and aiAvoidthis


ShapeBaseData provides a couple of miscellaneous fields. The first is "computeCRC." This field, if true, tells the engine to do some error checking when loading this shape. If the error checking fails, we will get an error message complaining that the shape could not be loaded, and the game will fail out to the menu. Why do this? WelL for one thing, this ensures that the server and all clients are using the same version of a shape. The CRC (cyclic redundancy code) is calculated on the server, and thus if a client in a multiplayer scenario has a nonmatching eRC, that client will fail out. The other miscellaneous field is named aiAvoidthis and has no function at this point. You may use this in your scripts to indicate that an AI should avoid the object. The only benefit this has over using a server-side dynamic field is that this field is networked, allowing clients to observe it, too.

6.3 Item and ItemData


'.
These classes are used to represent items, specifically, items that the player will interact with. These are things like weapons, power-ups, traps, mines, etc. Item and ItemData have all the features of their parents, ShapeBase and ShapeBaseData.

6.3.1 Item and ItemData Features


Item and ItemData add the following features to those inherited from ShapeBase and ShapeBaseData.

Rendering Light emission

175

- - _.. _ . . -. .

_.... -..

._-_. . . . . . . __ ._-_..._- -

Part III

Game Elements

Physics Stationary (Static) + nonstationary placement Auto-rotation (spinning animation)


Elasticity Velocity limits Stickyness Friction Gravity modification

Collisions Collision timeouts Dynamic typing


Items are used to represent objects that are to be picked up or otherwise interacted with. They are special in that they can be walked through but still signal a collision event.

6.3.2 Item Rendering


Items add one new trick to the rendering feature set: dynamic lights!

Lights, Camera, ... Action


Items can emit light in three ways: none, constant, and pulsing. In order to create an item with a light, specify the datablock as follows.
datablock ItemData( ConstantLightEgg ) { / / ... lightColor= "100 1.0"; 1ightRadius = 6.0; lightType "ConstantLight";
};

When an item is made from this datablock, it will emit a constant red light with a radius of 6 world units. The three names for the light types are NoLight, ConstantLight, and
Pu1singLight.

The lighting of an item can be further modified such that, if the item is nonstatic, it does not render a light.
datablock ItemData( ConstantLightEggStaticOnly ) { / / ... lightOnlyStatic true; 176
};

Basic Game Classes

Chapter 6

6.3.3 Item Physics


A fun thing about items is that they display all kinds of interesting physical attributes. They can be made to stay put or move around, to rotate, to bounce, to slide, to fall and fly at varying rates, or to float away.

Static Items
When we create an item, we can set the static field in the object (not the datablock) to true or false.
%theEgg = new ItemData() { datablock = "ConstantLightEgg H static false;

);

Setting this field to true tells the engine that this item will stay put once it is placed. If we want to allow it to move after placement, we set s ta tic to false. This parameter can be set in the create () method for ItemData, in the onAdd () callback for the data block that is used to create the item, or as we have done above, in the object creation statement. If we wish to change the static field later, we can do so. We can also check the current value as follows.
if( %theEgg.isStatic() ) { echo("This egg is static. It won't move now.
H

);

Rotating Items
Items are often used to represent objects that the player is meant to pick up. A common hint that an object is meant to be picked up is that the object rotates. This is often seen in arcade games and first-person shooters. Thus, TCE provides the ability to cause an item to rotate. This is done by setting the rotate field in the object (not the datablock) to true.
%theEgg = new I temDa ta () { datablock = "ConstantLightEgg H rotate = true;
);

The rotating state of an item can be modified at any time, and we can check it by using the following method:
if(

%theEgg.isRotating() ) { echo("This egg is rotating.

);

177

PdTt III

Game Elements

Bouncy Items As noted above, items can be made elastic, causing them to bounce when dropped. The field elasticity can take both positive and negative values. A positive value of 1 is not guaranteed to be equal to 100 percent elasticity, due to rounding errors. Also, if you choose to use a negative value, be aware that, if you don't limit the velocity (see maxVelocity below), eventually a bouncing item will crash the engine when the instantaneous change in velocity becomes too high.
datablock ItemData( BouncyEgg ) { / / ... elasticity 0.7;

};

The datablock above will produce an item that bounces for a while then settles down.
Maximum Velocity fmaxVeloci tyJ

Because we have various ways of causing an item to move and perhaps to increase its velocity, because the engine does not handle very high velocities and accelerations well, and for practical playability reasons, we need a way to limit the velocity an item can achieve. This is done quite simply as foHows:
datablock ItemData( LimitedVelocityEgg ) / / ... maxvelocity = 1000; II Limited to 1000 world units I s
};

Sticky Items
It may be that sometimes we would like an item to stick when it hits the ground. This can be achieved by making the item sticky.
datablock ItemData( StickyEgg ) ( / / ... sticky = true;
};

An item made with the above datablock will stick to the terrain when it faHs to the ground. This overrides elasticity. When an object sticks to the terrain, we can get both the position of the item and the normal at that point as foHows.
178

r-I
I

Basic Game Classes

Chaprer 6

%lastPos = %myltem.getLastStickPos(); %lastNormal %myltem.getLastStickNormal(); if ( 100000 < vectorLen ( %lastPos ) ) ( echo("This item did not stick yet."); else ( echo("This item stuck at: ", %lastPos, " with a normal of: ", %lastNormal);

hasStuck ()

Before version 1.4 of the engine, it was hard to tell if an item had stuck yet. However, with the official release of 1.4, a new method has been provided:
%stuck

%stickyEgg.hasStuck();

This method will return t rue if the item has in fact stuck to something.

Sliding Items
If some velocity has been imparted to an item, or if it has fallen to the ground in a sloped area, we may wish for this item to eventually stop sliding. TGE provides a friction field which can be made either negative or positive. Low values equal low friction, and high values equal high friction. A negative value will actually cause the item to accelerate. Again, we need to use caution with negative values; as with elastici ty values greater than 1.0, a negative friction will eventually cause the engine to crash. Interestingly, we can use a negative friction with a maxVelocity to create an item that stays in perpetual motion at about the same velocity.
datablock ItemData( PerpetualMotionEgg ) (

/I

...

friction = -10; II Accelerate rapidly to our limit maxVelocity = 20; II Limited to 20 world units / s

I;

Modifying Gravity fgravi tyModJ


Items have the ability to "experience" their own gravity; that is, we can modify the way gravity affects individual items. This is done through their datablocks as follows.
datablock ItemData( LowGravityEgg ) { / I ... gravityMod = 0.25;
};

179

..::..-.-

__ .-

Pelf[ 111

Game Elements

An item made with the above datablock will only experience one quarter the gravity normally experienced by an item. We can also make our gravi tyMod values negative. If you do so, be sure to limit the velocity. or else the object will flyaway and eventually crash the engine. Also. such an item should be moved back to a starting point or eventually destroyed, otherwise it will float off and be of no use to the player.

6.3.4 Item Collisions


Items are intended to represent objects that the player interacts with in the world. usually by running over them and picking them up. Consider that eventually we may wish to drop items that we have picked up. We'll cover this in all its gory detail in the "Inventories" section in Chapter 7. but basically we create a new instance of the to-be-dropped object and then drop it where the player is or toss it away from the player. Now, consider that. if we don't have a way to disable the collision features. we'll just pick the object up again as soon as it is created. Thus, collision timeout for items exists.

Collision Timeout
Individual items can be told to ignore collisions with one specific object for a short period of time. We simply do the following.
%iternHandle.setCollisionTirneOut( %objectToIgnore };

In the above example, we've told the item represented by %iternHandle to ignore collisions with %objectToIgnore. It will honor this request for approximately a half second and then re-enable collisions with the to-beignored object.

6.3.5 Items and dynamicType


.~.

180

A dynarnicType field is speCified for StaticShapeData and Item Data datablocks, In both cases. it proVides the ability to further differentiate an object's type by providing a value that will be added to the result of get Type () when called on this object. If you'll recall our earlier discussion of the getType () method (Section 5.1.4), you'll remember that each mission-placeable object has an associated bit-mask. We use these masks to differentiate objects when doing ray casts. radius searches. etc. The proper way to deal with dynamicType is to specify a new mask (in objectTypes.h) and export it to the console (in main.cc). Subsequently, you can use this value in dynamicType. and then getType () for these objects (and their children) will also have your new bit position set.

~--

Basic Game Classes

Chapter 6

Having said that, if your purpose is only to use this in scripts, you can specify new $TypeMasks : : values in script and use them. However, the best and safest way to do this is within the engine framework, where you will benefit from the checking your compiler does for you.

6.3.6 Maze Runner Lesson #3 (90 Percent StepJGame Coins


In this lesson, we will examine the game coin's datablock definition. Later, we will implement scripts to pick up these coins, but for now, all we need to do is talk about the coin's geometry, the datablock definition, and the creation script.

Copy Required Files


From the accompanying disk, please copy the file \MazeRunner\Lesson_003 \ coins.cs into \MazeRunner\prototype\server\scripts\MazeRunner. Now, edit the function onServerCreated () in the file \MazeRunner\ prototype\server\game.cs to look like the following.
exec(".IGPGTBase/loadGPGTBaseClasses.cs"); II MazeRunner exec("./MazeRunner/coins.cs"); II MazeRunner

You maybe tempted to try mounting an item to some other shape. If you try this, you will discover that it is not supported. In fact, items cannot be mounted to other shapes. but other shapes can be mounted to items. This is quite useful. as such mounted shapes can temporarily shield an item from contact and thus from

Please note that, until this step, the directory \MazeRunner\prototype\server\ scripts\MazeRunner did not exist, so you need to create it yourself.

Coin Geometry
The geometry for this coin is very simple and can be found in the file \MazeRunner\prototype\data\MazeRunner\Shapes\Items\coin.ms3d", where we copied it earlier. If you load the file in MilkShape 3D, you will see that it is nothing more than a thin disk. It has one render mesh and no collision mesh. Because this model is used for an item, a collision mesh will automatically be generated by TGE. The skin was generated using Ultimate Unwrap 3D. It's simple and does the job. Now, all we need is a datablock and a creation script, onAdd () .

The Coin Datablock


The datablock for our coins is very simple. If we look at the file we just copied, we will see the following datablock definition.
datablock ItemData( Coin: Baseltem ) { shapeFile = "-/data/MazeRunnerIShapes/items/coin.dts"; category = "Gameltems";

181

Part III

Game Elements

sticky = true; lightType = NoLight; mass = 1.0; res pawn = false;


};

The coin item has the following attributes. It is an instance of Item (just to be clear about this). It is derived from BaseItem (there are base datablocks for all of the classes we discuss in this guide). We'll be able to find this object under Shapes/GameItems in the Creator menu. It is sticky and will stay put when it hits terrain or an interior. It does not emit light. As a rule, I never create a massless object. This avoids any future difficulties should I choose to apply an impulse to the shape. So, this coin gets an arbitrarily chosen mass of 1. When this coin is picked up we don't want it to be respawned. So, we set the field respawn to false. This won't mean anything to you yet, but when we discuss the Simple Inventory sytem in Chapter 7, "Gameplay Classes," this will become clear.
The Coin onAdd ( )

We have mentioned callbacks only briefly thus far, and we will discuss them in Chapter 9, "Gameplay Scripting." For now, just know that all SimObject instances and all instances of children of SimObject call the onAdd () callback after the object is created and initialized. Later, when we write the scripts to place objects, it will become clear that we want objects to stay put when they are placed. Coins have the option of being static (don't move on their own), or nonstatic (affected by gravity and other forces). Therefore, we need to force the coin to be static by making a suitable onAdd () callback. Find the following code at the end of the file we just copied.
function Coin:: onAdd ( %DB , %Obj ) { Parent: :onAdd( %DB , %Obj ); %Obj .static true; %Obj.rotate = true;

The callback does the following.


182

f---I
I

Basic Game Classes

Olapter 6

Calls the Parent: : version of this callback to allow it to do any work it needs to do (optional and based on your design methodology). Sets the object as static. Now, it won't fall (due to gravity) or be affected by impulses. Makes the coin rotate. Now the render code will rotate the coin. Please note that this only rotates the render mesh, not the collision box that TGE generates.

Testing
To verify that our changes worked, you can: 1. restart the prototype, 2. open the "Maze Runner" mission, 3. start the Creator, 4. look under Shapes and find the folder GameItems, and 5. open the GameItems folder to find a new placeable shape, Coin.
If this did not work, check your console for errors (typos, files not found, etc.).

6.4 StaticShape and StaticShapeData


These classes are used to represent any world object that needs to allow moving objects to collide with it and needs at least some of the other features provided by ShapeBase and ShapeBaseData. If you want to make a completely stationary object that has a simple collision mesh and requires no interaction features, use TSStatic instead (see Section 6.5). StaticShape and StaticShapeData do not provide many new features. In fact, their main purpose is to act as a concrete instance of the ShapeBase and ShapeBaseData classes. In other words, you can create instances of these where you cannot create instances of ShapeBase.

6.4. 1 StaticShape and StaticShapeData Features


StaticShape and StaticShapeData have all the features of their parents ShapeBase and ShapeBaseData. Additionally, these classes provide the following new shape features. Powered state tracking Dynamic typing
183

Part III

Game Elements

6.4.2 Powered State


StaticShape adds the concept of powered vs. nonpowered. In truth, this is just a flag to be used by us in our scripts. The engine does nothing different based on this information. Using two new console methods, we can set and get the powered state of a StaticShape: %myStaticShape.setPoweredState( true ); II Shape is now 'powered' if ( %myStaticShape. getPoweredState () ) { echo("This shape is powered!"); else { echo("This shape is NOT powered!");

6.4.3 dynamicType
This field behaves exactly like the same named field found in the Item class. Please refer to Section 6.3.5, "Items and dynamicType," for a description.
Figure 6.2

a. Fade block.

6.4.4 Maze Runner Lesson #4 {90 Percent StepJFade and Fireball Blocks
In our game, we are going to have two kinds of special maze blocks. The first one will be a block that can be faded in and out of view (Figure 6.2a), and the second will be a block that shoots fireballs (Figure 6.2b). Both of these blocks require features from the ShapeBase hierarchy. The fade block uses the fading and hiding features. The fireball block uses the reskinning property. In this lesson, we will concentrate on the mesh properties and the datablocks that go with these two blocks. Later, we will write the scripts to fade the fade blocks and to shoot fireballs from the fireball blocks.

p. Fireball block.
Copy Required Files From the accompanying disk, please copy:
file \MazeRunner\Lesson_004\fadeblock.cs into prototype\server\scripts\MazeRunner, and 2. the file \MazeRunner\Lesson_004\fireballs.cs into prototype\server\scripts\MazeRunner.
1. the

\MazeRunner\ \MazeRunner\

Then, modify onServerCreated () in \MazeRunner\prototype\server\ scripts\game.cs to include these lines (bold lines are new):
184

~-

Basic Game Classes

Chapter 6

exec("./MazeRunner/coins.cs H ) ; II MazeRunner exec("./MazeRunner/fadeblocks.cs H ) ; II MazeRunner exec("./MazeRunner/fireball.cs H ) ; II MazeRunner

Block Geometry The blocks will both have the same geometry, namely a single render mesh and a single collision mesh. To see this geometry, open the file \MazeRunner\prototype\ data\MazeRunner\Shapes\MazeBlock\blockA.ms3d using MilkShape. You will see that this model has a render mesh named "blockO" and a single collision mesh named "collision-l ". To enable reskinning, we need to do something special with the model's skin.

Reskinning
Still in MS3D, if you look at the material named "skin", you will see that we are using a texture named "base.skin.png". (It only shows as "base" on the MS3D button, but trust me, the file is named "base.skin.png".) By using a skin with this name, we will later be able to change the skin on this model. To clarify, the rules for reskinning are as follows.
1. Skin your mesh with a texture named "base.XYZ.png", where XYZ can be

anything you choose. The important thing to notice is that "base" is at the start of the skin. This tells TGE that this is a reskinnable mesh. 2. Create as many extra textures as you need, as long as they have the name "LMN.XYZ.png", where XYZ is the same name from step 1 and LMN is a name to make your texture name unique. 3. Reskin a shape at any time by writing the following code.
%obj.setSkinName( "LMN H
);

Figure 6.3
Making material selfilluminating.

The above code tells the mesh to use the texture "LMN.XYZ.png" instead of "base.XYZ.png".

Self-Illuminating
Because we are using a sort of cartoon/platform theme in our game, we will want all of the blocks to self-illuminate. This means that they will not be affected by the in-game lighting. To do this, we simply choose the self-illuminating option when exporting (using the DTS-Plus exporter). Please see Figure 6.3. Datablocks All right, these base blocks are pretty much good to go. Let's just create some datablocks and we can move on.
185

II1I

I:dI

Part III

Game Elements

Fade Blocks Datablock


For the fade block, please open the file \MazeRunner\prototype\server\scripts\ MazeRunner\fadeblocks.cs. In this file, find the following lines of script.
datablock StaticShapeData( FadeBlock ) { category = "FadeBlocks"; shapeFile = "-!data!MazeRunner!Shapes!MazeBlock!blockA.dts"; islnvincible = true;
};

Not shown, but present in the completed copy of this file (as written by me), there is another bit of code at the top. It is a reloader. Reloaders are little scripts that are used to reload the file, thus reloading the datablock definitions and any scripts in the file. In single-player mode, I use reloaders to reload files I have changed while the mission is still running. This way, I can make minor tweaks to scripts, etc., and not have to reload the entire mission. The reloader for the fadeblocks.cs file is as follows.

function rldfb () { exec(".!fadeblocks.cs");

This datablock has the following attributes. These blocks will go in a special group (in the Creator tree) named FadeBlocks.
It loads the mesh for the model we just discussed. It is invincible and thus takes no damage. We want this so that fireballs

striking a fade block will not damage it.

Fireball Blocks Datab/ock


For the fireball block, please open the file \MazeRunner\prototype\server\ scripts\MazeRunner\fireball.cs. In this file, find the following lines of script.
datablock StaticShapeData( FireBallBlock ) { category = "FireBallBlocks"; shapeFile = "-!data!MazeRunner!Shapes!MazeBlock!blockA.dts"; islnvincible = true;
};

As you can see, this datablock is identical (except for the name) to our fadeblock datablock. The behavior differences are entirely script based, and the

186

l-

Basic Game Classes

Chapter 6

reason we need another datablock is because, later, we will want to associate some methods with the fade block but not the fireba]] block.

6.5 TSStatic
This class is not a child of the ShapeBase hierarchy and does not use a datablock. It is used for any shape that will not be moved and will not need to be animated or make sounds.

6.5. 1 TSStatic Features


TSStatic has the following features. Basic rendering Simple collision TSStatic objects are very lightweight objects used to render meshes that are used for scene filling and to render meshes that do not need any of the features provided by the ShapeBase hierarchy.

6.5.2 Rendering
TSStatic will render a standard mesh (just like a ShapeBase derivative), but it cannot play any animations, reskin, cloak, etc. It just renders.

6.5.3 Collision
If you wish for these shapes to be colfideable, you must create a collision mesh as part of the model. This gives you the freedom to choose which items are coliideable and which are not. The shape supports multiple collision meshes. The TSStatic object will not register collisions, nor will it respond, but all other active coUiders (objects that can collide with other objects) will register their own collision with a TSStatic object.

6.5.4 Creating TSStatic Shapes


Creating and placing a new TSStatic shape is simplicity itself:
%obj = new TSStatic () { position "0 0 0" rotation = "1 0 0 0"; scale = "1 1 1"; shapeName = "-/data/Shapes/Lessons/Genera1LessonShapes/egg.dts n
};

187

Part III

Game Elements

6.5.5 Moving and Scaling


The basic posi tion, rotation, scale, and shapeName datablock fields behave in the same way as they do for a ShapeBaseData-derived object. Also, being a a child of SceneObject, the TSStatic class can be scaled using setScale () and moved/reoriented using setTransform ().

6.5.6 Maze Runner Lesson #5 (90 Percent StepJMaze Blocks


The primary geometry of our maze consists of blocks and groups of blocks. Later, when we discuss the level-loading scripts (Section 9.5.10), we'll talk about how these blocks are placed. For now, we will restrict ourselves to the creation of these blocks. The maze blocks share the same geometry and skin setup as the fade blocks and fireball blocks from Lesson #4 (Section 6.4.4). So, if you have not completed that lesson, please do it first.
Block Geometry

In addition to the single-block geometry we produced for the prior blocks, we need several additional variations for the maze blocks. In theory, we could build our entire level out of single blocks. However, I don't advise this as we do pay a penalty (network and processing) for each block in the scene. So, knowing in advance that we will have various structures in our levels combining several blocks, we will make a few larger meshes. This way if we need an area the size of say nine (3 x 3) blocks, we can place just one big block. If you look in the \MazeRunner\prototype\data\MazeRunner\Shapes\ MazeBlock directory we created earlier, you will see that there are blocks A through J. The geometries of those shapes can be seen in Figure 6.4. There are four square blocks and three each of the horizontally oriented and vertically oriented linear blocks. It may not be apparent immediately, but with these
1J1x1

Figure 6.4.
Geometries of blocks A through J.

III::

._-=.':::::I =-= =
I

== =

--

--

I ___ L

IIII
I
I

D,71: 7

II'
II,

I II

7i I

188

1--

Basic Game Classes

Chapter 6

blocks, we can create symmetrically laid out levels without needing to reorient the blocks at placement time.

Placing Blocks
We aren't writing the code to place these blocks yet, but when we do, it will look something like this:
new TSStatic() ( shapeName = "-/data/MazeRunnerIShapes/MazeBlock/block" @ %blockType @ ".dts"; position = %actX SPC %actY SPC $CurrentElevation; scale = "1 1 1";
);

This code snippet is actually from our level builder, and as you can see, we will be dynamically selecting the mesh to use as well as calculating the position as we place the block.

Examine the Blocks


This guide does not discuss modelling, nor does it cover the various modelling tools. However, as the blocks have already been created for you, I suggest that you examine a few to see how they are constructed. Pay particular attention to blocks E through J. Don't forget that all of the maze blocks have been copied over to our data directory already at "\MazeRunner\prototype\data\MazeRunner\Shapes\ MazeBlock". You can open any of the block models (* .ms3d) with a copy of MilkShape 3D.

6.6 ShapeBaselmageData 'Images)


ShapeBaselmageData objects (commonly referred to as just Images) are lightweight objects that can only be attached to ShapeBase objects and are used to render, animate, and script weapons, backpacks, flags, and other mounted objects. These are used instead of ShapeBase objects because they require much less network bandwidth to manage and transmit to clients. In addition, they supply a unique set of capabilities.

6.6. 1 ShapeBaselmageData Features


ShapeBaselmageData has the following features.
Rendering Environmental mapping

189

--------

Part III

Game Elements

Light emission POV dependent rendering Camera offsets

Mounting Can mount to any ShapeBase class or child Engine-event transitions Timed transitions User-defined transitions Hooks for lighting, particle emission, sounds, and animations Scriptable state machines Up to 31 user-defined states Engine-event transitions Timed transitions User-defined transitions Hooks for lighting, particle emission, sounds, and animations Physical parameters Mass Collisions No collision box

6.6.2 Rendering Options


POVand Offsets
As noted above, ShapeBaselmage supplies some fields for manipulating how an image is rendered.
firstPerson. If true, this image is rendered in both 1st POV and 3rd POV. It is sometimes useful to not render an image in 1st POV, and this field

allows you to disable rendering if necessary.


eyeOffset. When rendering an image in 1st POV, the image may not be

in what looks like the correct position. To remedy this for the player's view only, you can apply this offset to adjust the position of the weapon in the player's first person view of the world. This does not affect third-person rendering and is not seen by other players.
eyeRotation. Similarly to position, when rendering an image in first POV,

the image may not be in what looks like the correct orientation. To remedy this for the player's view only, you can apply this rotation to adjust the rotation of the weapon in the player's first-person view of the world. This does not affect third-person rendering and is not seen by other players.
190

'-

. . _.

- - - _.....

_---

Basic Game Classes

Chapter 6

Lighting
When mounted, an image can emit no light, a constant, or a pulsing light. This lighting feature is controlled by the following ShapeBaseImageData fields.
lightType. This string specifies what type of light the image emits: NoLight, ConstantLight,orpulsingLight.

lightColor. This three-element floating-point vector determines the color of the light. Individual elements must be in the range [0.0, 1.0] and represent the red, green, and blue components of the light color in that order. lightRadius. This floating-point value specifies the radius of the light sphere. lightTime. For pulsing lights, this integer value specifies the light's period in milliseconds.

6.6.3 Mounting
ShapeBaseImageData has three parameters affecting how the image is mounted.
mountPoint. This field is a numeric value in the range [0, 31] and corre-

sponds to a numbered mount point on the receiving shape. When an image is mounted to a shape, it is the responsibility of the image mesh to supply a specially named joint/node: mountPoint. When instructed to mount this image to a shape, TCE will calculate a mount transform using the receiving shape's numbered mount point and the image's named mountPoint.
If the numbered mount point does not exist in the receiving shape's mesh, the receiving shape's centroid will be used instead (in the player, this further offsets to the foot position).

If the image does not specify a mountPoint joint/node, its centroid will be substituted for that part of the mounting transform calculation. offset. This field is used to apply a position offset to the mount transform. rotation. This field is used to apply a rotation offset to the mount transform. This is especially handy when a weapon mounts at the wrong angle. This can easily happen if a player's mounW joint/node has gotten rotated during the creation or animation/posing process. Instead of attempting to resolve this problem in the mounting shape's mesh/skeleton (which can be tricky), just apply a rota tion to the image's mount transform.

You can only mount one instance of an image to a shape. More than one instance of the same ShapeBaselmageData, mounted to the same ShapeBase object, violates the engine mounting protocol.

6.6.4 Weapon-Related Features


We will not be dicussing weapons-related ShapeBaselmageData features here; however, all fields and methods are listed and documented in the appendix that comes with this guide.
191

~._---

Part III

Game Elements

6.6.5 State Machines


The most powerful (and to some degree the most complicated) facet of ShapeBaselmageData-derived images is their state machines. Each image can define a unique state machine with up to 31 states. These state machines are designed to be used with weapons but can be used for other purposes, too. Before proceeding, you should already understand what a state machine is. However, if you do not, the following summary may help. A state machine, in the context of a game engine, is a mechanism by which action-reaction events can be scripted or programmed. Essentially, an object (in this case a weapon image) starts in a known state. Based on predefined input events, the state machine may transfer to a new state. Each state has a purpose, although the purposes may be varied and can include playing an animation, playing a sound, running a script, etc. Additionally, each state may define multiple exit paths.

States
There are 30 fields associated with the various states, state transitions, state triggers, state actions, etc., that the Image state machine handles. Because most of these are associated with weapons, a complete discussion of these states is not given here. For now, we will focus on how the basic state-machine mechanism works. A listing of all the state fields appears in Table 6.2. A complete listing of states with descriptions is provided in the Fields and Methods appendix.
Table 6.2.

State fields.
sta~eAllowImageChange

stateDirection stateEmitterNode stateFire stateName stateSequenceRandomFlash stateSpinThread stateTransitionOnTriggerDown stateTransitionOnLoaded stateTransitionOnNotWet stateTransitionTimeout

stateEjectShell stateEmitterTime stateIgnoreLoadedForReady stateRecoil stateSequence stateTimeoutValue stateTransitionOnTriggerUp stateTransitionOnNoAmmo stateTransitionOr.Wet stateWaitForTimeout
I

stateEmitter

..

s~ateEnergyDrain

sta~eLoadedFlag

sta~eScript

stateSound stateTransitionOnNotLoaded stateTransitionOnAmmo stateTransitionOnNoTarget stateTransitionTarget

192

Basic Game Classes

Chapter 6

Defining States
We can define up to 31 states in our Image state machines. To do so, we simply name them as follows.
datablock ShapeBaselmageData( SimpleStates ) {

/ / ... stateName[O] stateName[l] stateName[2] stateName[3]


};

= "GreenLight";

"Preactivate";
Figure 6.5. Four named states.

"YellowLight"; "RedLight";

This code produces four named states: Preactivate, GreenLight, YellowLight, and RedLight. So far, we haven't connected the states, so we don't know how the machine "flows." Thus, our state picture would look something like Figure 6.5.

_ ...... ._----- Y.""''''''


1 S."ond.

Required States?
If it isn't obvious by the names of these states, we'll be making a traffic light with this state machine. However, you may wonder at the choice of state zero
(Preactivate).

Generally speaking, you must define a state for the machine to start in. Traditionally, that state is named Preactivate and is numbered zero. This state will not execute scripts, animations, or sounds. The most it can do is give TGE a place to start the machine and wait for a bit before transitioning to the first active state.

Transitioning
There are several ways to transition from one state to another. In addition, we can make multiple paths out of anyone state. For now, we'll focus on making a single transition for each state. We want these transitions to look like Figure 6.6. Furthermore, the transitions we would like to use for our stoplight are timed transitions. It is possible to make states timeout and then transition to a named state. For example, if we wanted to create this sequence:

--

Figure 6.6. Transitions for stoplight.

Preactivate -7 GreenLight (immediate) GreenLight -7 YellowLight (3 seconds) YellowLight -7 RedLight (2 seconds) RedLight -7 GreenLight (3 seconds)

._,.------~y~

/\

repeat ...

193

Part III

Game Elements

we would code our state machine as follows.


Figure 6.7. Timed transitions for stoplight.

datablock ShapeBaselmageData( SimpleStates / / ... stateName[O] = "Preactivate"; stateTransitionOnTimeout[O] = "GreenLight"; stateName[l] = "GreenLight"; stateTransitionOnTimeout[l] = "YellowLight"; stateWaitForTimeout[l] = true; stateTimeoutValue[l] = 3.0;

'7

",,"d.1 \.",O.d.

....,... ....... - - - - - f

\ r._
};

stateName[2] = "YellowLight"; stateTransitionOnTimeout[2] = "RedLight"; stateWaitForTimeout[2] = true; stateTimeoutValue[2] = 2.0; stateName[3] = "RedLight"; stateTransitionOnTimeout[3] = "GreenLight"; stateWaitForTimeout[3] = true; stateTimeoutValue[3] = 3.0;

This produces the state machine in Figure 6.7. Making States Do Work Great! Now, we have a state machine that will transition: Preacti va te ~ GreenLight ~ YellowLight ~ RedLight ~ GreenLight ~ ... ad infinitum. Wait a second, though. It isn't doing any work! Well, as with transitions, state machine states can do lots of different kinds of work. They can run scripts, play sounds, trigger particle emitters, etc. Pretty cool. Now, for our example we want the stoplight to change the light colors repeatedly. How the heck are we going to do that? Here are some ideas. Run a script and change the image skin? Nope. Images don't support skin switching. Run a script and replace the image itself? Naw. You could do this, but it's really messy. Use an IFL and switch animation states? Yeah. That's what we'll do. IFL is the acronym we use when talking about an image file list. A TGE supported feature we have not yet talked about is animated textures. It is possible to create a model that takes a base image and then changes skins using
194

Basic Game Classes

Chapter 6

an animation sequence. It is kind of like the ShapeBase skin-switching idea, but it is more flexible and can get higher frame rates than that method.

Running Animations
So, we've chosen to run an animation to change the light. How do we do it? Like this.
datablock ShapeBaselmageData( SimpleStates ) { / / ... stateName[O] = "Preactivate"; stateTransitionOnTimeout[O] = "GreenLight"; stateName[l] = "GreenLight"; stateTransitionOnTimeout[l] = "YellowLight"; stateTimeoutValue[l] = 3.0; stateSequence[l] == "GreenLiqhtOn"; stateName[2] = "YellowLight"; stateTransitionOnTimeout[2] = "RedLight"; stateTimeoutValue[2] = 2.0; stateSequence[2] = "YellowLiqhtOn"; stateName[3] = "RedLight"; stateTransitionOnTimeout[3l = "GreenLight"; stateTimeoutValue[31 = 3.0; stateSequence[3] "RedLiqhtOn";

};

This example tells TCE to switch the animation sequence for this image to the named states when the state machine transitions into the state. At this point, our work is done. We have defined our state machine.

Running Scripts
Because you might want to do more than just run an animation, I'll get you started on running scripts and then let you investigate the other states yourself. To run a script when we transition into a state, we do the folloWing.
datablock ShapeBaselmageData( SimpleStates ) { / / ... stateName[O] = "Preactivate"; stateTransitionOnTimeout[O] = "GreenLight"; stateName[l] = "GreenLight"; stateTransitionOnTimeout[lj = "YellowLight";

195

Part III

Game Elements

stateTimeoutValue[l]

3.0;

stateScript[l] II
};

= "doSomething";

Then, we must be sure we've created a function doSomething () in the namespace of our image.
function SimpleStates:doSomething( %this ) {

II
}

Physical Parameters
If tracking physical properties is important to your game, then it will be worth noting that ShapeBaseImageData provides a mass field to represent the mass of a mounted shape. It can be extracted directly from the ShapeBaseImage mass field. There is no getMass () equivalent. Mass can be used for various purposes, ranging from calculating a player's cumulative mass (with weapons, etc.) to determining if a weapon is too heavy for the player to carry or mount.

Collisions
An interesting thing about ShapeBaseImageData images is that they do not have a collision box. Therefore, no collisions occur. However, you may notice that very large weapons will push back when they are mounted and the weapon is pushed up against an interior or another object with a collision mesh. This pushing back occurs if the mesh that the image uses defines a special node named retractionPoint. The engine will see that retractionPoint has collided with the boundary of a collision mesh and push the weapon back to prevent it from penetrating walls and other objects. If you do not want this behavior, simply do not create this node in your models.

Image Animations
Images support multiple animations, mostly related to weapons, but there is one animation sequence that is somewhat generic, namely the ambient animation.

ambien t Sequence
If you wish, you may define a cyclic animation for images, named ambient. This sequence will play continuously. It may be blended or nonblended depending upon your needs.

196

Basic Game Classes

Oldpter 6

6.6.6 Interiorlnstance
This section of the chapter is mostly informational. Except for basic rendering and placement. ail features related to interiors require art skills not discussed in this guide. However, I want you to know what features are available to those who are interested in learning more about the "art" aspects of interiors. You may skip this section if you are not interested in this kind of discussion.

Terrain Inside
When you create a new interior (that is, when you place one in the world), you may set a special field named showTerrainlnside to true or to false. If this field is set to true, terrain will show up inside the interior. If this field is set to false, all rooms bounded by portals will turn off any terrain that might normally poke throught the floor of the bounded room(s). Remember that, if there are no portals bounding a room, the showTerrainlnside field will have no effect.

Activating and Deactivating Lights


TGE supports the ability to enable and disable individual lights in an interior. To check for these triggerable lights, use the following method.
%mylnterior.echoTriggerableLights()i

Or, if you already know what your light names are, you can activate and deactivate the lights as follows.
%myInterior.activateLight( lightName )i %mylnterior.deactivateLight( lightName )i

Using alarmMode
TGE supports another Interior lighting feature. This is a sort of hangeron from the days of IHhes. In THbes 2, when a power supply got knocked out. the lights in an interior would turn red. This was the alarmMode setting for that light; Le., you would have a normalMode and an alarmMode light in the same spot, and alarmMode of the InteriorInstance would dictate which light was on. The method to switch the alarmMode on and off is as follows.
%mylnterior.setAlarmMode( "On" )i %mylnterior.setAlarmMode( "Off" )i

Notice that, instead of Booleans. this method takes the actual strings "On" and "Off".
197

Part III

Game Elements

Levels of Detail
In order to create an interior that supports multiple levels of detail (LOD), you must make several instances of the same interior and manually modify them to have less and less detail. Then, following the instructions for you particular exporter, export these interiors together. TCE can then use this multiple LOD interior. It will automatically modify the LOD for you.

Manual LOD
You may also manually set the LOD for an interior using scripts. First, you can query the Interiorlnstance for the number of levels it supports.
II Returns number of LOO levels in this OIF %myInterior.getNumOetailLevels();

Then you may select one of those levels.


%myInteior.setDetailLevel( 0 ); II Set LOO to 0

Selecting a nonexistent LOD will default to LOD O.


I'll mention it again later, but if your player shape is set to not render while in 1st POV, you will not be able to see yourself in mirrors while in 1st

Disabling LOD
You may wish to disable LOD changing for various reasons. To do so, simply set the global variable $pref:: Interior: :detailAdjust to false.

Mirrors

POV. To fix this, simply A very cool feature supported by Interiorlnstance is the mirror object. Using enable rendering of h' BSP too I s supporte d b y or . . . IOto e vanous lorque, SImp Iy d rop a mIrror entIty. I the payer shape .. I while in 1st POV. ~ your model and VOlW.

~
~

Yes, before you ask, mirrors will reflect the outside world too, not just the inside of an interior and its contents.

6.7 Summary

We started this chapter restating the fact that Torque has two broad categories of model rendering objects, the shape and the interior. We spent a short time discussing the general purpose of the shape category and then listing the various shape classes as well as mentioning their primary uses. Next, we briefly discussed the purpose of the interior category. Having finished summarizing and bullet listing, we jumped into a discussion of shapes and the ShapeBase hierarachy. First on this stop were the base classes ShapeBase and ShapeBaseData. We covered the primary features supplied by these classes, giving detailed descriptions for rendering, damage,
198

~-.-

Basic Game Classes

Chapter 6

energy, physical parameters (like mass and density), eye transforms. shape animations. sounds attached to shapes, shape and image mounting, and the deployment helper functions. Next up on the list of shapes were Item and ItemData. Again, a detailed discussion of features followed. We covered the cool rendering. lighting, physics, and collision features. ending with a short discussion of dynamic typing. Along the way we stopped and created some assets (coins) for our game. After Item and ItemData came a very short discussion of the simple StaticShape and StaticShapeData classes, which are basically concrete implementations of the virtual ShapeBase and ShapeBaseData classes. TSStatic came next. We learned that this is not a derivative of ShapeBase but rather a lightweight class used for rendering models that don't need a lot of features besides basic rendering and simple collisions. Here, we made two new resources for our game. the fade block and the fireball block. Really rolling now, we jumped into an introductory talk about the ShapeBaselmageData class. We learned about its various features, including rendering, mounting, per-image state machines, physical parameters, and collisions (the lack of them). As an introduction to the image state machine, we implemented a simple stoplight using the image state-machine features. After images, we moved on to a short discussion of interiors. Here, we learned about terrain interactions, lights and lighting, LOD, and mirrors. Overall, this was a fairly short chapter. but it still packed in a lot of useful information that you may wish to refer to again. Additionally, to supplement this information, there is a complete appendiX that documents all of the console classes, including shapes and interiors. The descriptions in the appendix are succinct but complete. covering all fields, methods, and callbacks for every console class we discuss.

199

Chapter 7 Gameplay Classes


7.1 Gamep'ay?
Gameplay is probably one of the most nebulous terms (besides fun) used

when discussing games and game design. For the purpose of this chapter (and subsequent chapters), we are less interested in the definition of gameplay than we are interested in the elements of gameplay. One such element is interaction. In fact, it is safe to say that gameplay cannot exist without interaction. Futhermore, I will propose that interaction is in fact a major element of gameplay. To that end, this chapter focuses on the primary classes that are used to enable and implement interaction within our games. The following classes are discussed in this chapter.
Camera. This provides us with our view on the world. Player. This class supports a variety of features and is intended to be used to represent bipedal, multipedal, and other types of avatars. Vehicles. TGE provides three implementable vehicle classes: FlyingVehicle, HoverVehicle, and WheeledVehicle. These, like the Player class, are meant to be used as avatars or as transport for the avatar.

This chapter also focuses on a topic that is not centered in anyone class, but operates on and with several classes to provide a very commonly found interaction construct, the inventory. Inventories form the basis for common game interactions, namely picking up, storing, using, and dropping objects. So, the last topic in this chapter is about an inventory system that is supplied with the guide. It is a standalone inventory system that (unavoidably) utilizes some scripting topics that we have not yet discussed. Thus, you may wish to stop before reading that part of the chapter and quickly review Chapter 9, "Game Setup Scripting" and Chapter 10, "Gameplay Scripting." Then, when you are properly briefed, return here and finish the chapter.

7.2 Camera and CameraData


Together and in cooperation with other gameplay classes, Camera and CameraData define our game view. Game view is a generic term I am using to consolidate several view-related topics. Some of these topics are listed in Table 7. I.
201

Part III

Game Elements

Table 7.1. Game view topics.

Topic
Point of View (POV)

Desatptlon
There are two basic POVs we are concerned with. First person, which is the case where the camera is looking out of the player's head or eye. Third person, which is the case where the camera is looking down on the player from a distance. Field of view is a camera term that has to do with the angle of coverage (or angle of view). When we talk about FOV in TGE, we are measuring an angle on either side of an imaginary vector coming straight out of the camera and pointing into the world in the direction we are looking. For an FOV of 45 degrees, our view angle is 90 degrees (45 degrees to each side of the vector). If we think for a moment, we'll come to the conclusion that an FOV of 180 degrees would mean we can see all around the point of Viewing (360 degrees of coverage). A standard FOV for first-person views is 90 degrees (180 degrees of coverage) or less.

Field of View (FOV)

Control Object Free Camera Zooming

In Torque, there always has to be a control object, scoping our position in the game world and thus allowing the engine to determine what is visible to us. Any of the classes in this chapter are approriate control objects. In addition to having the camera tied to one of the other gameplay classes, it is possible for the camera to roam freely, in effect taking over the role of avatar (although without any visible representation, of course). What we call zooming in TGE is actually a foreshortening of the FOV. That is, as our FOV decreases, it seems visually as if our view is zooming in and bringing far objects nearer. Likewise, as our FOV increases, objects seem to move away.

7.2. 1 Camera and CameraData Features


Camera and CameraData have the following features. Point of view Field of view Render scoping The Camera class is really quite lightweight and derives almost all of its behavior from the ShapeBase class. In fact, as you will soon discover, there are times when a camera is not even required, and another ShapeBase-derived class can handle the Camera class's duties. However, let's not get ahead of ourselves. Instead, let's first learn more about the game view.

7.2.2 Parts of the Whole


In order to control the current game view, we will (at times) involve several classes' fields, methods, console functions, and console variables. Table 7.2 summarizes all TGE elements involved with game view.

202

GamepJay Classes

Chapter 7

TGIIIenI8Id:

DescrIption

.~

Table 7.2.

Engine Defined Console Functions setDefaultFov( defaultFOV )


Sets default FOV to specified value if it is between the current min/max. Sets current FOV to specified value if it is between the current min/max. Sets the zoom speed (milliseconds per 90 degree FOV delta).

TGE elements involved with game view.

setFov( defaultFov )

setZoomSpeed( speed )

Globals $cameraFov
Global variable showing current camera's current FOV. Updated every frame. Defines current speed of free camera in world units per second. Set in scripts, used by engine. A global variable used solely for tracking the current first-person status of the camera.

$camera: :movementSpeed

$firstPerson

GameConnection:: Console Methods setFirstPerson ( FirstPerson )


Sets this game connection to first- or thirdperson view based on the Boolean value of the argument firstPerson.

Camera:: Console Methods setFlyMode() ;


Sets camera to free-camera (fly) mode; i.e., camera is not attached to an object. Attaches camera to arbitrary ShapeBase object and causes it to be in orbiting mode.

setOrbitMode( orbitObject, transform, minDistance, maxDistance, curDistance, ownClientObject ) ;

ShapeBaseData:: Fields cameraDefaultFOV


Defines default FOV for camera "viewing through" this shape. Defines max distance for camera "viewing through" this shape. Defines max FOV for camera "viewing through" this shape. Defines min distance for camera "viewing through" this shape. Defines min FOV for camera "viewing through" this shape.

cameraMaxDist

cameraMaxFOV

cameraMinDist

cameraMinFOV

203

Part III

Game Elements

Table 7.2 (continued).

TGE Element
firstPersonOnly

Description
Declares that the camera attached to this shape may only view in first person. Declares that the camera attached to this shape should use the shape's field parameters for FOV and Distance. This tells the camera to use the controlling object's camera transform.

observeThroughObject

useEyePoint

ShapeBase:: Methods

setCameraFOV( fov

) ;

Set FOV to new value fov. Automatically clamped to curent min/max. Does not take effect immediately, only when camea switches modes. Returns current camera FOV for this shape, which mayor may not be the same as the current FOV.

getCameraFOV();

PlayerData:: Fields

maxFreeLookAngle

Total radians of rotation (about player) allowed when in "free look" mode. Maximum upward rotation of camera about player in radians. 0.0 is straight forward. 1.57 is straight up.
I I

maxLookAngle

minLookAngle

Minimum downward rotation of camera about player in radians. 0.0 is straight forward. -1.57 is straight down.

VehicieData:: Fields

cameraDecay

Rate at which camera returns to default position (post-lag). Measured in seconds (floating point). How much the camera lags a vehicle that is accelerati ng. camera's vertical offset from vehicle in world units.

cameraLag

cameraOffset

Model Nodes

eye

Location for first-person camera to attach to this shape. Location for third-person camera to attach to this shape.

cam

204

Gameplay Classes

Chapter 7

As can be seen by this list, setting up the game view can be somewhat complicated. We will examine each of the TGE elements individually, in the order listed in Table 7.2. Then, we will take these elements and combine them (by example) into commonly encountered game views.

The Control Object (An Aside)


Before we can proceed, we have to briefly discuss the control object. As previously noted, the client requires that there be (at all times) a control object. This object is used to determine many things, but in the context of game view, we only care whether the camera is the control object or another shape is the control object. Changing the control object is as simple as a single function call. For an example of this, let's look at the camera-toggling command that comes with both the TGE Demo and the GPGT Lesson Kits,
function serverCmdToggleCamera(%client) { if ($Server: :ServerType $= "SinglePlayer") %control = %client.getControlObject(); if (%control == %client.player) { %control = %client.camera; %control.mode = toggleCameraFly;
}

The sampler that comes with this guide has a lesson that explores game views by allowing you to mix and match different player, vehicle, and camera settings. This lesson is named "Game Views."

else { %control = %client.player; %control.mode = observerFly;


}

%client. setControlObject (%control) ;

As can be seen, by simply passing the handle of a ShapeBase object (or a camera) to the method setControlObject (), we can change the current control object. The control object affects the game environment in several ways, but for the most part these are advanced topics. For now, we will limit our discussion to the differences between having a camera, a player, or a vehicle as the control object.

FOVand Zoom Console Functions


There are two FOV console functions and one zoom function. The setDefaul tFOV () and setFOV () methods do basically the same thing. They will change the current FOV to a new FOV. This change will occur either immediately or over a short duration (based on the current zoom speed).
205

Part III

Game Elements

However, there is a slight difference in the way these two functions operate. If the FOV is currently adjusting and we call setDefaultFOV (), it will be ignored. On the other hand, calls to setFOV () are never ignored. The setZoomSpeed () function is used to set the time it takes to zoom per 90 degrees of Fay. Here are some examples.
setZoomSpeed( 0 setFOV( 45
) ;
) ;

II II II
) ;

Transition FOV's immediately Set FOV to 45 degrees (takes network latency time only) Transition of 90 degrees FOV requires 4 seconds Set FOV to 90 degrees (takes two seconds)

setZoomSpeed( 4000

II II II II

setFOV( 90

) ;

The Globals
There are three globals that may at times be involved in game-view decisions. The first of these globals is $cameraFOV. It should be treated as a readonly global used to reflect the current FOV settings as the engine sees them. The second of these globals, $camera: : movementSpeed, can be read and modified. It is used to adjust how fast the camera moves in free-fly mode. The third and last of these globals, $firstperson, can also be read and modified. However, it changed its behavior after version 1.3. In version 1.3, changes to this global change the POV between 1st and 3rd pay. Starting in version 1.4, this global is used by scripts to track the current POV, but changes to the value do not affect the behavior of the engine. Only calls to GameConnection: : setFirstPerson () do this, as you will see shortly.

GameConnection: :setFirstPerson()
As I just mentioned, in versions 1.3 and earlier, the 1st and 3rd POV transition is controlled by the global variable $firstPerson. In versions 1.4 and later, this functionality is handled by the console method setFirstperson () . For either the TGE Demo or the GPGT Lesson kit, if you search the file" jclient/ scriptsjdefault.bind.cs" you will find the following code:
TV

function toggleFirstPerson( %val ) if ( %val ) ( $firstPerson = !$firstPerson; ServerConnection.setFirstPerson( $firstPerson );

206

GamepJay Classes

Chapter 7

This code now uses $firstPerson to track the current POV (lst or 3rd) and tells the server to switch to whatever POV we have selected.

Camera Methods
When the camera is not attached to a shape (when it is the control object), it can be in one of two modes. Free-fly mode. Camera is free to fly anywhere in the world. Orbiting mode. Camera is "tethered" to an object and follows the object if it moves. To clarify these concepts, let's look at some sample code.
%client.setControlObject(%camera); %camera.setFlyMode(); $camera: :movementSpeed = 25; II limit camera velocity to 25 world units/s

The above code makes the camera the control object, places the camera in free-fly mode, and then sets the camera's current movement rate to 25 world units per second, using the global $camera: : movementSpeed.
%client.setControlObject(%camera); %camera.setOrbitMode( %player , %player.getTransform() 10.0 , 15.0, 10.0 );

The second piece of code makes the camera the control object and then tethers it to the player, where it will be allowed to orbit. It is told to orbit the player and use the camera's current transform. Furthermore, the orbit "tether" is limited to a length of between 10 and 15 world units, starting at 10 world units.

ShapeBaseData Fields
ShapeBaseData has eight fields that contribute to our game view.

The FOV and Distance Fields


The first five ShapeBaseData fields are related to the FOV and viewing distance. We may specify a default FOV and constrain FOV within a minimum and a maximum bound by specifying degree values (between -360.0 and 360.0) for cameraDefaul tFOV, cameraMinFOV, and cameraMaxFOV, respectively. We may also define a minimum and maximum distance between the "camera" and the current control object by setting cameraMinDist and cameraMaxDist, respectively. 207

- - - - - -------

Part III

Game Elements

observeThroughObject
This field is used to tell the engine which FOV and distance values to use when a camera is in orbit mode. When the engine detects that the camera is in orbit mode, it will query the object that the camera is orbiting and use that object's datablock for the observeThroughObj ect field setting. If the field is set to true, the engine will place the camera directly behind the shape it is orbiting and use that object's datablock's FOV and distance values. If the field is set to false, then the engine will use the FOV values in the camera's datablock and the distance value specified in the setOrbit () call.

firstPersonOnly
This field is used to restrict the view (when a camera is attached to a shape) to 1st POV only. This is done by setting the control object's datablock field firstPersonOnly to true. If this field is false, the camera is allowed to assume either 1st POV or 3rd POV.

useEyePoint
Sometimes, the player will mount another shape, such as a vehicle. At times like this, we may want the camera to now use the vehicle's camera nodes (eye and/or cam). By setting the useEyePoint field to true, we are instructing the engine to do this. If this field is false, the engine will continue to use the FOV and distance values it was already using (in the vehicle's datablock). ShapeBase Methods There are two FOV methods scoped to the ShapeBase class. These are used for setting and getting the current FOV of a shape. I suggest, however, that you do not use the setcameraFOV () method. It almost always gets overridden or ignored. The getCameraFOV () method is useful, though, because it is the only way to get the current FOV for a non-camera object. Remember that, for the current camera, you can just observe the global variable $cameraFov.

PlayerData Fields When the camera is attached to a player and in 1st or 3rd POV, we can restrict the angles (pitch and yaw) that the camera may assume.

minLookAngle/maxLookAngle Witch) By setting the minLookAngle and maxLookAngle fields, we can restrict the up-down rotation (pitch) of the camera. These fields take values in radians. In the following example, the camera can pitch all the way around in either direction.
208

GamepJay Classes

Chapter 7

datablock PlayerData( testAvatar8: testAvatar2 ) { minLookAngle -3.141593; maxLookAngle = 3.141593;


) ;

In the following example, the camera can pitch straight down to straight up.
datablock PlayerData( testAvatarB: testAvatar2 ) { minLookAngle -1.57; maxLookAngle = 1.57;
) ;

maxFreeLookAngle(Yaw) In addition to pitch, we can limit the left-to-right (yaw) of the camera when it is attached to a player. This is done by setting the maxFreeLookAngle field. Again, this field takes values in radians. In this example, the camera can yaw a complete 360 degrees in either direction (left or right).
datablock Player Data ( testAvatar8: testAvatar2 ) { maxFreelookAngle = 3.141593;
};

Notice the name of this field: maxFreeLookAng Ie. The implication is that this (also) controls the angle of free-looking. Free-looking is a special mode where the camera is in 3rd POV and it rotates around the player without rotating the player's body. The head may rotate if an appropriate animation is provided (see Section 7.3, "Players"). This free-looking is used for looking around without changing aim-point and for other purposes. To get into free-look mode, the camera must be in 3rd POV, then we set the global variable $mvFreeLook to true. When this variable is false, the camera and player will behave normally.

VehicieData Fields Besides players, the camera can be attached to a vehicle. The vehicle datablock adds a few more fields to make the camera behave nicely. For example, when a vehicle accelerates, the camera can lag behind. Then, the camera can catch back up. Also, we can choose the current distance between the camera and the node on the vehicle it is currently attached to. Lagging and offset are controlled by three VehicleData fields.

209

Part III

Game Elements

Lagging
To enable lagging, we set the VehicleData field cameraLag to a positive value. Likewise, we must set the cameraDecay value to a positive value.
datablock WheeledVehicleData( testVehicle ) {

II

...

cameraLag = 0.1; II Lags by 10% of delta while accelerating cameraDecay = 0.75; II Recovers 75% of lag per second
};

Offset
We can force the camera to be vertically offset from the camera node it is attached to by setting cameraOffset.
datablock WheeledVehicleData( testVehicle

II
};

...
=

cameraOffset

1.5; II Vertical offset of 1.5 world units

Your Meshes and Special Nodes


We'll touch on this again when we talk about players and vehicles, but if you want the camera to attach properly to a model, the model must have two specially named nodes (joints): eye and cam. eye is the 1st POV camera mount, and cam is the 3rd POV camera mount. If one or both of these is not present and the current POV needs it, the default mounting point will be the centroid of the shape.

7.2.3 Basic Game Views Cookbook


At this point, you should have a pretty good idea of what is going on with individual elements that affect game view. However, you might still be fuzzy on the big picture, so I will provide some cookbook examples for the most commonly used game views.

The recipes in this section only apply to version 1.4 and later. If you are working with version 1.3 or earlier, you will either want to upgrade or change all code referencing the setFirstPerson () method to statements that change the value of the global $ f irstPerson instead. For example, instead of ServerConnection. setFirstPerson ( true ), you would have $firstPerson = 1.

210

'--. -

... --_.-

Gameplay Classes

Chaprer 7

1st POV Only-Standard (9O-DegreeJ FOV


To force the engine to use only 1st POV, have the player use the following datablock and make the player the control object.
datablock PlayerData ( firstPOVOnly ) ( firstPersonOnly true; observeThroughObject true; cameraDefaultFOV cameraMinFOV cameraMaxFOV 90.0; 90.0; 90.0;

I;

Forcing 1st POV Only-Alternate Method


There is another way to force a 1st POv. First, disable the toggleFirstPerson () function by unmapping it (from actionmap) or gutting it.
function toggleFirstPerson(%val) ( II removed entire body of function

Now, in the file" rv /server/scripts/clientConnection.cs" at the very end of the function GameConnection : : onConnect () add the following code.
ServerConnection.setFirstPerson( true);

Forcing 3rd POV Only


Follow the steps we used (above) to disable the toggleFirstPerson () function and make sure that your player data block has the following values.
datablock PlayerData ( thirdPOVOnly ) ( firstPersonOnly false;

observeThrouqhObject II
);

= true;

Now, in the file" rv /server/scripts/clientConnection.cs" at the very end of the function GameConnection : : onConnect () add the following code.
ServerConnection.setFirstPerson( false ); 211

---- --

----

-----------

----------

Part III

Game Elements

1st or 3rd POV Capable


To allow the game view to be either 1st or 3rd POV, have the player use the following datablock and make the player the control object.
datablock PlayerData( firstOrThirdPOVOK ) { firstPersonOnly = false; observeThroughObject = true:

II average FOV freedom cameraDefaultFOV = 90.0; cameraMinFOV 45.0; cameraMaxFOV = 120.0; II average looking freedom minLookAngle = -1.57; II straight down maxLookAngle = 1.57; II straight up maxFreelookAngle = 2.1; II 2/3 rotation
};

Enabling Orbit Mode


To enable orbit mode, the datablock for the object that the camera will be tethered to should be configured similarly to the following example.
datab10ck PlayerData ( useCameraSettings ) { observeThroughObject false; firstPersonOnly = false;

II Average Looking Freedom minLookAngle = -1.57; II straight down maxLookAngle = 1.57; II straight up maxFreelookAngle = 2.1; II 2/3 rotation
1;

Additionally, the camera should have a datablock definition that defines FOV values.
datablock CameraData ( fixedFOVDistanceCam ) ( II Standard FOV cameraDefaultFOV = 90.0; cameraMinFOV 90.0; cameraMaxFOV = 90.0;
};

212

'-----

.. -

Gameplay Classes

Chapter 7

Use Vehicle's Eye Node on Mount


To have the game view automatically use a vehicle's eye and cam nodes when a player mounts the vehicle, edit the vehicle's datablock as follows.
datablock wheeledVehicleData( theVehicle ) { I I ... useEyePoint = true; II

~};- - - ~ ~
One thing that people often forget is that the camera is derived from ~ SceneObject and therefore has all of its attributes. One of these attributes is the transform. It is often nice to have a camera dropped into the game in exactly a certain place with a specific orientation. One way to do this is to move the camera to the place you want to spawn, orient it, and then grab the camera's transform.

$camTransform

%cameraID.getTransform( );

With this information in hand. simply place a single spawn point in the game and then force it to assume the saved transform.

$spawnPointID.setTransform( $camTransform );
Last, save the mission. Now. the next time you load up and drop into the world, your free-camera position and orientation will be exactly correct.

7.3 Player and PlayerData


The Player and PlayerData classes derive from ShapeBase and ShapeBaseData, respectively. Therefore, they inherit all the features of those classes. Additionally, they add the following features. Rendering First POV enable Forces and factors Max speeds Energy drain Delays Resistance factors Angle limits Step height Velocity parameters
213

Part III

Game Elements

Programmable pickup radius Look-angle limits Impacts (vs. collisions) Special effects Foot puffs Footprints Splashes Bubbles Sounds Standard animations

7.3.1 Player Rendering (POVJ


As we've seen, when the camera is attached to a player, we can view our game in either 1st POV or 3rd POY. In additon to the features restricting camera yaw and pitch, there is one more field of interest: renderFirstPerson.

renderFirstPerson
When we are viewing in 1st POY, it may be neccesary to disable rendering of the player mesh (on the player's client only); that is, we might not want the player to be able to see his body in 1st POY. Rendering of the player's body (mesh) in 1st POV can be disabled using the renderFirstPerson field, as follows.
datablock PlayerData( doNotRenderinlstPOV ) {

/ / ... renderFirstPerson
};

false;

..

Please remember (from the Expert Tip in Section 6.6.6), if you have an interior with mirrors and you are playing in 1st POV with renderFirstPerson set to false, your player will not render in the mirror. To fix this, set renderFirstPerson to true.

7.3.2 Player Special Effects


The player comes with a ton of special effects, including particle effects and sound effects. For ease of consumption, these have been divided into categories.
214

Gameplay Classes

Chapter 7

Foot Puffs and Footprints


The player can be made to emit particles representing foot puffs while walking on terrain by specifying the following.
datablock PlayerData( makeFootPuffs ) ( II ... footPuffEmitter "myDustPED"; footPuffEmitterNumParts = 15; footPuffRadius 0.25; II

);

The above sample specifies that "myDustPED" will be used by the player's footpuff emitter. Furthermore, it will emit IS particles. The location of the foot-puff emitter is automatically determined by the engine. Besides foot puffs, we can have footprints. Footprints are rendered using decals, so please see Chapter 11, "Special Effects," for declaring decals. In order to use a declared decal for a footprint, do the following.
datablock PlayerData ( renderFootPrints ) { / / ... decalData = "PlayerFootprint"; II Decal Datablock decalOffset = 0.1; II Alternate decals left-right offset
};

Besides the decal datablock, an offset is specified. This offset is the distance from center (in world units) that alternating decals are rendered. In other words, for the above code, the left-decal is rendered 0.1 world units to the left of center, and the right is 0.1 world units to the tight of center. This makes the distance between the decals 0.2 world units. We could specify the PlayerFootprint datablock as follows.
datablock DecalData(PlayerFootprint) sizeX = 0.25; sizeY = 0.25; textureName "~/data/shapes/player/footprint";
};

The PlayerFootprint specifies that the footprint should be 0.25 by 0.25 world units square and use the image in Figure 7.1. This image measures 32 x 32 pixels. It could be larger for greater detail, but changing the size of the image file does not change the resultant footprint size.

Figure 7.1.
Player footprint.

215

---

-- -

.. -

----

- ..

_--

Pan IIi

Game Elements

Splashes and Bubbles


When the player enters andlor moves through the water, the engine can optionally produce splashes and bubbles. To create a splash when the player enters the water (near) vertically, do the following.
datablock PlayerData( splashAndBubble / / ... II 1 world unit/s or greater causes splash splashvelocity 1.0; II Particle Emitter DB for splash splash == "splashPED"; II Angle of incidence <= 45 for splash splashAngle 45.0

};

In this sample, if the player is moving at 1 world unit per second or greater and the angle of incidence (entry angle) with the water is less than or equal to 4S degrees, the splash PED will play. This means a splash requires a near vertical drop to happen. To make the player emit splashes while moving through the water, do the following.
datablock PlayerData( splashWhileMovingHorizontally ) ( / / ... II Splash at 0.25 world unit/s or greater splashVelEpsilon = 0.25; II Splash Particle Emitter DB #0 splashEmitter[O] "splashPEDO"; II Splash Particle Emitter DB #1 splashEmitter[l] "splashPED1";

)i

To produce bubbles for a period of time each time the player moves in water, do the following.
datablock PlayerData( bubbleDuringAndAfterMoving ) { // II Bubble Particle Emitter DB splashErnitter[2] "bubblePED"i II Ticks to froth (bubble) for (1/3 sec) bubbleEmitTime == 10.0;

...

};

216

Gameplay Classes

Chapter 7

Sounds and Sound Modifiers


In addition to particle emission, the player can produce a series of sounds. Here is an example.
datablock PlayerData( exitWaterSoundSample ) ( / / ... II Make sound when exiting at 2+ world units/s exitSplashSoundVelocity 2.0; exitingWater = "myExitSoundAudioProfile";

);

In this sample, the trigger event is exiting water at a velocity greater than exi tSplashSoundVeloci ty. When this event occurs, the exi tingWater audio profile is played. In addition to the above sound and its sound modifier, there are many, many more such pairs. Each of these pairs follows the same behavior as the one we just examined. A sound will be played if an audio profile for the sound is specified, and if the conditions of the sound's modifier are met. Please refer to Appendix A.3, "Console Objects' Fields and Methods Quick Reference," for a complete listing of the various player sound fields and their associated triggers andlor modifiers.

Property Maps
There is a file named "propertyMap.cs" located under the data subdirectory. In this file, you'll find statements like the following.
addMaterialMapping( "grass" , "sound: 0" , "color: 0.46 0.36 0.26 0.4 0.0" I;
Table

7.3.

Sound types.

Sound
0
1

Sound Type
Soft
Hard Metal

This statement associates some data with a texture (material) named "grass". One of these bits of data is the sound number associated with this material. You can add materials as suits your needs. The complete list of possible sounds are in Table 7.3.

Snow

7.3.3 Player Physics


The Player class adds a new set of physical parameters on top of those inherited from ShapeBase and ShapeBaseData.

Forces and Factors


In this section, we'll briefly discuss the fields that limit player motion. Somewhat later (Section 7.4), we'll talk about how the player is made to move.
217

Part III

Game Elements

These forces and factors are all relatively straightforward. We'll discuss the less obvious ones in Table 7.4. All velocities are in world units per second.
Table 7.4. Forces and factors limiting player motion.

Force/Factor

Purpose

I
I

Forward and Backward Motion maxForwardSpeed maxBackwardSpeed


Maximum forward velocity. Maximum backward velocity.

Sideways Motion maxSideSpeed


Maximum sideways velocity.

General Horizontal Motion horizMaxSpeed


Maximum horizontal velocity on ground, in air, or in water. Delta factor used to determine how much of horizResistspeed is removed from current velocity. Velocity at which horizontal resistance kicks in.

horizResistFactor

horizResistSpeed

Jumping jumpDelay jumpForce


Forced delay between jumps (in ticks). Force applied to player on jump. Should be less than 40,000 * mass. Drain this many energy points for every jump. Cannot jump if surface angle equal to or greater to this many degrees. Cannot jump if running faster than this. Cannot jump if energy lower than this.

jumpEnergyDrain jumpSurfaceAngle

maxJumpSpeed minJumEnergy

,
runEnergyDrain run Force

Running
Drain this much energy per tick while running. Accelerate player by this much per tick as a result of a move (command). Should be less than 40,000 * mass. Cannot accelerate if surface angle equal to or greater to this many degrees.

runSurfaceAngle

Upward Motion upMaxSpeed


Maximum velocity allowed in the positive direction.

218

--

-----

Gameplay Classes

Chapter 7

Force/Factor
upResistFactor

Purpose
Delta factor used to determine how much of upResistSpeed is removed from current velocity. Velocity at which vertical resistance kicks in.

Table 7.4 (continued].

upResistSpeed

Underwater Motion

maxUnderwaterForwardSpeed maxUnderwaterBackwardSpeed maxUnderwaterSideSpeed

Maximum underwater forward velocity. Maximum underwater backward velocity. Maximum underwater sideways velocity.

Recovery

recoveryDelay recoveryRunForceScale

Number of ticks to stay in recovery mode after hard Fall. Scale factor to apply to horizontal motion while in recovery mode.

Resist Factors

The resist factors in Table 7.4 may not be entirely clear at first glance. TGE provides resist factors for horizontal and upward vertical motion. These are in addition to the drag field provided by ShapeBaseData. The general equation for these resist factors is as follows.
if (velocity> resistVelocity currentVelocity -= resistVelocity * resistFactor * timeDelta;

In other words, once resist speed is achieved, resistance is applied by a factor of that resist speed.
Recovery Delays

When the player falls from a great distance, the landing is considered to be hard. TGE treats hard landings in a special way. As soon as a hard landing occurs, the player switches into "recovery mode." This recovery mode lasts for recoverDelay ticks. During this time, the player's run acceleration is modified by a factor of recoveryRunForceScale. The general equation for this is as follows.
if ( ElapsedTimeSinceHardFall <= recoverDelay ) currentVelocity += currentAcceleration * recoveryRunForceScale;

219

~~---

----------

Pctrt III

Game Elements

Impacts
The player can collide with objects just like any other ShapeBase-derived object. In addition to this collision detection. a new kind of collision has been added. These collisions are called impacts. There are two kinds of impacts, those with the ground and those with other objects.

General Impacts
A velocity threshold can be set, above which a collision is determined to be a general impact.
datablock PlayerData( general Impact ) { / / ... II Collision is Impact at >= 10 world units/s minImpactSpeed = 10.0;
};

Impacts with the Ground


A velocity threshold can be set. above which a collision is determined to be a ground impact.
datablock PlayerData( groundlmpact ) { / / ... groundlmpactMinSpeed = 8.0; groundImpactShakeAmp = "8.0 8.0 12.0"; groundlmpactShakeDuration = 1.0; groundlmpactShakeFalloff 0.5; groundImpactShakeFreq "10.0 10.0 10.0";

};

In the above sample. any impact at over 8 world units per second is considered to be a ground impact and thus fires the ground shake effect. The camera is shook with the specified amplitude and frequency, falling off by a factor of 50 percent per tick to nothing over 1 second.

Impacts and Recovery (Madej


As with a hard fall, impacts will automatically cause the player to enter recovery mode. If the player is squatting every once in a while for no particular reason, it is probably because the impact velocity settings are too low.

Step Height
220

There is a factor named maxStepHeight that limits how great a positive change in elevation must be before a player cannot step up. If the elevation

Gameplay Classes

Chapter 7

change in a particular direction is greater than this value, the player will not be able to walk in that direction. The only way to get over this step is by trying to jump over it.

7.4 Controlling The Player


So far, we've talked about how the motion of the player is limited and parameterized by fields in the PlayerData datablock. Now, let's talk about how we control our player's translations and rotations in the world.

7.4. 1 Movement Globals


TGE has a set of global variables that interact to determine if the control object translates or rotates. Additionally, the translation factors are further modified by a common global while the rotation factors are modified by the current FOV (via script).

Translations
All translations are modified (in script) by the global variable $movementSpeed. This value is a multiplier that affects the input value and is later multiplied by the various speed factors discussed above to give a final acceleration. The general equation of how the translations are calculated in script is as follows.
II Result is clamped [0.0, 1.0] $mvActionValue = %value * $movementSpeed;

A frequently asked question is. "Is there a way to dynamically scale my player's velocity?" I've seen folks answer this with a "no." It should be clear from this discussion that that answer is wrong. To scale your player's velocity. simply scale the value in

$mvActionValue.
This value can be between 0.0 and

Later, inside the engine, our acceleration is calculated as follows.


acceleration = $mvActionVa1ue * speedFactor * timeDelta

Subsequently, maximum velocity (ignoring drag and other factors) is as follows.


maxVelocity
=

$mvActionValue * speedFactor

The specific global variables (named action values corresponding to $mvActionValue in the first equation) are as shown in Table 7.5. To see some
ActIon Value $mvLeftAction
$mvRightAction $mvForwardAction $mvBackwardAction $mvUpAction $mvDownAction

DescrIption
Move left. Move right. Move forward. Move backward. Move upward. Move downward.

Table 7.5.
Keyboard translation global action values.

221

Part III

Game Elements

examples of the variables in use, examine the file "default.bind.cs" in either the TGE Demo or the GPGT Lesson Kit.

Keyboard Rotations
If we so choose, we can add key mappings to enable camera/player/vehicle yawing and pitching via keyboard instead of mouse. Each of these actions is modified by the preference variable $Pref: : Input: : KeyboardTurnSpeed. The general equation showing how these rotations are calculated in script is as follows.
SmvActionValue

= %value * SPref: :Input::KeyboardTurnSpeed;

The TGE Demo and GPGT Lesson Kit do not use these features, but they are easy to hook up. Table 7.6 describes the global action values for keyboard rotations.
Table 7.6.
Keyboard rotation global action values.

ActIon Value
$mvYawRightSpeed $mvYawLeEtSpeed $mvPitchDownSpeed $mvPitchUpSpeed

Description
Yaw right. Yaw left. Pitch down. Pitch up.

Mouse Rotations
All mouse rotations are modified (in script) by a script (provided in the TGE Demo and GPGT Lesson Kit) named getMouseAdjustAmount (). This is done to keep mouse yaWing and pitching consistent across FOVs. This function produces a multiplier that is used as follows. SmvActionValue += getMouseAdjustAmount(%val); The specific yaw and pitch global variables (named action values) are described in Table 7.7.
Table 7.7.
Yaw and pitch global action values.

AdIonValue
5mvYaw $mvPitch

DescrIption

Yaw camera by this amount. Pitch camera by this amount.

7.4.2 The MoveMap


We're doing pretty well so far. We know how to define a player so that it has the forces and factors we want, and we know how to tell TGE to translate/ rotate our character. Now, how do we attach that code to the keyboard and/or mouse?

222

GamepJay Classes

Chapter 7

In Chapter 9 we will discuss the ActionMap class, but to summarize its purpose for now, the ActionMap is a class whose job it is to convert device inputs into function calls. In both the TGE Demo and GPGT Lesson Kit, a special action map has been defined. Its name is moveMap. moveMap is automatically loaded when we start a mission. By default, it has been configured to connect our keyboard actions to function calls which then calculate movements using the global variables we discussed above. If you are curious about this process, I suggest you skip ahead to Section 9.4, "Device Inputs and Action Maps," and then open the "default.bind.cs" file you will find in either the TGE Demo or the GPGT Lesson Kit. We've talked enough now about the Player and PlayerData classes to jump into the actual creation of our test player. The accompanying disk contains several player models, including the default Torque Orc and Blue Guy. Additionally, it includes Simplest Player, which is a non-bipedal player with no animations or other special features.

7.4.3 Maze Runner lesson #6 (90 Percent StepJSimplest Player


For our game, we will need to make a very simple player. This player is nothing more than a ball with three nodes Uoints): floor, eye, and cam (Figure 7.2).
Figure 7.2.
Simplest Player.

Copy Required Files


From the accompanying disk, please copy the file "\MazeRunner\ Lesson_006\mazerunnerplayer.cs"into"\MazeRunner\prototype\server\scripts\ MazeRunner". Now, edit the function onServerCreated () in the file "\MazeRunner\ prototype\server\game.cs" to look like this (bold lines are new or modified):
exec("./MazeRunner/fireballs.cs"); II MazeRunner exec("./MazeRunner/mazerunnerplayer.cs"); II MazeRunner

Simplest Player Skeleton


Because we're not going to animate this player, it doesn't need very many nodes Uoints) in its skeleton. In fact it only needs a root node and the two camera mount points (see Table 7.8).
I

Node
floor eye cam

DeIcrIptIon
The root node, specifying the physical bottom of the mesh. The 1st POV camera node. The 3rd POV camera node.

Table 7.8.
Simplest Player nodes.

223

Parr III

Game Elements

Root Node

In this model, the root node is located at the bottom of the player, and the eye and cam nodes are attached to it. This node defines the bottom of the player and is where the mesh contacts the ground. The engine uses the lowest node it finds in a mesh's skeleton as the bottom of the shape; thus, if this node were placed in the middle of the player, the player would sink into the ground.
Eye and Cam Nodes

The next node is the eye node. It is located on the "forehead" just above and between the eyes. This is where the 1st POV camera will be mounted. The last node is the cam node. This is located behind and above the model. It doesn't necessarily need to be here, but this model was designed (in part) to show the difference between an eye mount and a cam mount. As you've probably guessed, this is where the 3rd POV camera will mount. Simplest Player Geometry
Visible Mesh

There isn't much to say about this. It's a ball. The player has one mesh and one skin. We're not using any IFLs or other fancy features.
Collision Mesh

We do not need to define a collision mesh for instances of the Player class, as the engine does this automatically. Simplest Player Animations Earlier I said that this player is not animated. I lied. OK, I didn't exactly lie. For any player to work, the root animation needs to be exported at a minimum. Then, to get rid of some annoying warnings, you'll need to export the other animations (shown in Table 7.9). Since the player isn't going to need these animations, I've left them blank and just exported the same sequence for each. The sequences for these animations are shown in Table 7.10, which includes the following information. Animation. This is the (required) name for the animation sequence in question. Start Key/End Key, These are the frames in which the named animation begins and ends. FPS. This is the base frame rate at which the animation should be played. Cyclic. This indicates whether the animation should be played once or in a cycle.

224

Gameplay Classes

Chapter 7

Animation
root run back side jump standjump fall land

DescrIption
A default animation that plays while the player is at rest.
Forward running animation. Backwards running animation. Sideways stepping animation. Moving jump animation. Stationary jump animation. Long falling animation, which starts about 1 second after fall starts. Hard landing animation (played while in recovery-mode).

Table 7.9. Animation descriptions.

Animation
root

Start Key
1

End Key
2

FPS
1

Cyclic

Blended
N

Table 7.10. Sequences for animations.

seq: root=1-2, fps=l, cyclic 1 run seq: run= 1-2, fps= 1, cyclic 1 back 1 side seq: side=1-2, fps=l, cyclic 1 jump seq: jump=1-2, fps=l 1 standjump seq: standjump=1-2, fps=l 1 fall seq: fall=1-2, fps=l 1 land seq: land=1-2, fps=l 2 1 2 1 2 1 2 1 2 1 2 1
Y

seq: back=1-2, fps=l, cyclic 2 1


Y

N
The default MS3D exporter does not support blending and many of the other cool special features that DTS supports. So, I suggest that you visit the GarageGames site and download the "DTS Plus" exporter (resource).

Blended. This indicates whether the sequence should be blended or not.


Finally, for each sequence there is a combined line something like "seq: root = 1-2, fps = 1, cyclic." This is what you would type in for the default exporter, but since we're using the DIS Plus exporter, you will enter the values via that exporter's dialog.

225

Part III

Game Elements

Simplest Player's Datablock

Because the datablock for this shape is a bit long, only the pertinent portions are listed here.
datablock PlayerData( MazeRunner : BasePlayer ) { shapeFile = "-/ da ta/MazeRunner/Shapes/Players!MazeRunner .dts" ;

boundingBox = "1.6 1.6 2.3"; invincible = true; groundlmpactMinSpeed = 1000; ImpactMinSpeed = 1000; renderFirstPerson = false; observeThroughObject = true; II

};

This player has the following notable attributes. 1. It derives (copies) from the BasePlayer datablock that comes with the GPGT Lesson Kit. 2. As would be expected, the mesh we just built (or copied) is used. 3. The shape is a little bigger than the normal character, so we've increased the dimensions of its bounding box from "1.2 1.22.3" to "1.61.62.3," adding an extra three-tenths of a world unit in the x and y dimensions. 4. The player is marked as invincible because we are not going to use damage to determine if it is "dead." Instead, we'll kill it immediately if the mesh is hit by a fireball or if it falls in the lava. S. Impacts are effectively disabled by setting the velocity factors to values greater than any velocity the player will be able to achieve in this game. 6. renderFirstPerson is disabled, meaning the mesh will not render while the game view is 1st POv. 7. The camera has been instructed to use the player's camera settings (observeThroughObject is true). To get the entire datablock, please copy "\MazeRunner\Lesson_006\ MazeRunnerPlayer.cs" to the "\MazeRunner\prototype\server\scripts\MazeRunner" directory.
Loading the Datablock

Now, edit the "\MazeRunner\prototype\server\scripts\game.cs" file and update


onServerCreated () to contain the following code (new code is bold).
exec("./MazeRunner/f~reball.cs"); II MazeRunner exec("./MazeRunner/MazeRunnerPlayer.cs"); II MazeRunner

226

,-

Gameplay Classes

Chapter 7

Using This Player


Now, to use this player instead of the Blue Guy we have been using thus far, edit the "\MazeRunner\prototype\server\scripts\game.cs" file and modify the highlighted code (below) in GameConnection: : createPlayer () to look like the following.
function GameConnection::createPlayer(%this, %spawnPoint) / / ... (

II Create the player object


%player = new Player() { dataBlock = MazeRunner; II Change this line client = %this;
};

/ / ...

7.5 Vehicles
So far, we have talked about game view, cameras, and players (the first category of avatars). Now we will discuss vehicles, the second category of avatar. TGE provides classes for making the following vehicle types. ~ Wheeled vehicles. Ground vehicles with four, six, or eight tires. Hover vehicles. Ground vehicles with no tires. Flying vehicles. Science-fiction-style air vehicles. We do not discuss this vehicle type here (although a working sample is provided).

T-h-e-r-e-a-re-----....

7.5.1 Vehicles Overview


Vehicles share many traits, and all three vehicle types derive from the same base class. So, we'll talk about vehicle geometries, nodes, particle emissions, and animations as a group. We will follow this with a discussion of the base classes VehicieData and Vehicle. Then, we'll talk about mounting and dismounting vehicles.

Vehicle Geometries (Meshes)


Just as the player must have some kind of geometry (mesh or meshes), so must a vehicle. Each type of vehicle has a minimum set of required geometries. These basic geometries are described in Table 7.11. Besides visual geometry and the one collision mesh, another kind of geometry can be included in your models-a second type of collision mesh named LOS (line of sight). See Table 7.12.

working samples of each type of vehicle included in the GPGT Lesson Kit. and a full explanation of how they were created is included in the appendices. All of these vehicles were created and animated with MilkShape 3D. a reasonably featured and low-cost too/.

227

----

---- - -

Part III

Game Elements

Table 7.1l.
Vehicle geometries.

Geometry
Chassis

DeIcrIption
The body of the vehicle. This can be complex or very simple. A simple nonconcave collision mesh. This is the primary collision mesh used for the vehicle.

Collision-l

It is suggested that this mesh not have more than 20 vertices because collision calculations are quite CPU-intensive and the time required increases with the complexity of the mesh.
Tire This is only required for the WheeledVehicie class.

Table 7.12.
Line-of-sight collision mesh.

Geometry
LOSCoI-9 .. LOSCol-16

DeIcrIption
Line-of-sight collision meshes. These meshes are used for registering the impact of projectiles and other line-of-sightdependent collisions like ray-casts.

In practice, you may specify more than one collision mesh, but this is not suggested. However, multiple LOS meshes are acceptable and quite normal to encounter.

General Vehicle Nodes


Another part of a model's construction is the set of nodes (or joints) to which the mesh attaches. In TGE, the majority of these nodes are used by the engine to attach particle effects, and the remaining two are used for attaching the camera (Table 7.13). Not all nodes are used by all vehicles and not all vehicles have all nodes (Table 7.14). As previously mentioned, some of the nodes are used to mount particle emitters. Table 7.15 specifies what particle-emitter field (in a datablock) is associated with what node. The emitters attached to a vehicle will activate at various times. Table 7.16 specifies when the emitters will be activated (not all these emitters are attached to nodes).

Vehicle Animations
Vehicles can have several animations. In addition to the damage animations that are inherited from the ShapeBase classes, vehicles have the new animations in Table 7.17. All of these animations are blended. Not all animations are available in all vehicles. Table 7.18 specifies which vehicles use which animations. Additional animations can be provided but must be activated from script.

228

>---

Gameplay Classes

Chapter 7

Node(s)
cam contraiiO .. contrail3 eye hubO .. hub7 JetNozzleO JetNozzlel JetNozzle2 JetNozzle3 JetNozzleX mountO .. mount3!

DescrIption
Third-person camera position. Particle-emitter mount. Simulates contrails. First-person camera position. Helper nodes that specify the location of the tires. Particle-emitter mount. Simulates thrusters in rear of vehicle.

Table 7.13.
Vehicle nodes.

Particle-emitter mount. Simulates thrusters in front of vehicle.

Particle-emitter mount. Simulates thruster on bottom of vehicle.

o is normally the driver mount-point, and 1..10 are passengers,


gunners, turrets, etc.

General mount points that can be used for anything. However,

Node
cam contrailO .. contrail3 eye hubO .. hub7 JetNozzleO JetNozzlel JetNozzle2 JetNozzle3 JetNozzleX mountO .. mount31

Wheeled
optional

Hover
optional

flying
optional optional optional optional optional

Table 7.14.
Use of nodes by vehicle type. Although the cam and eye nodes are labeled "optional," you must have at least one of them. If neither is present, the camera will mount to the centroid of the vehicle. Also, be aware that all nodes can be animated, including the cam and eye nodes.

optional optional

optional optional optional

optional

optional

optional

optional optional

optional optional

Node
contraiiO .. contrail3 JetNozzleO JetNozzlel JetNozzle2 JetNozzle3 JetNozzleX

Hover

Flying
trailEmitter forwardJetEmitter

Table 7.15.
Particle-emitter fields associated with nodes.

forwardJetEmitter

backwardJetEmitter downJetEmitter

backwardJetEmitter downJetEmitter

229

Part III

Game Elements

Table 7.16. Activation of emitters.

Node
contrailO .. contrail3

Wheeled

Hover

FlyIng
When velocity exceeds minTrailSpeed. On forward thrust.

On forward thrust.

JetNozzleO JetNozzlel JetNozzle2 JetNozzle3 JetNozzleX dustTrailEmitter

On backward thrust.

On backward thrust.

On upward thrust. Velocity > 0 && Elevation < = triggerTrailHeight Emits from rear of vehicle.

On upward thrust.

tireEmitter

While moving from tires.

When vehicle is within triggerDus tHeight of ground. dustEmitter Please note that this emitter uses colors specified for terrain in propertyMap for that terrain texture, or all white if not found. Dust rises from ground beneath vehicle to dustHeight. If vehicle has sustained damage percentage greater than damageLevel Tolerance [n], then damageEmitter[n] is activated for emitters 0 and 1. Emitter 2 is only activated if the vehicle is damaged and underwater. Damage particles are emitted at a random point at a distance of damageEmitterOffset from the vehicle's centroid. Additionally, n umDmgEmi t te rAreas specifies if we have 1 or 2 emitters specified.

damageEmitter[O] damageEmitter[l] damageEmitter[2]

Table 7.17. Vehicle animations.

Anlmellon
activateBack

DescrIption
An animation that occurs when the vehicle is thrusting (accelerati ng) forwa rd. An animation that occurs when the vehicle is thrusting (accelerating) upward. An animation to turn the brake lights on and off. Usually implemented with an IFL. An animation that occurs when the vehicle is gliding forward. An animation that occurs when the vehicle is gliding upward. Blended animations used to animate the suspension for wheeled vehicles. Blended animation to turn the steering wheel when wheeled vehicles turn.

activateBot

brakelight

maintainBack maintainBot springO

..

spring7

steering

230

Gameplay Classes

Chapter 7

Animation
activateBack activateBot brake light maintainBack maintainBot springO

Wheeled
optional

Hover
optional

..,....
optional optional

.l;"..l~~

Table 7.18.

optional

Use of animations by vehicle type.

optional optional

optional optional

..

spring7

steering

7.5.2 Vehicle and VehicleData


These classes are virtual parents to the three concrete classes used for wheeled, hover, and flying vehicles. The Vehicle class has no fields, variables, or methods. So, we only need to discuss the datablock. Vehicle Physics In general, vehicle physics can be quite difficult to understand and to manipulate. So, I'll give a short description of the various fields and their purposes, then I'll supply sample vehicles with working values in the GPGT Lesson Kit. After that, you'll need to experiment. Integration The integration field tells the engine how many times to try to resolve the current motion. The value in this field determines the time slice used. Larger values equal smaller time slices and more iterations. Choosing a value for this field is a tradeoff of stability vs. time. Smaller time slices mean a more stable evaluation, but we pay for these multiple updates in computing time. In short, a value of about 4 is good for hover and wheeled vehicles, but you may need a higher value for flying vehicles or high-velocity vehicles. Experimentation will tell. Friction and Restitution The bodyFriction field determines how much velocity is lost to rubbing on impact with a surface. This can have some odd side effects, however, so you may want to make this value either very small or zero (in the case of flying vehicles). The bodyResti tution tells us how much the vehicle will "bounce back" when it hits something. This field should be less than 1. A good value is between 0.4 and 0.5.

231

Part III

Game Elements

contactTol and collisionTol


The field contactTol is compared to the result of a dot-product calculation to determine if a collision occurred. Thus, if you want to cause collisions to be largely ignored, this value should be near to 1.0. However, this value is normally about 0.1, which is an angle of incidence of about 6 degrees; i.e., any contact at an angle betweeen about 6 and 90 degrees registers as a collision. The field collisionTol is a value that specifies the "don't care" distance for a collision. If the possibly colliding points are farther apart than collisionTol, the collision doesn't happen. This, too, is usually set to 0.1 (world units).

massBox and massCenter


The mass of a vehicle is treated as if it is evenly distributed within a sphere. The diameter of the sphere is normally equal to the distance between opposite corners of the vehicle's world bounding box. However, for wheeled vehicles, if the massBox field is greater than 0, this value is used instead. This way, we can compact the mass or spread it out as meets our needs. The massCenter field is a three-element floating-point vector specifying an offset from the vehicle's centroid. This is used to move the massBox away from the vehicle's centroid.

minDrag and maxDrag


In addition to the normal drag value provided by ShapeBaseData, we can specify a minDrag and maxDrag. However, these values are only used for flying vehicles. minDrag is the minimum drag that will always be applied to the vehicle. maxDrag is now a dead variable and not used at all.

Steering
We can specify a maxSteeringAngle in radians for all vehicle types. This will limit how quickly we can steer in a new direction. Smaller values equal slower turns, and larger values equal faster turns.

Jetting
Interestingly, all vehicles can use a j etForce, which is a generic forward thrust value (in the case of wheeled vehicles, applied in addition to frictional forces) . Jetting is activated when move trigger three is nonzero ($mvTriggerCount3 > 0). In order to jet, the vehicle must have more energy than minJetEnergy (by default this is 1). Lastly, when jetting, j etEnergyDrain energy is removed from the vehicle per tick. The default for jetEnergyDrain is 0.8.

232

Gameplay Classes

Chapter 7

Impacts and Impact Sounds Like the player, vehicles can have impacts. Likewise, there are sounds associated with these impacts. Because I've talked about this concept in Section 7.3.2, I will not discuss it further and just refer you to Appendix A.3, "Console Objects' Fields and Methods Quick Reference," for specifics. The Camera We have already discussed our ability to control the camera in Section 7.2: the camera can lag the vehicle when it accelerates and will do so when we set cameraLag to a positive value. This lag is recovered at a rate of cameraDecay.

The other thing we can do to the camera is offset it (vertically) by cameraOffset world units from the 3rd POV mount point (cam).

collOamage fields
Neither the collDamageThresholdvel nor the collDamageMul tiplier field is used by the engine. These are for scripting purposes only. That is it for our discussion of the VehicleData class. Now, let's discuss the general topic of mounting and dismounting, as well as how to use a vehicle as the prayer.

7.5.3 Vehicle Mounting


A vehicle can either be mounted (player sits on or in it) or it can substitute for a player. Furthermore, any of the following actions can occur. 1. 2. 3. 4. Player Player Player Player mounts vehicle on collision or in response to other action. starts in the mounted position. is replaced with vehicle on collision or in response to other action. starts as vehicle.

Each of these cases requires a set of console methods and some dynamic fields in the vehicles/players. Because there are innumerable correct ways to handle these cases, it might seem a bit daunting the first time you have to solve this problem. So, sample flows and source code are provided with the GPGT Lesson Kit to handle cases 1, 3, and 4. We won't cover case 2 directly, but it can be derived from the other cases. Mounting Vehicles In the GrGT Lesson Kit, when a collision occurs between a Player object and a Vehicle, the engine will attempt to fire the onCollision () callback

233

Part III

Game Elements

for both datablocks. The playerData: :onCollision () method provided with the Lesson Kit will then attempt to mount this player to the vehicle, if the vehicle is mountable and if the player is not already mounted to another vehicle (Figure 7.3).
PlayerData:: onCollision (). Fires on a collision and calls the PlayerData: : doVehicleMount () if the collided object is a vehicle, it is mount-

able, and the player is not already mounted to a vehicle.


PlayerData:: doVehicleMount (). Handles the work of mounting the player to the vehicle. This method also manually notifies the vehicle that an object (the player) is being mounted to it by calling the vehicle's onPlayerMount () method.

VehicleData:: onPlayerMount (). This method is called by doVehicleMount () in the case that a player gets mounted to the vehicle. The purpose

of this method is to do any special animations or other actions you might require in the case of a mounting. PlayerData:: onMount () . This is automatically called by the engine as a result of PlayerData: :doVehicleMount () calling the engine mount ()

Figure 7.3.
Mounting vehicles.

mounting

PlayerData::

VehicleData: :

--...,.--"
Time

.... "-

Succesfullly Mounted?

~---- .......

-. .......

Table 7.19.
Dynamic fields for mounting.

Player Object Dynamic Field Name


canMount

DescrIption
A Boolean value specifying whether the player is allowed to mount a vehicle. A Boolean value denoting whether the player is already mounted to something.

Range
[ true, false]

isMounted

[ true, false]

vehicle Dlttllblock Dynamic field Name


isMountable

DeKrlption
A Boolean value determining if this vehicle can be mounted.

Range
[ true, false]

234

Gameplay Classes

Chap(er 7

console method to mount the player object to the vehicle. In this code, we do some cleanup work on the player, like resetting the transform, placing the player in the sitting pose, and setting the vehicle as the new control object. Mounting Dynamic Fields In order to do the work of mounting or substituting, we require that there be a few dynamic fields present in the player object and the vehicle's datablock (Table 7.19). Dismounting Vehicles Assuming that the player is mounted to a vehicle, we may wish to allow for dismounting to occur. The GPGr Lesson Kit provides source code to handle this as a result of a key press, but dismounting can easily be made to result from other actions. too (Figure 7.4). User Action. The user requests a dismount via mouse click or button press. (See the "Vehicle Action Maps" section below.)
PlayerData:: doDismount (). Attempts to dismount from the current

mount point. This method manually calls the VehicleData: : onPlayerDismount () method to notify the vehicle that the dismount is occming.
VehicleDa ta: : onPlayerDismount () . This method is provided so that

the vehicle can playa special animation or do other work when the player dismounts.

Vehicle Action Maps


It is important to know that TGE has code that automatically checks to see if

a player is mounted to an object. When this is true and when a moveTrigger two event (SmvTriggerCount2 > 0) is received, the engine will automatically call the doDismount () catlback. In both the TGE Demo and GPGT Lesson Kit, the spacebar is tied to SmvTriggerCount2. So, you do not need to modify or add an action map unless you wish to remap the trigger to something besides the spacebar.
I
Ployo,Dato:: VohiclsDato: :

dismounting

USer Action

Figure 7.4. Vehicle dismounting flow.

----- ..... ........ -... .........


dOOismounlO

Succe.sfuWly Dismounted?

.... ---,
\ 235

Part III

Game Elements

7.5.4 Wheeled Vehicles


Now that we've discussed general vehicle information, we'll discuss the specific vehicle types supported by TCE. The first of these is the wheeled vehicle. Wheeled vehicles in TCE support 4, 6, and 8 tires. The chassis of the vehicle is represented by the WheeledVehicleData and WheeledVehicle classes, the tires by the WheeledVehicleTire class, and the suspension by the WheeledVehicleSpring class. We'll talk about each of these in turn.
WheeledVehicleData and WheeledVehicie

These classes inherit all the fields in the VehicleData and Vehicle classes, respectively. In addition, the WheeledVehicleData class brings some new fields and features.

The Engine
A wheeled vehicle is moved by its engine. The power of this engine is defined by the engineTorque field. Also, the engine can be used to slow the vehicle. That is, when the engine is not engaged in accelerating or maintaining the vehicle's current velocity, it can apply a braking force. Simply set engineBrake to a positive value, and the engine will slow the car by this factor.

Braking In addition to engine braking, we can actually apply a braking force. The brake force is set using the brakeTorque field. It uses equivalent units (applied oppositely) to engine torque. There is a small catch to braking. Braking is caused by $mvTriggerCount2 being nonzero. This is the same trigger associated with player jumping. So, braking will not work if the player is mounted to a vehicle.; i.e., only vehicles used as the player will brake. The Wheels
We need to specify a maximum angular velocity (rotational rate) for our tires. This keeps them from over- or underrotating and is used to tune the look of our tires. It does not affect how the vehicle drives. This effect is controlled by the maxWheelSpeed field.

Sounds
Our vehicles can make noises under various circumstances. When the engine is engaged, TCE will try to play the eng i neSound audio profile. When jetting (SmvTriggerCount3 > 0), TCE will play the sound specified by jetSound. If the vehicle skids or the tires otherwise break friction, TCE will attempt to play the sound specified by squeal Sound.

236

l_.

Gameplay Classes

Chapter 7

WheeledVehicleTire
A wheeled vehicle can specify a different tire datablock for each tire if we so choose. The tire datablock is named "WheeledVehicleTire" and has the following features.

Friction
Tires exhibit both static and dynamic friction. If you have not studied dynamics. this may mean nothing to you. In real life dynamics, there are two kinds of friction: static and kinetic (some texts will say there are three: static, kinetic, and breaking). Static friction is the friction found between two surfaces when both surfaces are stationary. Static friction is what keeps the objects stationary. When a force is applied that overcomes static friction, the object to which the force is applied will begin moving. This is named staticFriction in TGE. When an object is moving, it usually has a different friction. This friction is known as kinetic friction, and is named kineticFriction in TGE. Normally (for most materials), static friction is higher than kinetic friction. So, what does this mean in TGE terms? Well, TGE simplifies real-world physics, but it does respect these two factors. While a tire is either stationary or moving and has not yet slipped, staticFriction is applied. However, when the torque applied by the engine results in a force higher than staticFriction, the tire will begin to slip. At this point, TGE starts to use kineticFriction in its calculations. In short, with a lower kineticFriction, a tire that is slipping will continue to slip until the applied force is reduced or removed.

Longitudinal Forces and Factors


A tire exhibits forces in two directions (springs handle the third for TGE). The forward/backward force is known as longitudinal force (Figure 7.5, left). In TGE, this force is defined by the longitudinal Force field. There are two additional longitudinal factors that act in concert with longi tudinalForce. Their purpose is to produce a more realistic tire action. Real tires are like springs and deform slightly when forces are applied to them. However, they only deform so much before acting rigid (or exploding, which they do not do in TGE). The springiness of a tire is set using longitudinalDamping. This damping is attenuated by a factor logitudinalRelaxation (Yes, this field is misspelled. It has been and will remain spelled this way to prevent breaking people's scripts). To make your tires behave like rubber, make longitudinal Damping about 10 percent of the value of longitudinalForce, and you can adjust this by making logitudinalRelaxation between 0.0 and 1.0.

237

Part III

Game Elements

Figure 7.5.

Longitudinal and lateral forces.

Longitudinal Forces (top down view)

Lateral Forces (top down view)

Lateral Forces and Factors


The next force tires produce is side-to-side or lateral force (Figure 7.5, right). Lateral force is determined by the field lateral Force. Similarly to longitudinal forces, we have lateralDamping and lateralRelaxation factors.

Physical Parameters
Beyond forces, tires themselves have both a mass and a radius. The mass of each tire contributes to the vehicle's total mass. The radi us field is important because it defines the bounding-box size for the tire. By default. the radius is 0.6 world units. So, if you make an abnormally large or small tire, be sure to adjust this value.

Restitution
The resti tution field in tires is no longer used.

WheeledVehicieSpring
The final component in a wheeled vehicle is the suspension. The suspension is defined by the WheeledVehicleSpring datablock. As with tires, each tire location can have a unique spring. These springs have the following features.

Upward Force and Damping


.. Figure 7.6.

Upward force and damping.

To frame the discussion of this next force, think of the tire as being on the ground. Then, the third force component is the force that pushes up on the vehicle, keeping it off of the ground (Figure 7.6). This pushing force is defined by the field force in the spring datablock. By default, the spring will push with all its force when the spring is fully compressed, and with no force when it is fully extended. The force varies linearly between these two extensions. This spring force can be attenuated when the tire is traveling up and down. If we specify a value for the damping field, this force will be factored into the spring force. A good ratio for damping is about 20 percent of force.

238

Gameplay Classes

Chaprer 7

Figure 7.7.

The anti-sway factor.

Tires Are Even No Anti-Sway

Tires Are Uneven Anti-Sway Kicks In

The Anti-Sway Effect


An odd thing can occur when one tire hits a stone or some other obstacle. It can temporarily cause that part of the car to be higher than the rest of the car; i.e., the car is now off kilter. To compensate for this. TCE provides an antisway factor (see Figure 7.7). The anti-sway force, specified by the antiSwayForce field, is used to rebalance the vehicle (at least partially). In general, if the anti-sway value is lower than the normal force (antiSwayForce < force), the car will tilt away from the raised tire. If the values are equal, the opposite spring will try to equalize the force, levelling the car. It can only do so to the extent of the difference between the two springs' extensions. The anti-sway force equation is as follows.
antiSway = (oppositeWheelExtension - wheelExtension) spring->antiSwayForce;

Figure 7.8.

Length of travel of hub.

Length of Travel
Lastly, we can specify the length of our spring. This length limits the distance the tire hub may travel from its topmost position to its bottommost position (Figure 7.8). The length is specified by the field length and cannot be zero.

--

--}

..

Powered Wheels

The motivational force, the force that moves your vehicle, comes from the tires. By default, all wheels are enabled and thus produce motivational force during the game, but at any time after the creation of a vehicle, we may choose to disable or re-enable individual wheels. This will have an effect on how the vehicle steers and drives in general. To set the power on a wheel, simply use the following method.
II De-power left-front tire %vehicle.setWheelPowered( 0 , false);

239

Part IJI

Game Elements

Remember. wheels are ordered front left, front right, second front ... left rear, right rear. Also, trying to power or depower a wheel that does not exist will cause an error. so be sure your script is aware of the tire count for the vehicle it is modifying.

7.5.5 Hover Vehicles


The next category of vehicles is the hover vehicle. This vehicle is a ground vehicle that remains a short way above the ground. It has no tires to move or turn and instead uses "thrusters" for these maneuvers. There are only two classes involved in making these vehicles: HoverVehicleData and HoverVehicle.

HoverVehicieData and HoverVehicie


These classes inherit all the fields in the VehicleData and Vehicle classes, respectively. In addition. the HoverVehicleData class brings some new fields and features.

Horizontal Motion
The motion of the hover vehicle in the horizontal plane is controlled by three forces: mainThrustForce, strafeThrustForce. and reverseThrustForce. The first force is applied to forward motion. the second to left-right motion, and the third to reverse motion.

Drag
There is a field named dragForce that modifies the maximum rate of the hover vehicle. Setting this value too high will cause the vehicle to not move at all. Experimentation is required. but a good starting value is 1, then move upward.

vertFactor and FloatingThrustFactor


:'-.

The first of these two factors (vertFactor) is multiplied into the vertical component of drag. It defaults to 0.15 but may be increased to produce more drag in the vertical direction. The second factor (FloatingThrustFactor) is used to modify general thrust strength depending on whether the vehicle is floating (not in contact with water, terrain, or interior). If the vehicle is not floating, 100 percent of the force is applied. However, if the vehicle is floating, the general force equation becomes the following.
force
=

FloatingThrustFactor * force;

240

This factor can be between 0.0 and 1.0 and defaults to 0.25. meaning that floating thrust is only one-quarter that of nonfloating thrust. The purpose here

L_..._

Gameplay Classes

Chaprer 7

is to keep the vehicle reasonably powered while in the air, but to make the thrust very strong while in contact with water, terrain, or an interior. Floating Gravity When a hover vehicle is not in contact with water, the terrain, or an interior, the total amount of gravity applied to the vehicle will be as follows.
gravityForce
~

local gravity

However, once the vehicle contacts any of the aforementioned obstructions, we can reduce the force of gravity by a factor of floatingGravMag (can be between 0.0 and 1.0). This gives us the following gravity force equation.
gravityForce
~

local gravity * floatingGravMag

The purpose of this is to allow a nonfloating vehicle to get back in the air more easily. Hovering There is a force called stabSpringConstant. This field must be set to a value equal to two times the mass of the vehicle or higher, or the vehicle will sink to the ground. The field stabDampingConstant acts to keep the hover vehicle from bouncing around too much and can be higher than 5 tabSpringConstant. In fact, the higher it gets, the less bounce there is when hovering over terrain with abrupt elevation changes. Jetting Around If jetting is active (SmvTriggerCountJ > 0), the turboFactor is applied. The current calculated thrust is multiplied by the value in this field if it is nonzero. Stabilizers The hover vehicle has a nonvisible bounding box that is used to "stabilize" it. This box grows as the velocity of the hover vehicle increases and shrinks as the vehicle reduces speed. We can limit the bounds of this box by using the two fields stabLenMin and stabLenMax. Rolling and Pitching When the hover vehicle rolls and pitches. it can optionally glide in the direction of the roll or pitch. Simply set the fields roll Force and pitchForce respectively to some nonzero value and the vehicle will move toward the rotl/ pitch until it rotates back to vertical.
241

Part III

Game Elements

Keeping the Vehicle Upright Because hover vehicles may travel over hilly and bumpy terrain, it is possible that the vehicle may want to tip over. Therefore, the engine provides a force for keeping the vehicle upright. This force is specified using the normalForce field. When a hover vehicle is tilted or canted, this force is applied to right the vehicle so that it is parallel to the surface below it. It is not a strong contributor, so keeping this high is a good idea. Steering There are two forces involved in steering our vehicle. The first is named steeringForce and is the value applied in the direction of our turn. The second is gyroDrag. This is a resistive force that trys to stop the turn. Stay Put! When the vehicle is not thrusting and should be sitting still, it may still slide about, especially if there is a slope. To prevent the vehicle from constantly sliding away, we can set two fields to nonzero values. First, we set a threshold velocity brakingActivationSpeed. When the vehicle is not thrusting, autobraking will begin to activate as soon as the speed of the vehicle is lower than this. Once braking is activated. the force brakingForce will be applied until the vehicle comes to rest. Special Effects The hover vehicle supports three new sounds: j etSound, engineSound, and floatSound. These sounds play while jetting, thrusting, and hovering, respectively.

7.5.6 Alternate Mounting Positions


In our discussions, we have only talked about mounting to mountO, but it is completely possible to mount to another mount node. We can blindly mount our players to nodes, but the best way to handle multiple mountings is to check to see if a node is available. To do this, you can use this piece of code (slight modification of script found in the forums):
function findEmptySeat( %vehicleObj , %mountPoints, %startNode ) ( if ( 0 >= %startNode ) %count 0; else %count %startNode %count++ ) ( for ( 0 ; %count < %mountPoints

242

Gameplay Classes

Chapcer 7

if

%node = %vehicleObj.getMountNodeObject(%i); (%node == 0) ( return %i;

return -1;

This method iterates from 0 to %mountPoints and returns the number of the first mount point wilh no passenger. We can just mount our player to this point, or we can go a step further and find the closest node and mount to it.
function findNearestEmptySeat( %playerObj , %vehicleObj , %mountPoints ) ( %nearest = 1000; %mountNode ~ -1; fori %count ~ 0 ; %count < %mountPoints ; %count++ ) ( %node ~ %vehicleObj.getMountNodeObject(%i); if (%node == 0) ( %distVec = vectorSub( %player.getWorldBoxCenter() , getWords( %vehicleObj.getSlotTransform( %node , 0 , 2) ) ); %nodeDist ~ vectorLen( %distVec ); if ( %nodeDist < %nearest ) ( %nearest = %nodeDist; %mountNode = %node

return %mountNode;

This function behaves much in the same way as the prior seat finder, but it will return the node number for the nearest empty passenger position. Please note that, for this to work, your nodes must be numbered 0 through 7.

7.6 Inventories
It would be fair to say that most games implement some kind of inventory system. The purpose of these systems is to provide a set of mechanisms for storing game items and for later retrieving them. The functions of an inventory are varied, but at their most basic, they must provide the following minimal set of features.

Must be able to store items. This seems obvious, but what does it mean? It means that, when an inventory item is encountered in the world, the
243

---

- - - - - ----------------

Part III

Game Elements

inventory system must provide a means of removing it from the world and storing it for later retrieval. Must be able to retrieve items. Given that the system has stored an item, we will likely need to retrieve the item some time later. The inventory system must provide a mechanism for retrieving the item from storage and placing it back into the game world. In addition to these mandatory features, it is usually beneficial to be able to do the following.

Use an item. What is the use of having an item in inventory if it can't be used for anything? The bulk of responsibility for using should rest with the item itself, but the inventory system must provide a means of getting at the item's use methods. Flexibly handle different item types. The inventory system should be flexible. For example, it would be nice if the system could easily be programmed to do the following: 1. pick up a coin and store it; 2. when a health power-up is encountered, use it if it is needed and store it if not; 3. automatically mount and prepare weapon items if the player doesn't have an active weapon. Limit item carrying. Lastly, an inventory system should be able to just say no. That is, depending on the game genre, an inventory should not allow certain items to be stored, or it should limit how many/much of an object can be placed in it.
The TGE FPS Demo comes with a scripted inventory system that does some of the above tasks as follows. Objects are stored in the player object (the control object). The responsibility for storing, retrieving, and using items is split between the control object, the control object's datablock, and the object being stored/retrieved. Storable items must be predeclared. That is, the control object must be told what inventory items it can store. Storable items are declared and accessed using datablocks as indices into inventory arrays. This allows for item-specific behavior as well as a simple way of referring to inventory slots. It doesn't use the same methodology, nor is it as easily expanded, but the basic TGE FPS Kit inventory can also be maximum-count constrained. Having summarized the TGE inventory system, we will not be discussing it further. Instead, we will be discussing the Simple Inventory System.
244

'-.

Gameplay Classes

Chapcer 7

7.7 The Simple Inventory System fSimplelnventoryj


The Simple Inventory System (subsequently referred to as Simplelnventory) is provided in a fully functional state with the guide, so you could skip this chapter and just use it. However, you'll Jearn a lot more if you continue reading. SimpleInventory has the following attributes.
It is script-based and will work with any TGE game. It is implemented with ScriptObjects and can be placed in any object or

stand alone. In effect, this allows any object to have an inventory or inventories, further compartmenting and structuring game interactions. It is a generalized inventory system, designed to store nonunique items referenced by their datablock names. Items are stored and referenced by their datablock, and thus items with unique properties can be stored, but their uniqueness will be lost. An inventory can store any number of any type of datablock-identified item. A maximum count limit can be set for any specific inventory item. All methods that operate on Simplelnventory are scoped under the Simple Inventory: : namespace. Inventory methods are provided for ShapeBaseData: : to enable a basic set of Simplelnventory interactions:
doPickup () -pick up one instance of an object, doThrow () -throw or drop one instance of an object from inventory,

and
doUse () -use an object from inventory.

Inventory methods are provided for ItemData:: and Item:: classes to complete the inventory functionality.

'.

7.7.1 Designing Simplelnventory


Over the course of the next few pages, we will succinctly discuss the design of Simplelnventory. This will reinforce some scripting topics we have discussed previously as well as give insight into the system such that changing it (if you should choose to) will not be too tedious.

Inventory Builder
Generally, it is better to use a builder (constructor, for you c++ folks), than to hand-build complicated objects. So, we will use one for our inventory system:

245

Part III

Game Elements

newSimpleInventory( %name ) Creates a new simplelnventory object, with optional %name. Prints error message(s) Returns 0 if inventory failed to instantiate. %mylnventory = newSimplelnventory( "backpack" );

The inventory object returned by our inventory builder has the structure shown in Figure 7.9. The inventory itself is a ScriptStructure of inventory. Object. Simplolnvonlory It has an optional name (as (ScriplObJect) provided to the builder function) . It contains a SimSet named knownIteml1acking. This SirnknownttemTrackJng II Set is used to contain the IDs of all items (datablocks) ever stored in the inventory (this is used to simplify content tracking).
Figure 7.9.

[name]

SimSel

Specifying Stored Objects


We have a way to create our inventory object, now we want a way to identify an inventory (storable) item. As we said above, Simplelnventory should be able to store items specified/identified by datablocks. This means we would like to be able to specify our item datablocks something like the following.
datablock ItemData( bullet) ( II specific internal fields not important (yet)

I;

Simple. The above datablock is no different from any other Item Data datablock we would normally specify. This is good because it means we don't need to change our content-creation flow or remember any special rules.

Initial Contents
Next, we need to add a method for initializing the contents of our inventory.
setInvantoryCount ( %theSimpleInventory , %objectName , %nurra:>jects )

Set total number of %objectName objects in the inventory to %numObjects. Returns number of items succesfully set.

246

Gameplay Classes

CIlclpler 7

If you're not examining the code as you read, now would be a good time to open a browser and take a quick peek.at the code for this method (found in "SimplelnventoryGenera1.cs"). This code is fully commented and should be easy to follow. Feel free to peruse this in depth at a later time. For now, please take note of the following important points.
In order to avoid painful bugs, the Simplelnventory system validates arguments and enforces some rules. This is a good practice in general and specifically when dealing with datablocks. Since it will do the same check frequently, the validation code is separated out into a method that does the following . Verifies that %objectName (item to be inventoried) is both an object (exists) and is an ItemData datablock. This inventory system will only inventory ItemData-derived objects, so this is a safe restriction. Forces the %objectName into string format (vs. lD). Why? Recall that datablock names are automatically converted to IDs in some cases. Because we don't want to worry about this, during our day-to-day usage of the inventory system, we'll just make sure that the system itself watches for this and handles it. We need to be consistent when using datablocks as indices. In this case, we're always going to use names because they are easier to identify (than numbers) when llsing dump () and because we generally use names when referring to datablocks in script.

Limiting Inventory Counts


We said above that this inventory system allows limits to be placed on individual inventory item counts. To do this we need another inventory method.
setlnventoryMaxCount( %theSimplelnventory , %objectName , %maxObjects Limits storage of %objectName objects in the inventory to %maxObjects. %rnaxObjects can be: "I', 0, or N > O. A value of "I' clears any prior limit.

The limiting methodology used by SimpleInventory is not elaborate. Basically, a limit can be unspecified ("" meaning no limit), zero (0), or some positive value (N).

Remaining Basic Features


To this point, we have discussed how to create an instance of SimpleInventory, how to specify an inventory (storable) item, how to initialize an inventory instance, and how to limit inventory counts. What is left? Well, we still need the following features.
247

Part III

Game Elements

A means of getting an inventory count for any specific object.


getlnventoryCount( %theSimplelnventory
f

%objectName)

Purpose: Get total number of %objectName objects in the inventory. Returns 0 if none found.

A means of adding new items to the inventory.


addObject ( %theSimplelnventory
f

%objectName [

%numobjects ] )

Purpose: Add one [or %numObjects] %objectName items(s) to the inventory. Returns number of items succesfully added.

A means of retrieving an item(s) from the inventory.


removeObject ( %theSinplelnventory
f

%objectName [

%numobjects ] )

Purpose: Remove [or %numObjects] %objectName item(s) from the inventory. Returns number of items succesfully removed (which may be less than requested count) .

As can be seen, there really isn't much to the design of a simple inventory system. Next we'll address how to use this inventory system.

7.7.2 Using Simplelnventory


Simplelnventory Callback Flows
TGE provides a set of callbacks that "fire" in response to various game events. These callbacks are nothing more than console methods that are scoped to a particular class' datablock. One of these callbacks is the onCollision () method. onCollision () is called for all ShapeBase derivates and Projectiles when a collision occurs in the game. For now, we will limit our discussion to collisions between ShapeBase-derived objects (Player objects specifically) and Item objects.

Picking Up Objects
When a collision occurs between a ShapeBase object and an Item object, the engine will attempt to fire the onCollision () callback for both objects' datablocks. The SimpleInventory system uses the ShapeBaseDa ta: : on248

Gameplay Classes

Chapter 7

Sl1apeBaseDela::

~IeJnverrtory:'

nemData::

Figure 7. TO.

Flow of pickUps after collision.


Suc.cesstLJ Pickup?

TIme

Coll i s ion () callback to initiate pickups. Said pickups follow the flow shown in Figure 7.10.
ShapeBaseDa ta: : onCollision (). Fires on a collision and calls the ShapeBaseData: :doPickup () if the collided object is an Item. ShapeBaseData:: doPickup () . Checks to see if the owner object has an inventory. If so, it calls the item's ItemDa ta: : on Pickup () method. ItemDa ta: : onPickup () . This method will try to place itself in the inventory using the Simplelnventory: :addObject () method. If the item is successfully added to ShapeBase object's inventory, on Pickup () will call Item: : respawn () to temporarily remove (hide) the object from the world. ItemDa ta: : onlnventory (). Often it will be beneficial to have a place to do some extra processing after picking up an item. For example, when picking up a weapon, we would like to use the default flow (to reduce redundant code) but have a simple way of handling mounting, ammo loading. etc. In theory this could be done in the onPickup () by overriding, calling the Parent::, etc. However, this will quickly become an intractable solution for large games. Better is to have an item-specific callback that is executed every time the inventory count for that item is modified. The IternData: : onlnventory () method fills this role. I tam: : respawn (). Depending on the game type we're writing, objects that are picked up should either be respawned or removed permanently from the world. The simple inventory system handles both of these cases. If the dynamic field respawn is set to true in the item's datablock, the item is respawned. If not, the item is permanently removed from the world if the pickup succeeds. The I tern: : respawn () method does the respawning work. Items will respawn (become visible again) in $ Item: : RespawnTime milliseconds.

249

Part 11/

Game Elements

Figure 7. 1 1.

[I-..

ShepeBaseD81s: .

Sh'JpIelnvenory:

lon'('

Flow after a throw request.

------fino

schet1iIePopO )

Throwing/Dropping Inventoried Objects


Assuming we have an item(s) in our inventory, we may at some time wish to throw (drop) it. This kind of action can be accessed through a key press (as well as a myriad of other ways). Key presses are handled by action maps. When the action map dictates that a throw has been requested, it will use a commandToServer () call to call the shapeBaseData: : doTh row () method to start the throw flow (Figure 7.11).

Throw requested. The user requests a throw via mouse click or button press. The action map is programmed to convert this client action into a server action via the commandToServer () function (see "InventoryLesson.cs" and "ServerCommands.cs").
lessonMap.bindCmd( keyboard, "tn, "H, "commandToServer(\'throw\', Inventoryltem.getID()
H

)i

);

'.

ShapeBaseData:: doThrow (). Checks to see if the owner object has an inventory. If so, it calls the I temDa ta: : on Throw () method. If the I temData: : onThrow () method returns a new object handle, the ShapeBaseDa ta: : throwObj ect () method is used to do the throwing. ItemDa ta: : onThrow (). This method will try to extract one instance of the item from the owner's inventory using the Simplelnventory: : removeObject () method. If an intstance is acquired, onThrow () will instantiate (build) a new copy and pass the items handle back to the dOTh row () method.

ShapeBaseData:: schedulePop (). As with the pick-up flow, if the dynamic field respawn is set to true in the item's datablock, the item is

meant to be transient and so should be popped from existence after throwing. The ShapeBaseData: : schedulePop () method does this work. The
250

Gameplay Classes

Chapter 7

Use

User Action

ShapeBaseD8ta':

Simplelnventory: '

ftemData:

Figure 7.12.

Flow after a use request.


Use Succ:ess1uJ?

---------~
Time

I L

r---------ft
IS ""

---------,

to you to deClde ""he! a use means .. ,

I ,

item will pop from existence in $ Item: : PopTime milliseconds after being thrown (dropped). ItemData:: onInventory () . See pickup flow above. ShapeBaseData:: throwObject (). As noted above, this method actually "throws" the newly instatiated item object. This method handles both 1st POV (along eye vector) throws and 3rd POV (arc along forward vector) throws. Throw force is defined in the owner object's dynamic field throwForce.

Using Inventoried Objects


Assuming we have an item in our inventory, we may at some time wish to use it. This kind of action can be accessed through a key press (as well as a myriad of other ways). Key presses are handled by action maps. When the action map dictates that a use has been requested, it will use a commandToServer () call to call the shapeBaseDa ta : : doUse () method to start the use flow (Figure 7.12). Use requested. The user requests a use via mouse click or button press. The action map is programmed to convert this client action into a server action via the commandToServer () function (see "InventoryLesson.cs" and "ServerCommands.cs").
lessonMap.bindCmd( keyboard, "u", "H, "commandToServer(\'use\', InventoryItem.getID()

);H);

ShapeBaseData:: doUse (). Checks to see if the owner object has an inventory. If so, it calls the ItemData: : ooUse () method. ItemData:: onUse (). The coding of this method is entirely dependent upon what the use action means.

251

Part I'll

Game Elements

Item-Specific Responses
For an inventory system to be useful, it has to be somewhat flexible. SimpleInventory was written to be flexible without being too complicated as a first inventory example. The flexibility comes in several flavors.

Pickup Substitutions
Sometimes the pickup object needs to be different from the object we collide with. For example, we might decide to have grenades in our game. We'd like these grenades to come in packages of three grenades. We'd like the following to be true: grenade packs are used for onCollision () to start a grenade pickup flow, individual grenades are stored in the inventory, and individual grenades are thrown. SimpleJnventory allows this by adding an optional dynamic field named InventoryItem to datablocks that need to do a substitution.
datablock ItemData( Grenade) (

II
);

datablock ItemData( GrenadePack ) ( Inventoryltem = Grenade; II Store grenade, not grenade pack

II
);

With the above datablock, we can place grenade packs in the world, but when we pick them up, we get grenades. As noted, Inventoryltem is optional, and if not specified, the datablock name is stored instead. The observant wm notice one small flaw. We haven't specified how many grenades a grenade pack is worth. This leads to the next topic: variable pickup values.

Variable Pickup Values


When we pick up objects, we sometimes want the pickup to be worth one (1) instance, and other times we want it to be worth N instances, where N is nonzero. Simplelnventory allows this by supporting an optional dynamic field named InventoryValue. When this field is present in an item's datablock and that item is picked up, InventoryValue items will be stored. By default, one item is stored. For example, the following code will use the trick we learned above in combination with this new trick to store three grenades when picking up a grenade pack.

252

Gameplay Classes

Chapter 7

datablock ItemData( GrenadePack ) { Inventoryltem = Grenade; II Store 3 grenades instead of 1 grenade pack InventoryValue = 3;

II
};

Variations on onPickup ()
The prior two variations were useful tricks, but what do we do when we want the pickup flow to be completely different? Answer: we write a new onPickup () method. Let's say you have a coin item and a health power-up in your game. For the coin, the default pickup is acceptable. but we would like the health power-up to be automatically applied if the player needs it. and placed in the inventory if not. In order to do this. a new onPickup () method will need to be defined for the health power-up.
datablock ItemData (NormalHealthKit) healValue = 20;
);

II New onPickup(} for Norma1H~althKit function NormalHealthKit::onPickup(%pickupDB,%pickupObj,%ownerObj) ( II Check if player needs healing and apply kit if necessary, else store kit.
So. what about if we have multiple varieties of the health power-up? Is there a way to program this functionality just once? The answer is an emphatic yes. Recall that the className keyword can be used to add an additional level to the namespace of a class. We can use this to create a generic namespace for all health power-ups as follows.
datablock ItemData (NormalHealthKit) className "HealthKit"; healValue = 20;

I;
datablock ItemData (MegaHealthKit) className "HealthKit"; healValue = 100; I;

II New onPickup() for all Health Kits function HealthKit: :onPickup(%pickupDB,%pickupObj,%ownerObj)

253

--

._-- . - - - - - -

Part III

Game Elements

II Check if player needs healing and apply kit if II necessary, else store kit.

Alternate to onPickup () Recall that, in the flows, the onInventory () method was mentioned as a place to put "extra" code. This is still true and is in fact often the place where problems like the health kit above should be solved. It is up to you, but I suggest deferring changes in the flows until after onPickup () , onThrow (), and onUse () . In the end, this will keep your code cleaner and allow you to reuse other flows. For example, we solved the health-kit problem above by writing a new onPickup () . Alternatively, we could have added an onInventory () that would then call the onUse () method if the player needed to heal. We would already need to write the onUse () , so it would be better not to rewrite similar code for healing in an onPickup (), too.
II New onInventory() for all Health Kits function HealthKit: :onInventory( %inventoryDB , %ownerObj, %amount ) ( II If the player needs healing, call the onUse() flow.

Non-Pickup Variations We've discussed the pickup flow to death. What about the other two flows? Both throw and use can be item specific, too. The key is to program variant functionality in the namespace of the object that normally is responsible for deciding what the action means. For throws, it is the ShapeBaseData class that normally decides what a throw is. For uses, it is the ItemData class. Therefore, normally variations of a throw will be programmed in the doTh row () method, and variations on use will be programmed into onUse () . Finally, any time the inventory count for an item chang(2s, the onInventory () method is called, with the inventory DB (datablock), owner ID, and amount (of change). Consider this as a possible place to do your special coding.

Constraining Simplelnventory
As previously mentioned, Simplelnventory does not constrain object pickups. Any item can be picked up, and any number of items can be stored. It is easy to see that this is too simplistic for most uses, but it can quickly be improved upon by adding any or all of the following constraints.

254

r---

Gameplay Classes

Chapter 7

Allowed Items. Add code to predeclare the types of items that can be stored. Disallowed Items. Add code to predeclare the types of items that cannot be stored. Item Count Limit. Add code to limit the maximum number of a specific item that can be carried in the inventory. Total Count Limit. Add code to limit the maximum number of cumulative items (of all types) that can be carried in the inventory. Mass Limit. Add code to track and limit the total mass for all items in the inventory. Please remember that all ShapeBase-derived objects have a mass indicator in their datablock. Bulk Limit. Add a new field to the item's datablock denoting how bulky an item is. Then, add code to the inventory to limit total bulk. Even with these changes, the inventory system may be too restrictive. as it relies on datablocks to index items. This means that only objects using datablocks can be inventoried (not a big restriction). and all data in the object instances themselves are lost (can be a big problem). If you are programming a role-playing game (RPG) or similar game. it will be useful to allow object instances to be unique; e.g., this is Bob's sword, or these boots are damaged. Therefore, it will be absolutely required that objects that are stored in the inventory be faithfully re-created at a later date, and if you want to stick with a script-only system, you must find a way to determine the fields in an object and then to store them. This will require coding an extension into the engine.

7.7.3 General Inventory Tips and Gotchas


While coding up Simplelnventory, I ran into some issues. So, rather than let you stumble on them, too, I'm supplying them here. Datablock names as indices and arguments. Remember that 5impleInventory uses data block names both to index arrays of inventory items and as arguments in all the functions. Also, remember that the engine may automatically convert these names to ID numbers. This can cause a mismatch in the inventory lookup. So, when in doubt. use the getName () method. For an example of how this is used, see the Sirnplelnventory: : verifyArgs () method. Item Behaviors. Remember that inventory items are based on the Item class. For items to work appropriately as an inventory item. they must be properly configured. I tern. 5 ta tic. If you intend to be able to throw an object. this must be false; otherwise, the object will stay where it was spawned.

255

Part III

Game Elements

ItemData. sticky. If you want a thrown object to stop when it hits the ground. set sticky to true. ItemData. friction. Setting friction to a value of about 0.7 will cause a thrown object to arrest its motion quickly. ItemData. mass. If you intend to throw the object, it must have a positive mass. Using applyImpulse () on a ShapeBase object with zero mass will crash the engine.

Motivation for using ScriptObject. Perhaps this should have been explained

earlier. but as SimpleInventory uses script objects, they can be placed anywhere and in anything, including in other inventories. Also, why just have one?

7.7.4 Inventory Validation


SimpleInventory comes with code to validate that the basic functions of the inventory system are working properly. This code is located in "SimpleInventoryValidation.cs" and is run every time the GPGT Lesson Kit is started. This code may be disabled, but it is a short test and won't affect anything after running. To see if the system is working. search for the words "Validating Simple Inventory System" and check for error messages. Also, if you do decide to edit the system, you can cause it to reload the inventory system scripts and to rerun the validation scripts by typing the following in the console.
sris () ;

7.7.5 Maze Runner Lesson #7 (90 Percent StepJPreparing Our Game Inventory
In this short lesson, we will examine the steps required to get our player (MazeRunnerPlayer) to use the SimpleInventory system to pick up coins.

Loading the Inventory System


In order to use our inventory system, we must ensure that it is getting loaded. In fact, we have already done this first step. When we set up our "MazeRunner" directory and copied the Maze Runner prototype directory into it. we modified the file "\MazeRunner\prototype\main.cs". We had it load the inventory system's main script file. as follows.
function onStart() II in main.cs { II MazeRunner exec (\\ . IEGSys tems/Simplelnventorylegs_ Simplelnventory . cs") ;

256

Gameplay Classes

Chapter 7

II MazeRunner exec("./EGSystems/SimpleTaskMgr/egs SimpleTaskMgr.cs"); I I ..


This then loaded the other script files that comprise this system.

II In egs Simplelnventory.cs exec("./SirnplelnventoryBuilder.cs") ; exec("./SimplelnventoryGeneral.cs") ; exec ("./SimplelnventoryValidation.cs") ;


Adding an Inventory
With the inventory system being loaded, we now have to hook it to any classes that wish to "own" an inventory. The simplest way to do this is to have each class add an inventory system to the object when the object's onAdd () callback is executed. Take a look in the file "\MazeRunner\prototype\server\scripts\GPGTBase\ Player\playerDataConsoleMethods.cs". It contains the definitions for all of the important callbacks used by a player class. All of these callbacks are scoped to PlayerData: :, ensuring that they will be called unless a new datablock, deriving from PlayerDa ta: :, redefines the callbacks. We are already loading this script file, so we get the benefit of aH of these callbacks already. One of these callbacks is PlayerData: : onAdd () which, among the other things that it does, creates an inventory and saves a reference to it in the player object.
function PlayerData: :onAdd(%DB,%Objl

II

Parent::onAdd(%DB,%Obj) ;

II 2
%Obj.enableMountVehicle true;

II 3.
%Obj.mylnventory
~

newSimplelnventory();

%Obj.mylnventory.setOwner(%Obj);

This means that we do not have any work to do. We do not have to implement a new version of onAdd () scoped to MazeRunnerPlayer: :, but if we wanted to, we could write one like this:

257

---

- --------------

-------- -

...

Pan II,'

Game Elements

function MazeRunnerPlayer: : onAdd ( %DB / %Obj ) ( II Usually called first Parent: :onAdd( %DB / %Obj ) i

II Other statments here ...


j

Removing an Inventory
It is normal to destroy objects created in the onAdd () callback when the onRemove () callback is executed.

Again, this is taken care of fOT us by the base code we are using from the GPGT Lesson Kit. The following is the onRemove () callback from the same file we just examined above.
function PlayerData: :onRemove(%DB/%Obj)

II 1 i f ( isObject ( %Obj. mylnventory ) ) %Obj. mylnventory. delete () i II 2


Parent: :onRemove(%DB/%Objli

Easy as pie! Of course, we could again write a specialized version of the onRemove (l callback and just be sure to call the Parent: : version at some point (normally last).
function MazeRunnerPlayer: :onRemove( %DB / %Obj ) II Other statements here ... (

Parent: : onRemove ( %DB / %Obj )i II Usually called last

What About Constraining?


In our game, we don't want to constrain the inventory, but if we wanted, for some reason, to prevent the player from picking up coins, we could simply modify the onAdd () callback to look like the following.
function MazeRunner::onAdd( %DB / %Obj ) { Parent: : onAdd ( %DB / %Obj ) i

II No coins for you!


%obj.myrnventory.setrnventoryMaxCount( Coin / 0 );

258

Gameplay Classes

Chapter 7

In Review
I know you're disappointed that there was no work to do in this lesson. So, let's just summarize the steps instead. This way you will know what they are when you are on your own. 1. Load inventory system scripts. 2. Ensure that the onAdd () callback adds an inventory to the object when it is created. 3. In your own onAdd (), be sure to constrain the inventory system as is required by your game. Use the contraint methods included with the inventory system. 4. Make sure that the
onRemove ()

callback deletes the inventory.

7.8 Gameplay Classes Summary


We started this chapter by discussing the idea of gameplay. I proposed that interaction is a major element of gameplay, setting the stage for our discussion of the gameplay classes (classes implementing player interaction with the world). We closed the introductory material by summarizing the primary gameplay classes: Camera, Player, and Vehicle. Our first gameplay discussion was centered on the Camera and CameraData classes but cast a wide net about other concepts which we generally labeled game view (a combination of POV, FOV, control object, free camera, and zooming). We talked about game view for a bit, observing the fact that other classes interacted with the camera to define the concept. We then discussed the individual game view components in detail, describing each of them. We also discussed the side topic of render scoping and the fact that it is controlled by the control object. Having warmed up properly, we looked into class interactions in detail and closed our game view discussion with six (cookbook) examples of game view control, including: two methods to force 1st POV (one with a limited FOV), forced 3rd POV, a method of enabling 1st or 3rd POV, the correct settings to allow a camera to use its own parameters instead of those from the object it is attached to, and the way a camera can be made to switch to using a vehicle's view settings (not the player's) upon player-to-vehicle mounting. Our next discussion included the Player and PlayerData classes. We learned about all of the features provided by this important set of classes, including rendering features, forces and factors (speeds, delays, resistance, 259

Part III

Game Elements

etc.), pickup radius, looking angle limits (restrictions on view angles for cameras attached to players), the difference between an impact and a collision, special effects, and the standard player animations. We ended the discussion by making a simple player for use in our game. After Players, we discussed the various Vehicle classes, and to start the discussion off properly, we talked about general vehicle attributes. Geometries. Chassis, tires, and collision meshes. Nodes. Camera, tire, and special effect nodes. Animations. Back, bot, brakelight, spring, and steering. Once the most general discussion of vehicles was completed, we talked about the base classes for all vehicles: Vehicle and VehicleData. We discussed the features these classes brought to the table, including physics, steering, jetting, impacts, camera features, and emitters. We ended with a general discussion on mounting players to vehicles. Done with the general vehicle discussions, we talked about the WheeledVehicle and WheeledVehicleData classes. We learned about how to program the basic engine and braking parameters as well as about controlling the look of the wheel rotation animation. The WheeledVehicle class uses several datablocks, including the WheeledVehicleTire and WheeledVehicleSpring datablocks. We discussed these in order and learned about the following properties for each.
WheeledVehicleTire. We learned that we can implement up to eight

tires per wheeled vehicle using this class to represent the tires. Also, we saw that it is acceptable to mix tires on a vehicle. Friction. We learned that the tires provide all vehicle friction as long as the chassis is not in contact with the ground. Motivational forces. We discussed the fact that tires provide both longitudinal (forward-and-backward) and lateral (side-to-side) forces, which act together to move our wheeled vehicles and to maintain their heading. Tire radius. We examined this attribute and saw that it is important that it should match our tire model for correct visual behavior.
WheeledVehicleSpring. We learned that this class represents the

"shocks" for our wheeled vehicles. Damping forces. We learned about how damping is used to control the expansion and contraction rates for our springs and therefore the tires. These forces allow us to create very soft to very hard springs with varying rates of recovery. Anti-sway. We learned how the anti-sway force in the springs helps keep the vehicle's chassis level relative to the surface below the vehicle.
260

Gameplay Classes

Chapter 7

Length of travel. Here we learned how to reduce the distance a tire hub

may travel. We closed our wheeled vehicle discussion by talking about powered wheels and their effect on driving performance, followed by a set of examples showing how to choose alternate mounting positions. Next up, we talked about hover vehicles. We learned how to control our horizontal motion and how to implement a certain amount of drag in order to slow a travelling hover vehicle. We then discussed some factors that affect the vehicle when it comes into contact with the ground, water, or an interior versus when it is floating free of obstructions. We discussed hovering and jetting, as well as how to stabilize the vehicle and ensure that it remains upright. We talked about steering and ended with a discussion of how to keep our parked hover vehicles from floating away down a hill (the same method applied to stopping an unmanned vehicle). The final section in this chapter took a sharp turn and talked about a concept instead of a particular class. That concept is the inventory (or inventory system). We talked about what an inventory system is and why it is needed. Then we compared the features provided by the inventory system that comes with this guide (SimpleInventory) against the one implemented in the TGE FPS Demo. Once we were done with explanations and motivations, we jumped into a review of the implementation and usage of Simplelnventory. This discussion included detailed flows of pickups, throws/drops, and uses, discussing the scripts and classes involved as well as laying out motivations for the way the inventory behaves. To complete our discussion of inventories, we talked about various means of modifying the standard flow and ways to improve upon the system. This chapter contained no shortage of difficult to understand and even harder to remember details about interaction. Unfortunately, to successfully create your game, you need to undestand what we have discussed, so I suggest rereading this chapter and reviewing the samples that come with the GPGT Lesson Kit. When you are welt educated in these topics, you will be a long ways toward succesfully creating a game.

261

ChapterS Mission Objects


8.1 Mission Objects
This mega-chapter covers most of the objects that can be placed using the Mission Editor Creator. I call this a mega-chapter because it encapsulates a large series of object descriptions as well as tips on using and/or scripting them. If you are reading this chapter first, some of what you read here may not make a great deal of sense due to some holes in your TGE education. Those holes are filled in the prior chapters. So, if you do find this material confusing, please go back and read (or at least scan) the chapters that precede this one. Be warned: some of the objects described in this chapter are not simple. You will need to experiment with them to fully understand their capabilities, but this chapter should get you started down the right path. The primary goal here is to familiarize you with these objects and some of their attributes as well as to help you with any peculiarities. I won't necessarily cover every attribute of these objects in this chapter. Instead, an appendix is supplied, giving details on each object. Finally, it is assumed that you are familiar with the built-in tool set. If not, go back and read Chapter 3, "Torque Tools." When you are ready, come back and check this chapter out.
Throughout this guide and therefore in this chapter, we have exclusively used the term "world unit" instead of meter. However, in the GarageGames forums and on the Torque IRe channel, you may see people refer to things in terms of meters. Because some standard measurements such as acceleration due to gravity are set at metric standard values (9.81 world units per second squaredj it is easy to fall into the belief that the system is actually metric and that distances are measured in meters. In fact, the engine is unitless with respect to most measurements excluding time. However, as the engine has been given metriclike values for all important constants, this discussion of meters versus world units becomes a question of semantics. Because I wanted to insure that this guide would always be accurate with reference to measurements, I have chosen to use world units instead of meters, but you should not be confused when you see other sources of information on Torque reference meters.

8.2 Terrain
In Torque, terrain is represented by an infinitely repeating heightmap. The heightmap itself is usually represented by a 256 x 256 full-color (24-bit) PNG
263

Part III

Game Elements

Figure 8.1.

Terrain repeating.

image. The engine uses this single image as a home tile, which is edge-blended and infinitely repeated in the world plane (Figure 8.1). The default real-world measure of the home tile is 2048 world units on edge.

8.2. 1 Terrain Features


Terrain has the following features.

Detail texture. A texture used to give more detail to locally visible terrain. Bump mapping. The terrain supports emboss-style bump mapping, using a single source texture. In-game editing. With the Terrain Editor and the Terrain Painter, you can hand modify the shape and texturing of your terrain without leaving the game. This is described in Chapter 3. Algorithmic generation. The Terraformer provides a tool-set of algorithms for generating terrains. This is described in Chapter 3. Algorithmic painting. The Terrain Texture Editor provides a tool-set of algorithms for applying textures to the terrain. This is described in Chapter 3. Alternate sizing. Although it is advisable, one does not need to stick to a 2048-world unit square home tile. No terrain. Finally, if not needed, the terrain can be removed entirely.

8.2.2 The Detail Texture


When you first start working with the terrain, it is easy to be overwhelmed and to miss an interesting yet important feature, namely the detail texture. If you open up the Inspector and select the terrain, you will see that there is a field named detail Texture under the Media SimGroup. This field provides the path to a texture that will be used to add detail to the local terrain. This additional texture is rendered once every world unit for n world units. Additionally, it is blended with the underlying textures with a ratio that falls off to zero at about 64 world units from the camera. Look at the screen shots in Figure 8.2 to see the difference between terrain with and without a detail texture. I think you'll agree that the one with a detail texture is much nicer.. Great, right? Well, yes and no. Yes, because the terrain definitely looks better with a detail texture. No, because you can only have one per mission, which means all terrain in any single mission will have a fundamental sameness to it. For the most part, this is not a big deal, and most players won't even notice. However, you need to realize that your choice of detail texture can have a big impact on the visual quality of your terrain, and you should probably count on having different textures for different levels/missions, as this is a subtle way of creating distinct ambiences from level to level.

264

Mission Objects

Chapter 8

Figure 8.2.

Detail texture.

Terrain with Detail Texture

Terrain without Detail Texture

Detail textures may be any size between 1 x 1 pixels and 512 x 512 pixels as long as they follow the standard rules for textures used by Torque. See Appendix 0.1, "TGE Must-Know Facts," for information on TGE's texture rules.

8.2.3 Bump Mapping


This feature is controlled by four terrain parameters and a preference variable. It is simplest to edit the terrain parameters using the Inspector (Figure 8.3).
bumpTexture. Specifies a texture

to use as the emboss map. Must follow Torque scaling standards for bitmaps, should be a mixture of blacks and whites, and it should tile. You must save the mission and reload for this to take effect. The engine uses this texture to create the two textures required for embossing. One is the original; the second is the inverted original.
bumpScale.

r=-::::...--~---~-~--'"'t Figure 8.3. Editing terrain parameters.

MedIa

Determines how stretched the bump-map texture is. In other words, small numbers cause the emboss map to cover a very small area, giving a more finely detailed bump mapping. the emboss bump-map effect.

bumpOffset. Is the diagonal offset between the two textures that make up zeroBumpScale. Controls the bump-mapping radius. If you consider that bump mapping is only enabled within this radius (centered about camera), then it will be easy to understand that smaller values will cause the bump

265

Part III

Game Elements

mapping to cease nearer to the camera, while larger values will make it stretch further into the visible distance. As noted, there is one preference variable.
pref:: Terrain: : enableEmbossBumps. Allows you to disable this fea-

ture, which could be necessary on a slow machine or an older video card. Figure 8.4 illustrates the effects of these variables.
Figure 8.4.

obhoW 10 JW.I
l'!..,..;) ....

Changing terrain parameters.

Base Texture (scaleTerrain. png)

......,...,JlO1'l aoukl wIT


""l"'oT oJ obif;)

BumpO (bumpO.png)

Bumpl (bumpl.png)

bumpTexture - BumpO bumpScale - 3 bumpOffset - 0.015 zeroBumpScale - 2

bumpTexture - BumpO bumpScale - 8 bumpOffset - 0.015 zeroBumpScale - 2

bumpTexture - Bumpl bumpScale - 3 bumpOffset - 0.Q15 zeroBumpScale - 2

I I I

bumpTexture - BumpO bump5cale - 3 bumpOffset - 0.04 zeroBump5cale - 2

bumpTexture - BumpO bumpScale - 3 bumpOffset - ( -0.04 ) zeroBumpScale - 2

bumpTexture - Bumpl bumpScale - 3 bumpOffset - 0.04 zeroBumpScale - 2

266

bumpTexture - BumpO bumpScale - 16 bumpOffset - 0.05 zeroBumpScale - 6 Camera Distance - 80

bumpTexture - BumpO bumpScale - 16 bumpOffset - 0.05 zeroBumpScale - 6 Camera Distance - 95

bumpTexture - BumpO bumpScale - 16 bumpOffset - 0.05 zeroBumpScale - 6 Camera Distance - 100

I--- -_.

Mission Objects

Chapter 8

8.2.4 More about Terrain Painting


Although it might seem obvious, I'll say explicitly that the textures used to paint the terrain should be seamless. Why? Well, because the textures are repeated every squaresize world units. This means that, with a default squaresize of 8, a painting texture repeats after only 8 world units. Regardless, if your textures are not seamless, it will be noticeable.

8.2.5 Alternate Terrain Sizing


Interestingly, when people start playing with Torque, they soon realize that the terrain tiles. Then, after asking around, they realize that the map is "only" 2 kIn x 2 km. A percentage of these people have in mind making some kind of game that would require a much larger terrain, say a massively multiplayer online role-playing game (MMORPG). They immediately focus on the problem of making the terrain bigger. In fact, if you are reading this, I imagine that you might be one of those people. Now, I'm not going to say that you cannot scale the terrain, nor am I going to say that you cannot expand the tiling feature to include multiple unique tiles. You can do these things, but they are not trivial. I will provide two suggestions to the alternate terrain sizing problem and then leave the hard work to you.

Modifying squaresize
The easiest means (although not very robust) of modifying the terrain size is to change the terrain object's squaresize parameter. This parameter can be edited in the Inspector and can be found in the terrain's Misc SimGroup. What does changing the value do? If you will recall, the terrain heightmap is really nothing more than a two-dimensional array of values. Furthermore, we normally represent height maps as a bitmap that (in Torque) is 256 pixels on a side. squaresize is a multiplier that specifies how many world units apart the pixels are in the heightmap. Sounds simple, right? In a sense, it is. Legal values for squaresi ze are between 2 and 64 and are not strictly limited to multiples of two, meaning you can have the map sizes shown in Table 8.1.
, (

~~"'~,j.JI"T~;-~~"'-t':~: ...Il.:~":.
..d~.~~ ..I#'J"""_A ...

,-k-j: _ ..... ~

Table 8.1 .

2 4
8 (default)

512 world units squared 1024 world units squared 2048 world units squared 2304 world units squared

Map sizes.

64

16,000 world units squared (this is 256 million square world units!)

267

Part III

Game Elements

This seems good at first, but once we start playing around with it, we start to see problems. The one most people notice right away is "water holes." At nonstandard square sizes, water blocks will sometimes exhibit holes-that is, a square region where there should be water, but no water is rendered. This is very annoying. Another problem is collision. Terrain collision is affected negatively by larger square sizes. This can be so serious that the player may actually fall through the terrain in some places. Finally, we run into the more subtle issues of memory usage and texture bandwidth. Varying squaresize modifies both memory usage and texture bandwidth associated with terrain rendering. I have personally noticed that a squaresize of 2 severely reduces frame rate. So, given all these bad things, should you use this method? Sure, but only if you want to go up or down by a factor of 2. Then, this is a good partial solution. I say partial because there are ways of solving the problems noted above. However, I'm going to leave this as an exercise for the reader.

Atlas
OK, I admit it. Changing the squaresi ze is not that great an idea. Sure, it works in limited cases, but what if you want to make that really big MMORPG? Well, I must suggest that you move up to the Torque Shader Engine (TSE). TSE is a child of TGE that encompasses several new sets of features. The first, and most obvious, of these is shaders, hence the name. Less well known is the use of Atlas. Atlas is the terrain-engine manager for TSE. It can handle any size terrain, and I mean any size. So, if you really, really, really must make a big terrain, go ahead and try out TSE and Atlas. However, although I do encourage you to move up to TSE, I don't necessarily suggest that you start off making an MMORPG as a first game. Read on to understand my reasoning.

..

8.2.6 Big Terrains: Don't Do It!


I want you to stop and consider this simple question: How are you going to populate this very large world you wish to make? This might seem like a silly question, but let me assure you that it is not. lance read something to the effect that the people who made Ihbes 2 were a bit worried about the map size being a limitation but quickly realized that it is very difficult to actually fill four square kiloworld units of space. In fact, most missions in Ihbes 2 are much smaller than the maximum map size. OK, you may still be thinking something like: Yeah, but I can walk all the way across the map in, like, no time flat! In fact, traveling at top speed, it will

268

Mission Objects

Chaprer 8

take you just shy of 2.5 minutes to walk from one side of the map to the other. This would make the Torque character pretty darned fast. In fact, the default maximum (unmodified) speed for the character is 68 kiloworld units per hour. Normal humans sprint at somewhere near 30 kiloworld units per hour maximum, but it just feels too slow to make the character walk and run at normal human speeds. This information is important for the following reasons.
1. You are going to have a heck of a time populating 4 square kiloworld units, which is equivalent to about 400 square city blocks (there is no official dimension for a city block, but they average between 100 to 200 world units on end).

2. There are other solutions. Just use the tiled terrain. Who is going to notice that it repeats if it takes 2.5 minutes to run across it? Slow the character down and tighten up spacing on objects. This is easier to do than increasing the size of the terrain. Guaranteed! 3. This is really going to hurt and you don't want to do it. OK, I'm not exactly telling the truth, but I can say that it is not simple to do this.

8.2.7 No Terrain?
If you wish to have a terrainless mission, it is entirely possible. However, you may have to edit the mission file to do this. Thying to delete the terrain from the Inspector is a bit tricky. You have to unlock the terrain (set dynamic field locked to false), and then you have to delete it. My suggestion is that you simply open your mission file in any handy text editor, find the block named TerrainBlock, and delete the entire thing. Oh, you might want to put something in the world for your player to stand on, or the next time you open the mission, the player will fall forever.

8.3 Water (BlocksJ


After terrain, water is another hot forum topic. Fortunately, water has gotten a lot of attention from community members. However, this additional attention has had the side effect of making water seem complicated to use. In reality, most options are just that-optional. You can place and set up water in just seconds, or if you want to go for a specific effect, you can spend hours tweaking the parameters. For the sake of brevity, I will give the quick setup instructions first, then I'll cover the advanced options.

269

Part III

Game Elements

8.3.1 Basic Water (Quick Setup)


OK, get your stopwatch out. Start it. Now follow these instructions: 1. 2. 3. 4. S. 6. 7. Start the CPCT Lesson Kit. Open the World Editor training mission. Start the Mission Editor. Switch to the Creator tool. Switch to free-camera mode and move the camera up a few world units. Look somewhere near your character. Insert a new water block (Mission Object -7 Environment -7 Water).

8. 9. 10. 11. 12.


13. 14. 15. 16.

lust Click OK for the dialog that comes up. Switch to the Inspector tool. Click on the water block. Click the Expand All button. Change Media -7 SurfaceTexture to "gpgtjdatajGPGTBasejwaterj howwaterO". Make sure Debugging -7 UseDepthMask is not checked. Set Surface -7 surfaceOpaci ty to 1.0. Set Surface -7 envMapIntensi ty to 0.0. Click Apply.

Done! Depending on the speed of your machine, that should have taken about 60 seconds or less.

8.3.2 Water Features


Water has the .following features.

Discrete scaling. Because of the algorithmic nature of the water in Torque, water blocks are scaled in fixed increments. By default, this is 32 world units. Discrete positioning. Again, as a byproduct of its algorithmic nature (and due to a sometimes overlooked terrain relationship), water is positioned in fixed increments. By default, this is 8 world units, i.e., squaresize. Various texture-based effects. Basic surface texture. Plain-lane base texture for water. Shore texture. An additional texture for shorelines. Over and under environmental maps. Static environmental reflections on the surface of water from above and below. Specular reflections. Simulates perturbed specular reflection from water surface.

270

Mission Objects

Chapter B

Underwater fog. Torque provides a static fog for when the camera is underwater. Underwater texturing. Under certain circumstances, up to two additional caustic textures will be rendered over the view. Waves. Torque supports sinusoidal waves. Viscosity and density. These two real-world characteristics affect the characters and objects that encounter the water. Predefined water types. Torque provides several predefined types of water that give you various ready-made effects. Flow. Torque can Visually simulate flowing water. Distortion. If the above visual effects are not enough, you can use distortion parameters to make the water yet more realistic or unrealistic if you so choose Multiple blocks. Last, you may have multiple independent blocks of water.

8.3.3 Advanced Water


All right, unless you are just goofing around and learning the engine, it is likely that you will want to make your water look a little more interesting. No problem there. Water blocks can do some very cool things. Position and Scale Before we jump into the cool stuff, let's briefly discuss basic positioning and scaling. Unlike most objects, you cannot position or scale water blocks arbitrarily. Instead, the x and y components of both position and scale are adjusted in discrete steps. Position < x, y> is adjusted in steps of 8, and Scale < x, y> is adjusted in steps of 32. For both position and scale, the z parameter can be adjusted continuously. On a side note, if you have been reading this guide straight through, you may recall that the default terrain squares i ze is also 8. It is no coincidence that both position and scale are adjusted in multiples of squaresi ze. If you are going to play with nonstandard terrain sizes, or if you are going to make modifications to the way water blocks work, you'll have to remember that terrain and water are closely related. Kissing cousins, you might say. It is very important to note that the z parameter should not be zero. Most people make the mistake of not adjusting this parameter. Most of the time, this will seem OK, but if the camera will ever be under the surface of the water, then you must have a positive value for z. More accurately, you must adjust the z parameter of a water block, such that the lower boundary of the water block is below the lowest point in the terrain, for all points in the terrain covered by
271

-----._----- -

Part III

Game Elements

the block. Why? If you do not do this, you may encounter a strange bug where the water fog disappears at certain viewing angles. This can destroy any suspension of disbelief you have managed to accrue, and it is very distracting.

The Various Textures (MediaJ


The water block has progressed greatly since the day Torque was first released. With this progression has come a profusion of new parameters, including a multitude of texture parameters. Fortunately, these parameters are simple to understand.
surfaceTexture. This texture is used to define the base water layer(s). This texture is rendered in two layers, with one layer reoriented at a 45degree angle (about z, of course). This makes the water more interesting. shoreTexture. We'll talk more about shorelines in a moment, but Torque has the ability to render shorelines differently. When it renders the shoreline, it blends this texture with 5urfaceTexture, giving a nice visual effect.

envMapOverTexture. If environmental mapping (see "Reflections and

Specular Masks" below) is enabled, this texture is rendered when looking down onto the water from above. This represents an environmental reflection on the water's surface.
envMapUnderTexture. As with envMapOverTexture, this represents an

environmental reflection, but this is the texture you will see if looking up from beneath the water. submergeTextureO and submergeTexturel. These two textures are only used when liquidType is one of the lava types (Lava, HotLava, or CrustyLava). These two textures are rendered perpendicular to the viewing plane. Additionally, they are animated. A suggestion I was given, which I'll pass along, is to use two high-quality (say 512 x 512 instead of the normal 256 x 256) grayscale caustics for these. Note: By making some simple changes to the source code, you can colorize the resultant output to the screen.
specularMaskTex. This texture is used to make the surface of the water look as if it is reflecting light. Again, this should be some kind of caustic grayscale. The engine does take into account the position and elevation of the sun when rendering the specular effect.

Makin' Waves
The water would not be very interesting if it were just a flat plane. Fortunately, Torque supports a wave feature. The bad part is that it is a simple sinusoidal function. Nonetheless, it does a good job and looks good for most purposes. If you wish to have waves, set the WaveMagni tude parameter to a nonzero value. Bigger values equal bigger waves. Note that it is best not to attempt to place two water blocks side by side if you are using waves. Because the algorithms for

272

~--

Mission Objects

Chapter 8

each block are calculated separately, you will get visible seams and discontinuities. Also note that there is one disappointing thing about waves. If your player is floating in water (see "Sinking and Floating" below), the waves will not lift the player; that is, the water motion does not affect the player's vertical position, nor will splash effects occur from water hitting a motionless player.

Sinking and Floating


You may be wondering about how to make a character float, or perhaps you would like to make the water more viscous, say like quicksand. Well. Torque supports two water parameters for these effects: density. The default water density is 1. Meanwhile, the default character density is 10. This means that the character will sink upon entering the water. Therefore, if you want the character to be more buoyant, you can adjust either or both parameters. Just remember the following rules: water density < player density -7 Player sinks. water density = = player density -7 Player neither sinks nor floats. water density > player density -7 Player floats . viscosity. In addition to choosing whether a character will float or sink in water, we can indirectly adjust how quickly this occurs by changing the viscosi ty of the water. A thicker fluid like, say, honey has a high viscosity, whereas plain water will have a low viscosity. By increasing this value, you create an effect where the player will require more time to float or sink.

This also affects the player's ability to walk through water. If the viscosityofthe water is high and the player is hip-high (model's centroid is SUbmerged) or further submerged, the player will begin to slow appreciably while walking.

Liquid Types
The liquidType parameter was mentioned briefly above. Out of the box, Torque supports several water types. They are legacy types from the 1Hbes 2 days. Unfortunately, they are not all distinct any longer. Now you have three basic categories. Basic water types. All these behave similarly: Water, OceanWater, RiverWater, and StagnantWater. By default. all three lava types apply the same damage, but you can change this by specifying your own values in the $DamageLava, $DamageHotLava, and $DamageCrustyLava parameters. Please note that your own scripts will have to use these settings to apply damage.

"

Lava types. These cause damage when the player enters the water block, but not while the player is submerged. It is up to you to write scripts that apply damage while the player is submerged. The reason for this is flexibility. Instead of forcing a fixed iterative damage on users, the creators of TGE decided to leave subsequent iterative damage up to us. When the water type is one of the three lavas, submergeTextureO and submergeTexturel will be rendered if you have specified them. Lava. Damage parameter is $DamageLava. Hot Lava. Damage parameter is $DamageHotLava. CrustyLava. Damage parameter is $DamageCrustyLava.

273

Part III

Game Elements

Quicksand. This behaves just like water, except that the underwater fog

does not render. Any other behaviors are up to us and our scripts. For most purposes, a liquidType of either Water or Lava will suffice.

Underwater Fog
So, what is underwater fog? It is the effect of water coloration and dimming that can be attributed to the physical effect of light passing through water. Until version 1.4, TGE employed a fixed color for water fog, which could not be adjusted via script. If you are still working with version 1.3 or prior, I suggest exposing the parameter that affects fog color to the console. As of this time, that code exists at about line 900 in "game.cc". Just look for the following code.
glColor4f(.2, .6, .6, .3);

Fortunately, if you are using version 1.4 of the engine, a color vector is now exposed under the name underwaterFog and can be modified from the Inspector and from scripts.

WaterFlow
So far, we've talked about how to make waves, but what about horizontal effects, like water flow? Torque supports this too. You can cause specific textures to translate over time, giving the illusion of water flow. The following parameters are involved.
FlowRate. If this value is nonzero, water flow will be enabled. The higher

the value, the more quickly textures will translate. The following textures flow. nonoriented surfaceTexture.
shoreTexture. FlowAngle. This parameter (in degrees) determines the direction of the translation. The following values demonstrate the direction of flow based on angle. 0. Textures will translate in the negative direction along the world xaxis. 90. Textures will translate in the negative direction along the world yaxis. SurfaceParallax. When FlowRate is non-zero, the flow rate of the oriented surfaceTexture is controlled by this value as shown in Table 8.2.

274

Mission Objects

Chapter 8

SurfacePaz:alla
Magnitude greater than 1 Nonoriented surfaceTexture flows more slowly than oriented surfaceTexture. Nonoriented surfaceTexture and oriented surfaceTexture flow at same rate. Oriented surfaceTexture flows more slowly than nonoriented surfaceTexture. Oriented surfaceTexture remains stationary. Oriented surfaceTexture counterflows.

Table 8.2. Flow rate of surfaceTexture.

Magnitude equals 1

Magnitude less than 1

Magnitude equals 0 Negative values

Water Distortion In addition to supporting waves and water flow, Torque supports a distortion feature. It is difficult to classify this effect, because by varying the distortion parameters, you can get wildly different effects. However, the basis for these effects is simply the stretching and squeezing of the surfaceTexture's and shoreTexture's uv coordinates across a defined grid. The following parameters are involved.
DistortGridScale. You don't normally need to vary this from its default

value unless you have scaled your water. This allows you to adjust distortion such that the effect is the same between a large water block and a small water block. DistortMag. If this value is nonzero, distortion is enabled. Generally, the magnitude of this value should be less than 1 or the distortion behaves strangely. Both positive and negative values are legal. DistortTime. As you might guess, this is the period of the distort function. It is inversely proportional to the distortion's rate of change. In other words, larger values mean slower distortions and smaller values mean faster distortions. A value of zero is illegal and will cause the texture rendering to fail gracefully. Realistic Shoreline Rendering We've mentioned the shoreTexture several times now but avoided discussing how and when it is used. TGE multitextures the shoreTexture with the surfaceTexture based on the depth at that location and the following parameters.
ShoreDepth. Shore rendering is determined by a ray cast at distinct points

across the surface of the water block. The result of this ray cast returns the distance between the top of the water and the terrain directly below that point on the surface. If this value is greater than or equal to Shore Depth,

275

Part III

Game Elements

Figure 8.5.

Depth versus alpha curves.

Sigmoid (prior to version 1.2)

o < DepthGradient < 1 Fast Fade Out, Slow Fade In

DepthGradient = = 1

DepthGradient > 1 Slow Fade Out, Fast Fade In

the engine is instructed to render the shoreTexture. If you choose to set this value to zero, the shoreTexture will not render at all. As might be intuited, these two parameters determine the minimum and maximum alpha to use while rendering shoreTexture. This directly affects the multitexturing equation involving the surfaceTexture and shoreTexture. DepthGradient. Controls the slope between MinAlpha and MaxAlpha. In older versions of the engine, this was implemented as a sigmoid function, but since version 1.2, it has been implemented using the (more involved) gamma-correction function. This gives us the depth versus alpha curves shown in Figure 8.5.
MinAlpha/MaxAlpha.

Reflections and Specular Masks


TGE doesn't support real-time reflections (out of the box), but it does support the next best thing, which is a static environment map. In fact, as noted above, it supports two maps, one for above the water and the other for below. In addition to being able to specify these two environment maps (using envMapOverTexture and envMapUnderTexture, respectively), you determine how they blend by adjusting the envMaplntensity parameter. Legal values are between 0 and 1.
276

1------

-------

Mission Objects

Chapter 8

In addition to environmental mapping, TeE supports specular masks to simulate highlights. The specular mask is used to make the surface of the water shiny, that is, to provide interesting looking highlights. When you use a specular mask, the engine will render highlights, based on the texture you provide (specularMaskTex), the position of the sun, the elevation and inclination of the camera, and two additional specular parameters.
specularPower. This determines how large an area is shiny. Lower val-

ues cause more of the specular map to be rendered; larger values will tend to show just a spot of highlighting. specularColor. This can be used to change both the color of the resultant highlight and its intensity. This parameter takes a 4-tuple floatingpoint vector "r g b a." The specularMaskTex should be a grayscale caustic for a natural-looking water highlight.

Texture Scaling
1\vo parameters have been provided to allow you to modify the scale of the surfaceTexture and the shoreTexture rendering. These are named TessSurface and TessShore, respectively. Low values result in the textures covering large areas of water prior to repeating, whereas large values cause the textures to repeat over shorter distances. Some caution is in order when using these parameters. First, extremely small values can cause the textures to become distorted. Second, extremely large values can cause texture aliasing even when the camera is very near the water. Just remember, if you cause your graphics card to have to downscale the texture when the camera is near the water, you are wasting your artists' time.

Tying Up Loose Ends


In addition to the water-block parameters covered thus far, there are a few additional ones. First, there may be severa! under the Dynamic SimGroup. You can remove all of these. None of these parameters is hooked to anything in Torque 1.2 and beyond. The remaining parameters are the following.
rotation. Water blocks cannot be rotated. UseDepthMask. Caution is in order regarding this parameter. You may

crash the engine if you attempt to change this in the Inspector or from the console. So, if you want to experiment, change the mission file directly. Simply stated, if your value is false, only the envMapOverTexture will be rendered on the top of the water. All other surface textures will be disabled. surfaceOpacity. This affects how opaque the combination of surfaceTexture and shoreTexture is. A value of zero is not transparent, just

277

- - - - - - - - - - _..

---

Part III

Game Elements

very translucent. A value of one is quite opaque. You'll have to adjust this to meet your needs. removeWetEdge. Setting this value as true tells the engine to (attempt to) clip the edges of water that protrude from beneath terrain features. Results will vary when using this feature.

8.3.4 Maze Runner Lesson #8 rIO Percent StepJLava in the Cauldron


The game will have lava at the bottom of the cauldron. Falling into this lava kills the avatar and causes it to be respawned in its original spawn position. For now, we're only worried about getting the visual part done (the lava). We'll handle the interactions later. Please do the following.
1. Start up your Maze Runner prototype, run the "Maze Runner" mission, and

start the Creator tool. 2. Create a water block (Mission Objects -7 Environment -7 Water), only providing the name "MazeRunnerWater" when the creator dialog appears (Figure 8.6). 3. Using the Inspector, be sure that the water has the settings shown in Table 8.3.

Figure 8.6.

Creating a water block.


~ bill Jill I ll~ t ,1~ltl , I ,

---

~~~-~

~i

"

OK, so it doesn't look exactly like lava, but it gets the point across. You can tweak this to your heart's content after we get the game running. For now, let's move on.

,
I

Mission Objects

Chapler 8

8.4 Sky
In standard Torque, the sky object renders a sky box. In addition to the six sides of the box, you may specify up to three textures for cloud layers and three separate fog layers.

8.4. 1 Sky Features


The sky has the following features. Configurable sky box. As noted above, the sky is represented by a sky box. It offers such features as disabling the bottom texture and render bans. Three cloud layers. With the standard Torque sky, you can have up to three cloud layers, each individually configured. General fog and three layers of fog. In addition to the generalized fog supported by the Sky object, you can define three additional layers of fog. Visibility distance. The Sky object is the place you go when you want to modify (the camera's) maximum view distance. Wind. The Sky object owns and controls the wind vector, which is used by other mission objects. Environmental map. It may seem strange, but when you are seeking the environmental map that is used on characters and objects with environmental mapping enabled, this is the place you go. It is part of the sky box's texture list.

8.4.2 The DML File


The DML file is the place you specify your skybox and cloud textures. The file itself can be placed anywhere you wish below the game root directory, since you can specify the relative path in the fieid materialList. A sample file would look something like the following.
gpgt_basel gpgt_base2 gpgt_base3 gpgt_base4 gpgt_baseS gpgt_base6 env_map layerO layerl layer2

In this example, gpgt_basel .. gpgt_base4 represent the side textures, gpgt_base6 is the top of the box, and gpgt_baseS is the bottom of the box.

279

---_

_---

Part /II

Game Elements

The first five textures are required if useSkyTextures is true and renderBottomTexture is false. The sixth texture is required if renderBottomTexture is true. The next texture in the DML file is env_map. This texture is used for any environment mapping applied to shapes. This texture is optional if you are not doing any environment mapping and do not intend to have clouds. Finally, the last three textures in the DML file specify texture names for the cloud layers. The ordering of these textures has nothing to do with the cloud height. Cloud height is controlled by cloudHeightPer [31. We'll talk more about this in Section 8.4.4. Please note that I've stated above that this or that texture is optional based on decisions you make. However, until you get rolling, I suggest that you always specify six textures for the sky box and one additional texture for the environment map. This way, you won't run into any difficulties. Note also that the file is positional. Therefore, for example, if you want clouds, you must have specified the seven prior textures, even if they are dummy textures that won't be used.

8.4.3 The Sky Box and Render Bans


"When it [noRenderBansl ;s false, the engine wi/l draw fog onto the sky box [directly]. It does this so 3D objects that are fogged out (say all white) don't stand out against an unfogged background (the sky bOX). If the camera enters an area of fog ra 'band') the skybox will be appropriately fogged too." -Ben Garney, September 15, 2005 In general, by setting noRenderBans to false, we ensure that rendering looks good with fog. Of course, we may not always want this behavior and can thus enable render bans by setting the field to true. To get a visual perspective on this, take a look at the two images in Figure 8.7. It should be noted that the effect of this choice is especially evident from a height.

Figure 8.7.

Use of render bans.

noRenderBans -- true

noRenderBans

false

280

Mission Objects

Chapter 8

Figure 8.8.

Cloud textures.

One Texture: cloudHeightPer

== 0.8

One Texture: cloudHeightPer

== 0.5

One Texture: cloudHeightPer

== 0.2

8.4.4 Clouds
As mentioned above, the cloud layers are specified by textures eight, nine, and ten in the DML file. All cloud layers are optional.

cloudHeightPer
Texture eight corresponds to cloudHeightPer [1], nine to cloudHeightPer [2] , and ten to cloudHeightPer [3] . These parameters (cloudHeightPer) are used to control the central height of the cloud meshes. The cloud meshes themselves are a nine-faced hemisphere. The cloudHeightPer parameter specifies the height of the upper plane of this hemisphere. Figure 8.8 has some sample images to demonstrate the cloudHeightPer parameter. A value of 0.0 will cause the cloud mesh not to render, and values above 0.8 poke through the sky box causing visible artifacts.

MUltiple Layers
In terms of viewing, Layer 2 is rendered first, and Layer a is rendered last, meaning that Layer a will look like it is in front of Layer 2 regardless of
cloudHeightPer.

Cloud Motion
Cloud motion is described by two parameters. All clouds move in the same direction, specified by the (misnamed) parameter windVeloci ty, which is an x-y-z vector. The x and y components control the direction of the wind and therefore the clouds. Putting a nonzero value in z breaks the cloud renderer, so don't do it. You can control the velocity of the flow with the cloudSpeed parameter. Yes, velocities can be negative, so clouds can counterflow.

281

Part III

Game Elements

8.4.5 Fog
Clouds are cool, but sometimes you need fog in addition to, or instead of, clouds. No problem. Fog is supported in Torque by a general fog, and by up to three fog layers.

General Fog
The first type of fog affects visibility regardless of your location. The field fogDistance is used to determine this. Low values indicate low visibility, and high values indicate high visibility. Values greater than or equal to visibleDistance are eqUivalent to 100 percent visibility (unless you have noRenderBans unchecked).

Fog Layers
As noted above, there are three layers. Layer 1 is always the lowest, and Layer 3 is always the highest. Each layer has a field Eogvol umeN associated with it. This field takes three parameters: visible distance, bottom elevation, and top elevation. The visible distance determines the distance from the camera at which visibility is (near) zero. Bottom and top elevations determine where the layer (or band) of fog begins and ends, respectively. To enable a band, visible distance must be greater than zero and top elevation must be greater than bottom elevation. Also, do not forget that, if you are going to enable more than one layer of fog, they must not overlap each other, or rendering will be messed up. They may touch but not penetrate. Here are some sample settings.
fogVolumel fogVolume2 fogVolume3

"250 0 50"; "350 50 150"; "25 200 500";

The first layer starts at a world units and stops at 50 world units, with a visible distance of 250 world units.

'.

The second layer starts at 50 world units (touching layer one) and stops at 150 world units, with a visible distance of 350 world units. The third layer starts at 200 world units and stops at SOD world units, with a visible distance of only 25 world units.

8.4.6 Visibility
We've seen that fog can affect our visibility, but how do maximum view distance? This question is critical and mance as well as aesthetics; visibleDistance is the looking for. It measures in world units and can be set we determine our can affect perforparameter we are to just about any

282

L.

Mission Objects

Chapter 8

value. A word of caution, though: extremely large distances can kill performance big time.

8.4.7 Rendering Issues


If you are having rendering problems, you may wish to check the following.
1. Get the latest drivers for your video card.

2. Set quality settings to their highest values for D3D or OpenGL, depending on which application programming interface (API) you are using. 3. Be sure that bit depth is 32 (both in your driver settings, and under Options -7 Graphics -7 Bit Depth from the main menu).
If you still encounter issues, talk to someone on the Torque Internet Relay Chat (IRC) channel (IRC server: irc.maxgaming.net; IRC port: 6667; channel: ttGarageGames), or post a descriptive thread (after searching the forums, of course).

8.4.8 Sky Scripting


Storm Fog
Storm fog is a scripting feature used to fade a layer of fog in and out over a period of time. In order to enable this features, the sky must have been created with the fogStorm [1,3] checkboxes checked. You must have correctly defined the visible distance and low and high values for the fog layer. For example, if we wished to fade in and out just one layer, we could define something like in Figure 8.9. Notice that fogStorml is selected. Subsequently, we could fade layer 1 of our fog to 50 percent over a 5 second period, using the following code.
Sky.stormFog( 0.5 ,
L,.

Figure 8.9.
Storm-fog definitions.

5 );
00 0
!lOO 1.eooo[J[J 1.DOODee 1 .CODeDD 1 . 1.eeCD[J[J D.DCDODD D.DCODDO 1.

Or, we could turn it entirely off instantly using the stormFogShow () method.
Sky.stormFogShow( 0 );

O.DDDD[J[J D.DDDODD 1.DDDDOC 1 .

Note that fog layers fade in layer by layer, starting with Layer 1 and ending with Layer 3. They fade out in the opposite order. Please note that, if you enable fogStorm and the storm is inactive (or deleted), your fog will disappear.

283

Part III

Game Elements

Storm Clouds

Storm clouds is a scripting feature similar to storm fog, except for the three cloud layers. Because clouds are defined differently from fog, we can't disable the feature for certain layers, so all layers that are defined will be affected when using the stormClouds method. That said, if we wanted to fade our clouds out over a 10 second period, we would write the following code.
Sky.stormC1ouds( 0 , 10 );

8.4.9 Maze Runner Lesson #9 flO Percent StepJStarry Night


If you are building the Maze Runner game while you read this guide, the original sky is a bit too bright for our game, so we will need to do the following to create a starry night instead.
1. Start up your Maze Runner prototype, run the "Maze Runner" mission, and

start the Inspector tool. 2. Find the Sky object and change the DML file to one you will find in /MazeRunner/prototype/data/GPGTBase/skies/starrynigh tj starry_sky. dm!. This file contains the following list of texture names.
starsO starsl stars2 stars3 stars4 stars5 stars6 c10udl

The textures used in this file are just a set of five generated starfields, a placeholder for the seventh texture, and a randomly (noise) generated translucent cloud texture (Figure 8.10).
Figure 8.10. Sky textures.

II
skyO .. skyS (similar) sky6 (placeholder)

284

c/oudl

!---

Mission Objects

Chapter 8

Parameter
material List
I cloudHeightPer[O]

Value
prototypejdatajGPGTBasejskiesjstarrynightjstarry_sky,dml

Table 8.4.

0.5 0 0 0.0005 0 0 1000 2000 < 5500300 > <000> <000>


Use defaults

Sky settings for starry night.

cloudHeightPer[l] cloudHeightPer[2] cloudSpeedl cloudSpeed2 cloudSpeed3 visibleDistance fogDistance fogVolumel fogVolume2 fogVolume3
All others

3. Using the Inspector, be sure that the sky has the settings shown in Table
8.4.

8.5 Sun (Mission LightingJ


The Sun object has a simple job, namely to determine how the mission will be lit. Initially, you mayor may not find this particular mission object simple to use, but with a little help, this should be no big deal. Please note that this object does not have a visible representation; that is, you can't actually see the Sun mission object. If you need a visual representation of your sun(s), use the fxSunlight mission object.

8.5.1 Sun Features


A sun has the following features.

Configurable light source. Using the Sky mission object, you may configure the position of the light source and coloration (both direct and ambient) of the light it emits. Object shading. Objects are darker on the side opposite the sun's position. Shadows. Shadows are supported, but there are issues. See Section 8.5.2, "Shadows and Sun Direction." No sun and multiple suns. You can have 0, 1, 2 ... well, you get the idea.

285

Part III

Game Elements

8.5.2 Shadows and Sun Direction


Torque supports shadows and pseudo-self-shadowing. When I say pseudoself-shadowing, I mean that objects are darker on the side facing away from the sun. This is done correctly for the terrain, shapes, and interiors. Unfortunately, shadows cast by objects onto other objects are a little buggy. Both terrain and interiors properly cast shadows onto other objects, but shapes do not. What do I mean by properly? Well, shadows should be calculated based on the azimuth and elevation parameters. If I say a shadow is cast correctly, I mean it adjusts based on these parameters. Table 8.5 should clarify things.
Table 8.5.

Shadows?
Terrain Does adjust based on sun parameters. Does affect other mission objects. Self-shadowing is baked. Do adjust based on sun parameters. Do affect other mission objects. Are baked into terrain. Adjust orientation and length based on sun parameters. Do affect other mission objects. Are dynamic.

self-Shadows?
Yes

Shadowing and selfshadowing.

Please note that, while you are reading this chapter, it is likely that you are using the demo version ofTGE. In order to show the engine's best face, the demo includes some features from the Torque Lighting Kit (TLKJ. While this is nice, it may cause some confusion if a feature that I describe here does not match the demo. So, instead of trying to document both TGE and TLK here, I will be describing the standard version of the Torque Software Development Kit (SDK). This way, you will know exactly what you are getting when you bUy the SDK while also knowing (from the demo) what TLK can do for you.

Interiors (.dif)

Yes

Shapes (.dts)

Yes

Baked shadows are calculated once during the lighting phase of a mission load and are static until/unless the mission is relit. Better Lighting Although lighting in the base version of TGE is good, it cannot compare to the extended ranges and other features provided by the Torque Lighting Kit. Neither can it compare to the almost unlimited set of effects you can get by using the Torque Shader Engine. So, if you're looking for more intense or dazzling lighting effects, remember that you have options that keep you in the Torque family and thus retain all of the other great features Torque provides. Azimuth and Elevation Once you grasp the concept of azimuth and elevation, they are quite easy to work with, but describing them directly is a bit of a chore. I'm sure there is a succinct mathematical way of describing these terms, but not being a mathematician, and wanting to be clear to those similarly handicapped, I will instead describe them simplistically.

286

Mission Objects

Chapter 8

Imagine, if you will, that we have a magic arrow (yes, a vector). The base of this arrow is stuck to the world axis. Magically, the head of the arrow always points at the sun. Given this, our magic arrow will behave as shown in Table 8.6.
Azimuth (degrees)
0

Elevation (degrees)
0

1heArrow
Points down the y-axis and lies in the

Table 8.6.

x-y plane.
45 0 Makes a 45-degree angle between and y and lies in the x-y plane.

Azimuth and elevation following the sun.

90

45

Points down the x-axis, making a 45degree angle between x and z.

In all cases above, x, y, and z are the world axes. Both azimuth and elevation can theoretically take any value between and 360, but in practice, there are certain values that do not work well.

Azimuth
Legal range: [0,360). At 90 and 180 degrees, shadows stop rendering.

Elevation
Legal range: [0, 360). Suggested range: [0, 90). Engine will crash if this is set to 90 degrees. Values greater than 180 are below the terrain and may produce odd effects.

8.5.3 Color and Ambient Parameters


OK, enough about where the sun is, but what about the color and ambient parameters? First, both of these parameters affect the scene lighting in different ways. Briefly, color is the part of the light that is cast directly onto shapes, interiors, and the terrain. It accounts for the shadows that interiors 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 interiors. Changes to the ambient portion of lighting are most easily noticed, but you should experiment with both factors (ambient and color) to achieve the results you need. Both parameters take four arguments, < r g b i> , where i is the intensity. Currently, intensity has no effect for either parameter.

287

----- ------

1/

Part III

Game Elements

8.5.4 Multiple Suns?


You may have more than one sun, but be aware that the following is true. Mission lighting will take significantly longer. Lighting is cumulative and clamped, meaning you can saturate your lighting. Shadows do not behave as you would expect with two or more light sources; instead, you'll likely end up mauling your shadows. The number one reason for adding multiple light sources is to get cool shadowing effects. Since this doesn't really work as expected, you are probably better off just sticking with one sun.

8.5.5 No Sun?
This has been an on-again, off-again feature. Currently, you must specify a sun or your game will crash (TLK handles this case without crashing). However, if you want a totally dark mission, you can achieve this with a sun present. Just set the two color parameters (color and ambient) to "0 a a 0". In the end, this is safer than removing the sun, even if it does work for you now.

8.5.6 Maze Runner Lesson # lOr 10 Percent StepJLow Lighting


If you are building the Maze Runner game while you read this guide, the original sun (lighting) is a bit too bright for our game, so we will need to do the following to match our night sky.
1. Using the Inspector, lower the lighting values for the Sun object to the values in Table 8.7. 2. Now, relight the scene (ALT + L) to see the values take effect.
Table 8.7. .. Lighting values for low lighting.

Field.
elevation azimuth color ambient

Values
90 90 < 0.5 0.3 0.3 1 > < 0.2 0.2 0.2 1 >

8.6 Precipitation and Lightning


A couple of nice effects to be able to add at will are precipitation (i.e., rain, snow, hail, etc.) and lightning. These are actually separate mission objects 288

l_.

Mission Objects

Chaprer 8

(one for precipitation, and two possibilities for lightning), but I'll address them together because they are relatively small and have at least a tangential relationship.

8.6. 1 Precipitation Features


Precipitation has the following features. Variable density. You can choose between a light shower and a downpour. Additionally, the density of rainfall varies randomly over time to give it a more organic feel. Variable velocity. Since real raindrops do not all fall at the same rate, Torque supports the ability to randomly vary the velocity of individual drops. Drop coloration. For an additional degree of realism, you can modify the coloration of individual drops by providing up to three colors. Multiple textures. Because having just one texture for the drop would be boring, Torque supports 16.

8.6.2 lightning Features


There are two different objects that can be used for lightning. First, there is the Lightning object, which supplies the following features. Generated lightning. Based on LightningData fields you set, the engine generates jagged lightning bolts. Targetable strikes. You can, to some degree, target where lightning begins and where it will strike. Fade color. You can choose what fade color is used for the bolts. The fade color is used to simulate the effect of seeing a lightning strike. Fogging. You can enable fogging features to make the lightning extra impressive, but this feature requires hardware support. Thunder. You can supply a sound datablock to provide thunder with the lightning. Second, there is a recent addition, WeatherLightning, which supplies the fol[owing features. Textured lightning. Based on WeatherLightningData fields you set, the engine renders your supplied lightning textures. SkyFlash and fuzzing effects. Based on WeatherLightningData fields you set, the engine renders flashes in the sky and an afterimage for each bolt. Thunder. You can supply a sound datablock to provide thunder with the lightning.

289

.. _ - - - - - - - - -

- - - - - - ~ - - - _. . .

_.---._--_

Part III

Game Elements

8.6.3 Let There Be Rain


Setting up a precipitation object requires that we consider several facets of the rain storm's behavior, including the density of the storm, the speed at which individual drops fall, drop coloration, and the images that should be used for our raindrops. As you will see, all of this is quite straightforward. Precipitation Density Precipitation density is a measure of how many raindrops we have in a certain area. We can vary the precipitation density by varying maxRadius, maxNumDrop, and percentage. Together, maxNumDrops * percentage determines the current number of drops falling. We can spread these drops out by selecting various values for maxRadius. A low value of, say, 30 will cause drops to fall within 30 world units of the camera, and a value of 125 will cause them to fall as far away as 125 world units. Precipitation Velocity In order for our precipitation to look more realistic, we'll want it to fall at varying rates. To do this, simply set minVelocity to a nonzero value lower than maxVeloci ty. Now, drops will fall at some random speed between minVeloci ty and maxVelocity. Additionally, setting offsetSpeed to a nonzero value adds a bit of horizontal velocity to the drops. Don't overdo it on this parameter, though, as high values can make the precipitation look a bit unnatural. Varying Drop Colors The base color of your drops is determined by the texture(s) you use for your precipitation (see below), but you can modify this with the color (3: 1] parameters. As far as I can tell, 33 percent of the drops are either color1, color2, or color3. So, setting the <r g b> portion of these to something other than < 1 1 1 > wi!! cause the textures to be shaded that color. Note that the alpha channel (fourth value) does nothing. Precipitation Media By default, any individual drop is a billboard. l For the sake of this discussion, think of a billboard as a polygon that automatically orients itself to be facing a specific direction. These billboards are textured using 1/16th of a texture supplied in the PrecipitationData field dropTexture. That is, you supply the relative path to a PNG file in Precipi tationData. dropTexture. This texture should be a 4 x 4 grid containing a raindrop image in each of the sixteen resulting grid blocks (see Figure 8.11). When the engine gets ready to produce a new drop, it will randomly select one of the 16 subtextures and use it as the precipi-

If you want to learn all about billboards, pick up a good book like Akenine-Moller and Haines, RealTime Rendering, Second Edition (A K Peters, LId., 2002).

290

Mission Objects

Chapter 8

tation billboard. You may use lPG or PNG files for precipitation, but I suggest using PNG, as lPG does not support the transparency that you will likely need.

Figure 8.11.
Precipitation texture fa 4 x 4 grid of subtexturesJ.

8.6.4 It Was a Dark and Stormy Night ...


What would a storm be without a little lightning and thunder? Well, fortunately you don't need to find out, because Torque comes with two different classes that each display different styles of lightning and play thunder sounds, too. We will discuss both in this section, starting with a discussion of Lightning and then segue into a discussion about WeatherLightning. Lightning (and WeatherLightning) objects are blocks like water. This means that you can place multiple blocks of lightning throughout your mission, or if you choose, you can have just one big block covering the whole mission. Blocks may overlap. You can freely scale the lightning block using the Inspector and the mouse.

t i f i It f t
1f
~
I

1f It

It

8.6.5 Lightning Strikes!


First, it is important to understand what a strike is. When the engine gets ready to draw the lightning, it decides whether it is going to strike the ground, the highest local object, or if there will be a miss. When there is a miss, the lightning is drawn at an angle, sometimes even parallel to the ground. These misses give the lightning a more realistic look. So, how does the engine determine if there will be a miss or a strike, and when there is a strike, how is it determined if an object or the ground will be hit? First, the zone where anything can be hit is determined by the location of the lightning box as well as the strikeRadius. Bolts will strike objects or the ground within s t r i keRadi us of the lightning object. To determine if an object will be hit, or if the ground will be struck, the engine grabs a list of all damageable objects in the strike zone and does a sort, looking for the highest object. It can randomly choose an object that is not the highest, but it prefers the highest object (as does real lightning). Finally, the engine rolls the dice, so to speak, and if the value it gets back is less than or equal to chanceToHi t (remember those good old AD&D days?), that object is hit. If the value is higher than chanceToHi t, then the bolt hits a random location on the ground. We can control the number of lightning strikes (this includes misses) per minute with the parameter strikesPerMin. This is not the inverse of the strike period but instead a rough number of strikes per minute. Increasing this value increases the number of strikes in any period, but strikes can happen very rapidly or with short pauses between them. This just gives it a more random feel. You can't predict a lightning strike.
291

Part III

Game Elements

So. what about strikeWidth? Well, this determines the width of the bolt on a strike. Bolts all have a default width for misses, but for strikes, you can control the width. Do you want a fat strike or a narrow one? Lightning Color The textures you choose for your lightning are used as a mask, but the coloration comes from the color and fadeColor parameters. The bolts are drawn first, using color, and then over a short period, they are faded out. While this fade occurs, the bolts are colored fadeColor. This gives a nice heated plasma effect and mimics the behavior of the eye when it sees a lightning bolt. When you see an actual lightning strike or any focused bright light, most of the receptors in the eyeball fire for the area where the bolt is focused by your eye's lens. This temporarily uses up all the available chemicals that make sight possible. In other words, those receptors are temporarily turned off by the overload. The effect is a phantom bolt that fades over a short time. Leaning Lightning? ]n addition to controlling the strike zone, we can control where the lightning bolts start. If we set boltRadius to zero, then all bolts will radiate from the topmost center position of the lightning box. We can also set the value to something big, like 500. Now, all the bolts will seem to be coming from far away and angling towards the strike zone (assuming a small strike zone). Ooh ... Pretty Lightning! Finally, if you set useFog to true and if the user's graphics card supports both multitexturing and fog-coordinate extensions (a pretty good bet for cards two or fewer years old), the engine will do a nice bit of texturing with local fog (Le., fog around the camera). Two Ways to Create Lightning The engine supplies two means of making lightning. The first type is generated (Lightning object) and uses no textures. The second way uses textures instead of generation (WeatherLightning object) for more spectacular effects.

Generated Lightning [Lightning Objects)


In order to create generated lightning, we must still create a datablock for our lightning.
datablock LightningData(LightningExamplel ( 1/ Play this sound when lightning strikes! strikeSound = LightningStrikeSound;

292

Mission Objects

Chapter 8

// up to eight thunder sounds can be defined thunderSounds[Oj ThunderSoundO; thunderSounds[l) = ThunderSound1;


};

This datablock specifies zero textures, a strike sound, and two of the eight possible thunder sounds. Now, we can place a lightning object in our mission using this datablock or create one via script.
new Lightning() { position = "0 a 180; scale = "100 100 500"; dataBlock = "LightningExample"; strikesPerMinute = "90"; strikeWidth = "0.25"; chanceToHitTarget = "100"; strikeRadius = "25"; boltStartRadius = "100"; color = "1.000000 1.000000 1.000000 1.000000"; fadeColor = "0.100000 0.100000 1.000000 1.000000";
};

This sample will produce a lightning storm centered at an < x y> of "0 0" and starting at an elevation of 180 world units. Up to 90 bolts will strike per minute, all of which will be fairly narrow and striking within a radius of 25 world units of "0 0", but starting at a radius of 100 world units; that is, these lightning bolts will lean in. Finally, the bolts will start off completely white and fade to a dark blue.

Textured Lightning (WeatherLightning Objects)


Alternatively, we could use the WeatherLightning object and specify a WeatherLightning datablock as follows.
datablock WeatherLightningData(TexturedLightningExample) strikeTextures[O) "./data/lightning1frame1"; strikeTextures[l] "./data/lightninglFrame2"; strikeTextures[2] "./data/lightninglFrame3"; flashTextures[O] fuzzyTextures[O] fuzzyTextures[l] fuzzyTextures[2] "./data/flash"; "./data/lightningFuzzframe1"; "./data/lightningFuzzFrame2"; "./data/lightningFuzzFrame3";

293

Part III

Game Elements

strikeSound

LightningStrikeSound; ThunderSoundO; ThunderSoundl;

thunderSounds[O] thunderSounds[l]
};

This datablock uses three textures for lightning bolts, one texture for a boltorigin flash (in the sky), and three textures for after-bolt fade images. In each case, we could have specified up to eight textures for the three effects. In addition to these visual effects, like Lightning, WeatherLightning can playa strike sound and up to eight thunder sounds (although we only specified two). At this point, you might be a little confused about what you get with Lightning objects and what you get with WeatherLightning objects, so let's summarize their features.

Lightning Features Revisited


In summary, the Lightning object is used to create generated lightning effects.
It gives us various controls over how that lightning is generated, including the

width of the bolt, the starting location of the bolt, the ending location of the bolt, its initial color and ending color, and finally the number of bolts per minute. In addition to these Lightning field controlled features, the LightningData datablock has the fields and features shown in Table 8.8.

WeatherLightning Features Revisited


In summary, the WeatherLightning object is used to create textured lightning effects. It gives us fewer controls over the bolts than the Lighting object. In fact, we can only control the number of bolts per minute, using the strikesPerMin field. However, this object does have the benefit of producing very nice bolt effects. These effects are specified using the WeatherLightningData datablock and supplies the features shown in Table 8.9.

8.6.6 Maze Runner Lesson # 11 (10 Percent StepJStormy Weather


If you are building the Maze Runner game while you read this guide, we are now going to add some rain, lightning, and thunder to our scene. The game is meant to have a "cartoon spooky" theme, and these elements will add/to that.

Adding the Rain 1. Start up your Maze Runner prototype, run the "Maze Runner" mission, and start the Creator tool.
294

Mission Objects

Chapter 8

Table 8.8.
strikeSound thunderSounds[8]
An audio profile to use for the strike noise. Should be 30 audio profile created with da tablock keyword. Eight audio profile slots for thunder/lightning strike sounds. Should be 20 audio profile created with datablock keyword.

Fields in Lightning datablocks.

field Nllme
strikeTextures[8] flashTextures[8] fuzzyTextures[8] strikeSound thunderSounds[8]
Eight texture slots for relative paths and names of lightning texture files. Eight texture slots for relative paths and names of lightning origin-flash texture files. Eight texture slots for relative paths and names of lightning fade textures. An audio profile to use for the strike noise. Should be 30 audio profile created with datablock keyword. Eight audio profile slots for thunder/lightning strike sounds. Should be 20 audio profile created with da tablock keyword.

Table 8.9. Fields in WeatherLightning datablocks.

I;

,...........
minSpeed max Speed rotateWithCameraVel numDrops boxWidth boxHeight doCollision
All others

ve'"
1
1.5

.'

,'.

Table 8.10. Settings for rain.

true

2000 200 100 0


Use defaults

Figure 8.12. Adding rain.

Figure 8.13. Adding lightning.

295

Part III

Game Elements

Table 8.11.
Settings for lightning.

~.~,

-,
position scale

/:.':.

Value
< 0 0 300 > < 256 256 250 >

strikesPerMinute strikeWidth strikeRadius color fadeColor chanceToHitTarget boltStartRadius All others

6 1.5 128
< 0.89 0.8 0.42 1 > < 0.50.9 0.9 1 >

0 32
Use defaults

2. Select a precipitation object (Mission Objects -7 Environment -7 Precipitation) , giving it the object name "MazeRunnerRain" and choosing the datablock BaseRain (Figure 8.12). 3. Open the Inspector and give the new rain object the settings in Table 8.10.

Adding the Lightning and Thunder


1. Go back into the Creator tool. 2. Select a lightning object (Mission Objects -7 Environment -7 Lightning), giving it the object name "MazeRunnerLightning" and choosing the datablock BaseLightning (Figure 8.13). 3. Open the Inspector and give the new lightning object the settings in Table 8.11.

8.7 Audio Emitters


...
So far, we've focused on visible environmental objects. What about sounds? Audio emitters are objects that you can use for placing positional sounds. Audio emitters have the ability to turn themselves on and off based on a trigger. This trigger can be modified in size and shape to meet your needs. Let's take a look, or perhaps I should say, let's have a listen?

8.7.1 Audio Emitter Features


Audio emitters have the following features.

2D sound. This is sound with no apparent source. In other words, it is neither directional nor positional.
296

Mission Objects

Chapter 8

3D sound. This is sound with a specific source. Furthermore, this type of sound is modulated by distance from and facing angle to the sound source. Looping and nonlooping sounds. Emitters can be programmed to loop a variable number of times or as one-time emitters. Triggers. 3D sound emitters have the ability to turn themselves on and off based on a cut-off distance.

8.7.2 20 Sound
2D sound is very simple. All 2D sound emitters are turned on at the earliest opportunity (shortly after they are created). If looping is enabled, audio emitters will not stop playing until all loops have been exhausted; otherwise, they will play once and then stop. You can specify a 2D audio emitter with the following settings. Media
description. Set this to the relative directory + filename for the sound

file. Either WAV or OCC files are acceptable formats. type. A value between 1 and 8, corresponding to the audio group this emitter should belong to (see "2D Cain" below). Sound
volume. Between 0.0 (0 percent gain) and 1.0 (100 percent gain). outsideAmbient. Should be checked. Looping. Set looping parameters based on your requirements (see "Looping" below).

Advanced is3D. Should be unchecked. 2DGain Cain determines how loudly your sound will play. The gain equation for 2D emitters is as follows.
2D gain
==

game master volume * audio group gain * emitter gain

Came master volume is controlled from the main menu under Options -7 Audio. Audio group gain is controlled by the field Media -7 type. Valid values for type are 1..31. By default, only 1..8 are set up. O. Is reserved. 1. CUI audio type (Options -7 Audio -7 Shell Volume) 2. Sim audio type (Options -7 Audio -7 Sim Volume) 3..8. Set to 0.8 (search for channel Volume in scripts).
297

---------------~--

-~-

Part III

Game Elements

The purpose of this gain is to allow you to adjust the gain for a group of emitters in one step. Emitter gain is controlled by the field Sound
~ volume

parameter.

Looping
If you haven't already guessed, the looping parameters allow you make an emitter (2D or 3D) play the sound file between one and infinite times. To enable looping, make sure Looping ~ isLooping is checked. Then, set your loop count. Loop counts work as follows.
loopCount = = -1. Loop infinitely. loopCount = =" O. Loop once and only once. loopCount

= = 1. Loop once, possibly twice.

loopCount = = (n > 1). Loop n times.

On rare occasions, a value of 1 will cause two loops. So, if you really want only one loop, use a loopCount setting of O.

Loop Gaps
The loop gap parameters control the delay between subsequent loops. minLoopGap, as you would imagine, defines the lower boundary for delays and maxLoopGap the upper. Torque randomly chooses a value between these two. Loop gaps are approximately equal to 2n milliseconds, where n is the LoopGap value selected. Please note that loop gaps can be used to do some interesting things (see Table 8.12).
Table 8.12.
IIiDLOapGap

llfeorT.oopQap
0
1

.:~

(:;~~.:~.

AdIon

Use of loop gaps.

0 0
1 N> 1

Sound turns on, but won't turn off (2D and 3D) Sound turns on immediately and turns off at end of loop or upon exiting 3D region (see below). Sound does not turn on, ever. Normal behavior.

0
N> 1

By using the settings minLoopGap = 1 and maxLoopGap = 0, you can tell the emitter to not play at load time. Once the load is completed, you can have a script set the gap values to whatever delay you need, or you can hook the sound up to a trigger.

2D Visual Feedback
Visual feedback in 2D mode is simple. While editing, you can see the emitter as a small cube. The cube will be black while not playing and green while playing (Figure 8.14).

298

Mission Objects

Chapter 8

Figure 8.14.

Audio visual feedback.

Audio emitter off

Audio emitter on (playing)

8.7.3 3D Sound
In real life, sound radiates from a source to a listener. Additionally, sound is attenuated by several factors, including distance, angle, occlusion, etc. Torque simulates the behavior of real-world sound with OpenAL's 3D sound features. 3D audio emitters support distance and angular attenuation. How they support these features can be a little confusing, so we will treat this topic like a puzzle and examine each puzzle piece individually to see how it fits into the complete picture.

Sound Zones and Sound Cones


In practice, audio emitters support four zones of sound (Table 8.13 and Figure 8.15) .

laM 0

Figure 8.1 5.

Sound cones.

omltlor

Zone A-Inner Cone


As noted above, gain in the inner cone a function of distance from the emitter (source). To determine the physical volume of the inner cone, we must specify the following.
Delatptlon
A
B

cone vector

Table 8.13.
Gain is a function of linear distance from source. Gain is a function of linear distance from source and angular distance from inner cone edge. Gain is a constant value determined by outside volume. Emitter is deactivated.

Listener in inner cone. Listener in outer cone. Listener in area outside outer cone. Listener beyond maximum distance from source.

Four zones of sound.

c
D

299

Part 1/1

Game Elements

is3D must be checked to enable 3D sound. rotation specifies the direction in which coneVector points. maxDistance specifies the base of the cone. coneVector is a unit vector, but you can image a line passing through the vector, starting at position and ending at position + coneVector * maxDistance, and this is the

position specifies the tip of the cone and the base of the coneVector.

position of the cone base.


conelnsideAngle specifies the inner cone sweep.

To specify the gain of the inner cone, we must specify the following.
volume. Emitter gain. referenceDistance. This specifies the distance (from the emitter) at

which 3D gain

= =

0.5.

Inner cone gain works as shown in Table 8.14. Zone B-Outer Cone Gain in the outer cone is a function of inner-cone gain and the angle from the outer edge of the inner cone. To determine the physical volume of the outer cone, we must specify the following. inner cone.
coneOutsideAngle, which specifies the outer cone sweep.

The outer cone shares all the parameters of the inner cone including the axis. To specify the gain of the inner cone, we must specify one additional parameter.
coneOutsideVolume, which is the gain at and beyond the outer edge of the outer cone. Important! If this value is 0, the outer cone will be disabled and there will be no sound except inside the inner cone.

Outer-cone gain works as shown in Table 8.15. Zone C-Outside Volume


If coneOutsideVol ume is nonzero, the area outside of the outer cone has a gain between coneOutsideVolume and zero, based on the distance from the emitter. Outer-volume (zone) gain works as shown in Table 8.15.

Zone D-Beyond maxDistance The maximumDistance can be used to draw an imaginary sphere around the emitter. If the camera enters that sphere, the emitter is told to load its sound. Additionally, if the camera is inside an enabled sound zone, the emitter is told to play the sound. Conversely, if the camera moves from within the sphere to outside the sphere, the sound is told to stop playing.

300

Mission Objects

Chapter 8

Table 8.14.
P<R 0.5

P/R

where P = I listener position - emitter position R = referenceDistance M = maxDistance

Inner cone gain.

P == R
M>P>R

0.5

Table 8.15.

ca ca

== 1a

Ig Ig ~ Ov (as a function of angle) Ov

where Ig = inner cone gain at current distance from emitter ca = (coneOutsideAngle - Current Angle) / 2 1a = cone1nsideAngie / 2 Oa = coneOutsideAngle / 2 Ov = coneOutsideVolume

Outer cone gain.

< 1a < Oa

ca

== Oa

Table 8.16.

P
where P =

coneOutsideVolume

Outer volume gain.

(as a function of distance)

I listener position -

emitter position

3D Visual Feedback
Before we jump into examples, let's discuss the visual feedback associated with 3D audio emitters. Because there are more audio concepts to express, the visual feedback is a little more complex than for 2D emitters, but only marginally (Figure 8.16 and Table 8.17). You can specify a 3D audio emitter as follows: Media
description. Relative directory
Figure 8. 16. Visual feedback for 3D audio.

+ filename for the sound file. Only WAY format is supported. Mono and stereo formats OK.
type. 1 through 8 (see "2D Gain" above).
volume. Between 0.0 (0% gain) and 1.0 (l00 % gain).

Sound

outsideAmbient. Should be checked. Looping. Set looping parameters based on your requirements (see "Looping" above).

301

Parr ill

Game Elements

Table 8.17.
Audio emitter-3D visual
feedback.

Inner cone Outer cone Outside volume On/Off indicator

Red fading

to

blac.k. Fade starts at

r-ececenceOistance.

Pinkish-purple, Blue. same as 2D (not visible in Figure B.16).

Advanced enableVisl1alFeedback. Should be checked. Please note that, even if this is not checked, visual feedback renders when. a 3D emitter is selected. is3D. Should be checked, conelnsideAngle. Set to your preference, coneOutsideAngle. Set to your preference. 0 to disable, coneOutsideVolume. Set to your preference. 0 to disable all but inner cone. coneVector. Don't tOuch this. It is set automatically when you adjust rotation, 1'fped changes will be overridden,
Audio Descriptions and Profiles

Audio descriptions and profiles are art alternate way at (pre-) specifying the specifics of an audio emitter. These will be discussed in Chapter 11, "Special Effects." For now, it is perfectly sUltable to define the parameters (or an audio emitter using the Inspector.

8.7.4 3D Emitter Examples


Figure 817 gives examples of 3 D audio emitters.

8.8 Particle Emitter Nodes


One of the more time-consuming mission objects to place is the particle emitter-not because it is paJ1icularly hard to understand, but because it offers a venerable cornucopia of features. Moreover, it is just plain fun to play with! In facl, if you don't approach it knowing the basics of how to use it and with a good idea of the result you want, you could burn several hours goofing around, While I can't help you focus on a particutar idea, T can help you understand the basics of using it. I must warn you before we start: we are going to depart {rom using the mission editor alone. In order to build emitters, we need to write some script datablocks. Par now, you can just use my examples directly, and you should not get into too much trouble, Later, you may experiment and write your own,

.302

Mission Objects

Chapter 8

Figure 8.17.
3D audio emitters.

coneInnerAngle:90 coneOuterAngle: 0 coneOutsideVolume: 0

coneInnerAngle: 360 coneOuterAngle: 0 coneOutsideVolume: 0

c
coneInnerAngle: 270 coneOuterAngle:360 coneOutsideVolume: > 0

D coneInnerAngle: 180
coneOuterAngle: 360 coneOutsideVolume: > 0

8.8. 1 What Is a Particle Emitter Node?


Particle emitter nodes (PENs) are static objects (that is, they don't normally move) that can be used to provide special effects such as smoke, fire, waterfalls, fireflies ... you name it. They do this by emitting-you guessed it-particles. As is commonly2 the case in 3D systems, these particles are billboards. In the case of particles, these billboards are usually textured with a partially opaque and partially translucent texture and are usually facing the camera. What this means is that, when you look at any particular particle, it will normally be facing you, and you will likely be able to see through parts of it. So, what do we have so far? In Torque, particles are billboards, and they are shot out of PENs. However, particles don't just shoot out of PENs. In fact,

Some common particles are billboards, pixels, and lines.

303

Part III

Game Elements

) Done right, panicles do nOl consume a lot of resources (memory, CPU time, geometry budget, etc.).

we (the game designers) choose how many particles there are, what kinds of visual effects they have, how fast they shoot, whether they are aHected by wind, gravity, etc. All these factors make PENs useful. Most lmponant of aJ{, we can create some awesome effects at a low cost.'

8.8.2 Particle Emitter Data Blocks


As I mentioned above, we need to build a few datablocks before we can play with particle emitters. SpecLfi call y, we wiH need a minimum of three datablocks . ParticleEmitterNodeData (PEND). Think of th..is as the base for the emitter. It controls one aspect of the parUcle emitter-Ume, ParticleEmitterData (PED). This is used to describe the behavior of the PEN itself. It controls how many particles are emitted, how fast, and in what position/direction . .. ParticleData (PD). This describes individual particles. It controls coloration, fade, spin, drag, velocUy, acceleration, and whether gravity, partide life, and a few other things affect a pa rticle. The GPGT Lesson Kit (and the prototype content on the accompanying disk) comes with several predefined particle datablocks, l[1cludirtg the following .
.. baseSInokePDO. ..

.. ..

.. .. .. .. ..

A ParticleData datablock used to represent smoke. bal!leSmokePDl. A ParticJeOata datablock used to represent smoke, Uses same p.arameters as baseSmokePDO with new texture. bal!utFirePDO. A ParticleOata datablock used to represent simple nre. baaeFirePD1. A ParticleData datablock used to represent a nicely animated fire. basQSparkPDO. A ParlicleData datablock used to represent sparks . baseBubblePDO. A ParticieData datablock used to represent bubbles. baseDustPDD. A Partic1eData datablock used to represent dust. baeePED. A ParticleEmieterData datablock using baseSmokePOO . basePEND. A (default) ParticleE.mitterNodeData datablock.

If we wanted to use some of the above datablocks in script, we could do the follOWing:
new ParticleEmitterNode(PN TestOl position = "0 0 0"; rotation = "1 0 0 0"; scale - "1 1 1"; dataBlock a "basePEND"; emitter = "basePED";

304

Mission Objects

Chapter 8

velocity
};

"1";

8.8.3 ParticleEmitterNodeData {PENDJ Datablock Parameters


The PEND datablock specifies a time multiplier for an individual PEN (Table 8.18). This time is used subsequently in certain calculations, which we'll cover in Section 8.8.7.

8.8.4 ParticleEmitterData {PEDJ Datablock Parameters


The PED datablock specifies the behavior of a PEN, including what particles it emits, at what rate, in what direction, with how much velocity, and for how long (Table 8.19). It also describes how particles will be oriented.

8.8.5 ParticleData {PDJ Datablock Parameters


The PO data block describes an individual particle, including how things like wi nd, drag, gravity, and an acceleration factor affect it (Table 8.20). It also describes physical parameters of the particle including color, size, spin, and lifetime. Lastly, it describes advanced features, like alpha inversion and animation.

8.8.6 PEN Parameters


In order to specify a PEN in your mission, you can add it with the World Editor (WE) (Fll-7 F4; Mission Objects -7 environment -7 particleEmitter), or by hand-editing your mission file. In order to do this, we need to specify the parameters in Table 8.21.

8.8.7 PEN Equations


As promised, I'll describe some important equations below. Armed with these and the subsequent descriptions of theta and phi, orientation, and animation, you should be able to prespecify approximate values before you start to experiment and tune, which should save lots of time.

Table 8.18.
t imeMul tiple [ 0.01 , 100.0 ]
Time multiplier, used to increase or decrease elapsed time by a ratio. Affects ejection period, ejection position calculation.

PEND datablock
parameters.

305

Part III

Game Elements

Table 8.19.
PED datab/ock parameters.

ejectionPeriodMS periodVarianceMS ejectionVelocity velocityVariance ejectionOffset

[1, inf) (O,ejectionPeriodMS] [0, inf) [O,ejectionVelocity] [0, inf]

100

Milliseconds between last and next particle ejection. Amount to vary ejection period by. Initial velocity imparted to particles. Amount to vary initial velocity by. Particle ejections begin at ejectionOffset distance from emitter. Modifies emitter ejection up and down. This modifies the PEN up vector.

o
2.0 1.0
0.0

thetaMax

[ 0 , 180 ] [ thetaMin , 180 ]

90.0

o = fully up, 180 = fully down


thetaMin [ 0 , 180 ] [ 0 , thetaMax ]

0.0

Modifies emitter ejection up and down. This modifies the PEN up vector.

o = fully up, 180 = fully down


phiReferenceVel [0,360]

0.0

causes emission point to rotate clockwise phiReferenceVel degrees per second about the PEN UP vector. separate from phiReferenceVal, this parameters enables a random ejection between 0 degrees and

phi Var iance

[ 0 , 360 ]

360.0

phiVariance. overrideAdvance orientParticles false [ true, false] false


Always false (legacy code).

If true, face emission direction. If false, face camera. If true and if orientParticles
direction of motion. If false, use orientParticles setting.

orientOnVelocity

[true, false]

true

==

true, face

particles lifetimeMS

PD name(s)

List of PD datablocks to use/emit.

[0, inf)

Length of time to eject particles before stopping (in milliseconds).

lifetimeMS == 0: Always on lifetimeMS > 0: lifetimeMS milliseconds lifetimeVariance useEmitterSizes useEmitterColors [O,lifetimeMS) false false

Amount to vary lifetimeMS by. Not used for PENs. These apply to particle emitters attached to a particle emitter object.

306

Mission Objects

Chapter 8

Table 8.20. PD datablock parameters.

PanlmetIer
dLagCoefficient windCoefficient gLavityCoefficient
I

'Ra.
( 0.0 , 1.0 ) [0.0,1.0']
( -inf , inf )

Default
0.0 1.0 0,0

Desafptlon
Factor determining velocity subtracted per second. Percentage of wind vector added to particle vector. Gravitational acceleration for particle. Negative values cause particles to rise. Multiplier determining how much of the PED ejectionVeloci ty is added to the initial velocity of the particle, Incremental velocity added to particle velocity on a per-second basis. Particle life in milliseconds. At the end of its life, the particle is deleted. Amount to vary lifetimeMS by. Speed at which particle rotates about its facing vector. Only valid when PED orientParticles == false. Minimum random value added to spinSpeed. Maximum random value added to spinSpeed. Inverts interpretation of texture alpha. Sequence between additional textures, specified in animTexName [50]. Frame frequency for animated textures. Texture path and filename (PNG only). Must be <= 255 characters long. Additional texture path and filenames (PNG only). Used when animateTexture == tLue. animTexName [0 1 same as textureName.
I

inheritedVelfactor

[ 0.0 , inf )

I
I

0.0
I

constantAcceleration lifetimeMS lifetimeVarianceMS spinSpeed

( -inf , inf )

0,0 1000.0 0.0 0.0

( 100 , inf ) ( 100, lifetimeMS ) ( -10000 , 10000)

spinRandomMin spinRandOffil'1ax uselnvAlpha animateTexture framesPerSec textuLeName

( -10000 , 10000 ) ( -10000 , 10000 )


true or false true or false

0.0 0.0
false false

( 1 , 200 )
"Path + File Name"

1
\\11

animTexName[50l

"Path + File Name"

\'"

colors[4]

"r g b i"

"1.0 1.0 1.0 1.0"

Color interpolation values. Please note that only these values determine particle color. The texture is used as an alpha map, not for coloration. Size interpolation values. Key frames. These affect interpolation rates over life of particle.

sizes[4] times[4]
I

[ 0 , inf) [0, 1]

1 0.0, 1.0, 1.0, 1.0

307

Part III

Game Elements

Table 8.21.

field Name
Transform

PEN parameters.

position rotation scale

Used to set location of PEN. Values have no effect. Values have no effect. Not used by engine. PEND datablock name. PED datablock name. Initial ejection velocity for this emitter.

Misc

nameTag da taBlock emi t ter (Particle data in WE) veloci ty

Some of the datablocks below produce vectors. Those vectors are calculated from a series of vectors and scalars (from the datablocks and internally from the engine). In order to be clear, I will italicize vectors and bold scalars. Velocities are in world units per second, and unless otherwise specified, input vectors are unit vectors.

Particle Initial Velocity


Each particle is given an initial velocity vector at ejection time. The velocity vector is determined as follows:
emitAxis * PEN.velocity * ejectionAxis * ( PED.ejectionVelocity + PED.velocityVariance * 2.0 * rand[O.O,l.O] - PED.velocityVariance ).
emitAxis is always < 0, 0, 1> (in practice you can ignore this factor). ej ectionAxis depends on orientation, theta, and phi. rand [0.0,1.0] pro-

duces a random value between 0 and 1.0.

Particle Post-Ejection Velocity Changes


After being ejected, a particle mayor may not have its velocity modified.
NextVe10city == CurrentVe10city * ( (PD.constantAcceleration * Initia1Ve1ocity) (CurrentVe1ocity * PD.dragCoefficient) (WindVe1ocity * PD.windCoefficient) + 0.0, -9.81> * PD.gravityCoefficient)

Please note that there is a time delta component not shown.


308

Mission Objects

Ch.apter a

Particle Lifetime

Particle lifetimes are a simple concept. If a particle (s created at time n, at time n + lifetime, the panicle wHl be deleted. Lifetimes affect interpolation. which will describe next. The PD.lifetimeVarianceMS allows us to randomly va!)' individual lifetimes, which makes things seem less aniticial when viewed. Lifetimes are in milliseconds.
PD.lifetirn~

+ ( rand(-l,l]

* PD.lifetimeVarianceMS

8.8.B Particle Interpolations


Particles are subject to two types of interpolation: color and size. Color interpolation is the ability to modify the particle color over its lifetime. Similarly, size interpolation is the ability to modify the particle size over its lifetime. Interpolation is controlled by key frames (PD. times ( 4 I), of which Torque allows up to (our. The minimum value for a \<ey (rame is 0.0, and the maximum value is 1.0. Key frames should be used in order, and unused key frames should be set to 1.0. This is probably all still sounding rather mysterious, so rll give some examples and explain what they do. PD.cclorlOJ PD.color/l! PD.coler[2: PD.coler!3l PD.sHeIOJ PD.sizerll PD.size(2] PD.size(3] PD.time(OJ
~D.t:iTtle(lJ

"1 . 0 "1 . 0 "1.0 "1.0 1.0; 1.0; 1.0; 1.0; 0.0;


1. 0;

1.0 1.0 1.0 1.0

1.0 1 . 0";
1.0 0.0";

1.0 0.0" ; 1.0 0.0";

PD.time(2J PD. time ( 3 J

1. 0; II Unused 1.0: II Unused

The above example tells the panicle to remain at slze 1.0 for its entire lifetime and to fade smoothly from bright white to transparent. I?D.celer(OJ PD.color(l) PD.celer[2] PD.color(3) "1.0 "0.2 "0.0 "0.0 0.2 0.2 1.0"; 1.0 0.2 1. 0"; 0.2 1.0 1.0" ; 0.2 1.0 1.0";
309

Part III

Game Elements

PD.sizeIO] PO.size!l] PO,size!2] PO,size[3]

0.5;
1.0;
1.5;

2.0;

PO. time (0) 0.0; II 1/3 time here framed by time(Ol and time(1) PO. time! 1) = 0.33; II 1/3 time here framed by tlme(ll and time I:?) PO.time(21 = 0.66; II 1/3 time here framed by time(2] and time 131 PD.time(3) = 1. 0;

The above example causes the particle to smoothly increase from a size of O.S to 2,0 over the particle's lifetime. Additionally, the panicle's color is interpolated from a shade of red, to green, then to blue, where it stays {or the last one-third of its lifetime, Interpolation takes some prac(lce gettif\g used (0, but it's a nice touch (hat gives us some cool variations on particles,

8.8.9 PEN Lifetimes


Just as particles have Ufetimes, so can particle emitter nodes, A PEN can be told to emit particles forever or for a nxed duration.

II Emit forever after being created PD,1ifetimeMS = 0;


Emit for five seconds plus or minus 1.5 seconds after being created PED.litetimeMS = 5000; PED.lifetimeVarianceMS 1500;

II II

B.B.l0 PEN Particle Ejection Frequency


The PEND and PED datablocks give us three parameters in total to adjust the rate at which part.icles are emitted.
PEND. timeMul tiple. This changes the simulation time versus real time ratio. All events occur in simulation time. With the addition of this parameter. the particle emitter will view time as passing at the rate of PEND. timeMultiple realtime. This feature allows us to use the same PED tn two (or more) different emitters and vary the rate of emission. II also gives us a nice way to tune the overall rate of our effects.

310

Mission ObjectJ;

(hap{cl8

PEO, periodMS. This is the base time between particle ejections,

PEn, periodvarianceMS. This is the amount to vary the base time between ejections. Given these three parameters. the particle emitter wUl eject particles at random intervals, where the time between ejections is (l/E>END. timeMult iple) - (PED.periodMs-PED.periodVaria~ceHs) and (PEND.timeMultiple) "(PED.periodMS+PED.periodVariar.ceHS). To clarify this, let's look at some examples.

II Emit a new particle every 200 milliseconds with no variation PEND,timeHultiple = 1,0; PED.periodMS = 200; PED.periodVarianceHS = 0,0;
In the above example, the panicle emitter will see lime passing at the normal rate. so that one second of real time is equal to one second of simulation time.

II Emit a new particle every 400 milliseconds with no variation


PEND.timeMultiple = 0.5; PED.periodMS = 200j ?D.periodVarianceMS = 0.0; In the above example, the particle emitter wiU see time passing at hal{ the normal rate, so that two seconds of real orne are equal to one second of simul.ation time.

II Emit a new

particl~

PEND.t~meMult~ple

every 100 milliseconds +1- 25 ms 2,0;

PED.periodMS = 200; E>ED.periodVarianceMS

= SO;

In the above example, the particle emitter will see time passing at twice the normal rate, so that one second of real time is equal to two seconds o( simulation time.

8.8.11 Theta and Phi Explained


ParticleEmitterData has four fields: thetaMin, thetaHax. phiReferenceVel, and phiVariance. Together, they control the direction in which our emitter ejects particles. Although they have scary-sounding names, these fields are really quite easy to use, To show this, let's start with the theta fields, and then we'll discus the phi fields.

311

Parr III

Game Elements

Figure B. 1B.

thetaMin and thetaMax


Theta controls the up and down of (he emitter's ejection vector. Imagine, if you will, that you are standing to the side of an emitter. If we play with the theta parameters, we can make the emitter eject particles anywhere straight up and straight down (Figure 818), Torque supplies the two parameters PE,D. thetaMin and PED. thetaMax. These act as boundaries. We point the emitter in <1 specific direction such as 90 degrees (slraight out) by setti[lg PED.thetaMill to 90 and PED, thetaHax to 90. Alternatively, if we wish (0 spread our particles ou(, we can set PED. thetaMin to 0 and PED. thetaMax to 90. Now, particles will be randomly ejected with an ejection vector pointing between straight up and straight out.

Theta ejection veccors.

45 dell

Odell

90 deg

+----'!

135 de;
180 deg

Since theta was so simple, you might jump to the conclusion that phi controls the left and right. If you did, you would be both right and wrong. The phi parameters do control the ejection vector's left to right pointing, but [lot Uke Figure 8.19. the theta parameters. Whereas PSD. the taMi n and PI:: D. the taMa x were used Phi ejection vectors. to set the minimum and maximum up-down ejection angles, our minimum 180 deQ phi angle is always zero degrees and PED.phiVariance controls the upper angle. This means we cannot point our phi in the same way we can theta. (See Figure 8.l9,) So, what about PED.phiReferenceVel? This strange paramBO dag +---+----1. 270 dell eter causes the emitter to spin clockwise about its up vector. PED.phiReferencevel is measured in degrees per second. OK. let's summarize what the theta and phi parameters do (or o dag us, PED. thetaMin and PED. thet.at'1ax allow us to control the updown pointIng of our ejection vector. Furthermore, we can specify a y range of up-down positions between which the ejection vector will L. randomly vary. Next. PEG. phiVar iance aHows us to change the right-left pointing of our ejection vector, but we can only adjust the right direction of the ejectlon vector. Lett is always stuck at 0 degrees. Finally, PEG. phiReterenceVel can be used to cause the emitter to spin clockwise about its up vector at phiReterenceVel degrees per second.

L)(

phiReferenceVelandphiVariance

B.B.12 Orientation Explained


We've covered orienting the ejection vector, but what about the particle itself? First. remember that the particle is actually a billboard, Initially, r said that these billboards would normally (ace the camera. The PED orientation parameters give us the ability to choose between various billboard orientations, Table 8.22 summarizes particle orientation options.
312

Mission Objects

Chapter 8

r PIID. orient1'arUcl
false

lBD.orlentoDV.locl~

RelUlting Ortentation
Screen oriented.

Table 8.22.

don't care

Particle always faces screen , (camera).

Particle orientation options.

true

false

Face ejection.
Face along ejection vector.

true

true

Face motion.
Face along trajectory.

8.8. 13 Animated Textures


Among the other cool features supported by Torque'sparticle emitter is the ability to animate a particle via multiple textures. In Torque, you can specify up to 50 separate textures. 4 Then, while the particle is being displayed, Torque will cycle through these images. It's really quite simple to do this. Take a look at the following example.
PO.animTexName[O] PO.animTexName[l] "-/path_to_texture/textureO"; "-/path to texture/texture1";

If you're willing to edit the engine, you can set this value to anything you want (within reason).

PO.animTexName[49] = "-/path_to_texture/texture49"; PO.framesPerSec = 1; II Play one frame per second

In the above example, we've specified 50 distinct textures for use in our sequence. Then, we specified that they must be played one (frame) per second. When the sequence gets to the end, it will begin to repeat. It's really that simple.

8.8. 14 MUltiple Particles?


You might recall that we could specify more than one particle for the PEO. particles parameter. If you specify multiple particles for an emitter's PED, the emitter will eject the particles in order and then repeat. The following reasonable questions arise. 1. How do I specify more than one PD? 2. How many can I specify? Here are three examples of the syntax for specifying three particles for aPED.
particles II OR particles II OR particles PO NameO TAB PO Name1 TAB PO - Name2; PO NameO SPC PO Name1 SPC PO Name2; "PO NameO PO Namel PO Name2";

313

Parr IH

Game Elements

Basically, PED. particles needs to be a whitespace-separated string of PD names. You may specify as many particles as you need.

8.8.15 Holy Popping Particles!


An interesting problem I initially had while playing with particles was a disturbing popping effect when the particles' po. 1 i f et imeMS limit was hil. ThIS can have several sources, but it you study the effect. it should be ilpparent that the cause is simply the (act that a very visible object is suddenly popping out of existence. To make this transition subtler. just use the panicle interpolallon par.ameters. Here are some suggestions: Be sure your interpolations aresmooth; i.e.. don't use values like 0,1,0,5, 0.6, 1.0 unless you are looking for a shuddering effect. Fade particles by lowering the fourth PD. colors parameter (which repre sents intensity or alpha) over the lifetime o( the particle, Shrink particles in the latter part of their life,

8.8. 16 Can I Mount Emitters?


A common question in the forums is, "Can 1 attach an emieter to my XYZ)" Unfortunately, you may not attach a particle emitter to an arbitrary shape or node in a shape. Many shapes provide specializ.ed nodes (or particular emitter effects. but TCE does not support arbitrary mounting of panicle emitters

8.8.17 Can I Move Emitters?


Another question I often see is. "Can r move an emitter after [place it?" Often, the answer 1 see given to this questions is, "No," However, this is not true. There is a way to move panicle emitters. If you want to move a particle emitter after it is placed in the world, do the following.

m of the emitter you want 10 move in a global variable or in another appropriate location. 2. In script, modify the position held of the particle emitter node. Yes, modify the pos i t i on field. 3. Last. to move the PEN, simply rescale the PEN, uSlng lts current scale,
1. Store the SmyPEN ~ II ... create the PEN and store its 1D $myPEN.position = "10 10 10"; II We want to move to< 10 10 10 > SmyPEN.setScale( $myPEN.getScale() );
314

Sure. it's a bit of a hack. but it gets the job done.

Mission Objects

Chapter 8

8.8. 18 Maze Runner Lesson # 12 (90 Percent StepJ-Teleport Station Effect


If you are building the Maze Runner game while you read this guide, we are now going to create the datablocks for a set of particle emitters that will be used later to mark the position of our teleport stations. We will need three distinct versions of this emitter. So, our strategy will be to create a base ParticleData datablock and a base ParticleEmitterData datablock using the previous ParticleData data block. Then, we will use the inheritance feature of Torque5cript to create two copies of each datablock with minor modifications. This will give us a total of six datablocks. For the ParticleEmitterNodeData datablock, we'll just use the basePEND datablock that comes with this guide. You may have noticed that the TeleportStation_PDO data block definition only supplied array elements 0, I, and 2 for of each of the colors[], sizes[J, and times[] arrays. You may wonder exec("./MazeRunner/mazerunnerplayer.cs"); II MazeRunner why Idid not specify array index three for exec("./MazeRunner/teleporters.cs"); II MazeRunner each of these arrays. The reason for this is ParticleData (TeleportStation_PDO) simple. Interpolation We want our particles to be nebulous particles of medium size with a red, occurs between times green, or blue coloration. 0.0 and 1.0, and since times[2] is defined as 1.0, the interpolation datablock ParticleData(TeleportStation PDO) will automatically dragCoefficient = 0.0; stop when it gets to gravityCoefficient = -0.50; colors[2], sizes[2]. and inheritedVelFactor = 0.0; times[2]. This is not constantAcceleration = 0.0; to say that we could lifetimeMS = 400; make times[3] less lifetimeVarianceMS = 100; than J.O and add more useInvAlpha = false; entries, but rather textureName = "~/data/GPGTBase/particletextures/smoke"; that we don't need to colors[O] "0.70.10.10.8"; <@5seallOfthearray colors[l] = "0.7 0.1 0.1 0.4"; . ~~ elements. colors[2] = "0.7 0.1 0.1 0.0"; ~"'f --------" sizes[O] 0.1; sizes[l] = 0.3; 315 From the accompanying disk, please copy the file "\MazeRunner\Lesson_012\ teleporters.cs" into the directory "\MazeRunner\prototype\server\scripts\ MazeRunner". Now, edit the function onServerCreated () in the file "\MazeRunner\ prototype\server\game.cs" to look like the following (bold lines are new or modified).

Copy Required Files

Parr III

Game Elements

sizes[2] times[O] times[l] times[2]


};

0.3; 0.0; 0.5; 1.0;

As can be seen. this particle will float upward since it has a negative gravity coefficient; it has a short lifetime between 300 and 500 milliseconds;
Figure 8.20.

the particle it uses is nebulous (see negative image in Figure 8.20); it fades from medium red to dark red evenly; and
it starts off small and triples in size over time.

Smoke particle

ParticleEmitterData (Te/eportStation_PEDO)
datablock ParticleEmitterOata(TeleportStation PEOO) ejectionPeriodMS 1; periodVarianceMS == 0; ejectionVelocity == 2.0; ejectionOffset == 0.5; velocityVariance == 0.5; thetaMin == 0; thetaMax == 80; phiReferenceVel == 0; phiVariance == 360; overrideAdvance = false; particles "TeleportStation POOH;
};

As can be seen, this particle emitter ejects a new particle every millisecond, meaning we'll have up to 500 particles alive at any time (per emitter); it ejects particles at 1.5 to 2.5 world units per second starting at the center to 0.5 world units out; the ejection vector will be anywhere about the center and starts from slightly upward to straight up; and of course, it uses the particle we just made.

Duplicate Datablocks
The last step before trying these emitters out is to duplicate them so we have three sets. As you can see when looking at the code, we have taken advantage of TGE's datablock inheritance:
316

Mission Objects

Chapter 8

datablock ParticleData(TeleportStation PDl colors[O] "0.1 0.7 0.1 0.8"; colors[l] "0.1 0.7 0.1 0.4 h ; colors[2] "0.1 0.7 0.1 0.0";
);

TeleportStation_PDO ) {

datablock ParticleEmitterData(TeleportStation PEDI particles "TeleportStation PD1";


);

TeleportStation_PEDO ) {

datablock ParticleData(TeleportStation PD2 colors[O] "0.1 0.1 0.7 0.8"; colors[l] "0.1 0.1 0.7 0.4"; colors[2] "0.1 0.1 0.7 0.0";
) ;

TeleportStation_PDO ) {

datablock ParticleEmitterData(TeleportStation PED2 particles "TeleportStation PD2";


) ;

TeleportStation_PEDO ) {

We only needed to change the particle colors and to use the correct particle in our new emitters.

Testing the Emitters


We're not ready to use these emitters in our game, but we should test them. Do the following. 1. Start up your Maze Runner prototype. 2. Load the "Maze Runner" mission. 3. Use the Creator to place a particle emitter (Mission Objects -7 Environment -7 ParticleEmitter). 4. Give the emitter (node) any name you like. 5. Use the base PEND ParticleEmitterNodeData datablock. 6. Select one of the three ParticleEmitterData datablocks we just examined (Figure 8.21).
t,
i~

t ... t :

,... -

~1"'1

t.

Figure 8.21.
J

Testing the emitters.

ParticleEmitter Dialog Settings

Resultant Emitters

317

Part III

Game Elements

8.9 fxShapeRepficator & fxFo'iageRepficator


These two replicators are birds of a feather and are both crealed by Melvin May. Their purposes are multi(old: allowing multiple objects to be placed automatically and randomly within specified bounds, allowing this to be done in such a way as to make the scene look more organic (i.e., not artificial), aDd reducing the network transmission cost of multiple related objects to that of a single object plus a few additiOflal parameters. Melvin May has managed to do this quite succ.essfully, very much to the appreciation of Torque users. Furthermore, his fx objects are, for the most part, easy to understand and use. Before we get into the usage of these two replicators, ['11 give a succinct list at all parameters tor both the fxShapeReplicator and the fxFoliageReplicator. To save space and due to the common nature of these replicators, I'll combine their parameters into one list, indicating when a parameter exists in the shape replicator but not the follage replicator, or vlce versa.

B.9. J Replicator Features


The replicators have the following features:
a

II

II

Directed random placement. Using a tricky inner and outer-eliLpse affor dance. you can direct Torque to replicate a specific number of objects in random locations within a clearly defined area. Multiple toggleable placement restrictions. Because rafldom pla.cement wouldn't be any good jf you couldn't specify rules for where to place and flat to place, the replica tor missiOfl obiects both have a slew of toggleable tests for placing objects. Dimension and orientation controls. In order to make a scefle more organic, you can provide metrics that will allow objects to be fandomly sized and oriented within set bounds. Advanced culling. The foUage replica/Of provides the ability to tune the culling algorithm. The culling algorithm is responsible for choosing when to render objects and directly affects frame rate. The ability to fifle tune this is a real plus. Animation and lighting. Foliage can be both animated and lit (or self-Iit\. You have direct control over how this is done.

8.9.2 Placing Replicators


318

Repliciltors are placed much like any other item in the world. You just drag them and drop them where you wish them to be. The location of the repHcator

Mission Objects

Chapter 8

PGIItIon
Inside area defined by InnerRadiusX and InnerRadiusY? Inside area defined by OuterRadiusX and OuterRadiusY, and outside area defined by InnerRadiusX and InnerRadiusY? Outside area defined byOuterRadiusX and OuterRadiusY?

QlnPlace
No

TableB.23.
Replicator placement rules.

Yes
No

can be the center of a placement target. The size and shape of this target are controlled by the inner and outer radius parameters. These parameters can be used to create two ellipsoidal areas. If we ignore restrictions for a moment, placement rules simply become those shown in Table 8.23.

8.9.3 Replicator Visual Feedback


Melvin May has supplied a nice visual feedback mechanism for seeing where Figure 8.22.
Visual feedback of replica tor.

the shapes will and will not be placed. Examining the image in Figure 8.22, we can see two ellipses that were created with the following settings.
InnerRadiusX InnerRadiusY
=

5, OuterRadiusX

==
=

25

= 15,OuterRadiusY

= 20

If you look closely, you will see that objects are randomly placed in the area outside inner ellipse and inside outer ellipse.

8.9.4 Seeds
A very important aspect of replicators is that they will produce the same result each time they are used as long as they are given the same Seed. The Seed is used as an input to a random number generator. This generator is used to produce and place all objects associated with the replicator.

8.9.5 Replicant Count


You may select how many objects you wish to replicate using either the
ShapeCount or the FoliageCount parameter, depending upon which replicator you are using. It is important to understand that this is a theoretical

maximum, not the guaranteed number of objects you will get.

8.9.6 Placement Restrictions (Restraints)


Besides the .ellipses and the position, what else controls placement? There is a nice set of "knobs" with which we can tune placement rules. These are called restrictions or restratnts in the foliage and shape repJicators, respectively. Their names are pretty self-explanatory, but just in case, I'll explicitly spell out their use in Table 8.24 and show an example in Figure 8.23.

319

Part III

Game Elements

Table 8.24. Restrictions and restraints.

RettrIdion I RestnInt
AllowOnTerrain AllowOnInteriors AllowOnStatics

R-.Jt
If this is set to t rue, objects can be placed on terrain if present. If this is set to t rue, objects can be placed on interiors (buildings, etc) if present. If this is set to t rue, objects can be placed on other shapes if present. This means that if you are using the fxShapeReplicator, it is possible to have objects get stacked on top of each other by a replicator. See Figure 8.23. When objects are placed on terrain, they will not be placed on areas with a slope greater than or equal to this value.

AllowedTerrainSlope

I AllowOnW8ter
false true

AllowWaterSurfIIce

AllowOnTeITIIln
-

blult
Objects cannot be placed in areas with water. Objects can be placed on surface of water. Objects can be placed on terrain below water.

true false

Figure 8.23. Stacked shapes

true

true

(AllowOnStatics == true).

In addition to the restraints listed in Table 8.24, fxShapeReplicators offer three additional parameters. AlignToTerrain causes shapes that are placed on terrain to align to the terrain's up vector. Furthermore, you can adjust how this alignment occurs by adjusting the parameter TerrainAlignment, which is a 3-value vector. Lastly, you can enable or disable shape collision boxes by setting Interactions to true or false, respectively.

Interactions Must Be True for Collisions


We just covered this, but I must restate it nonetheless. If you have the Interactions field set to false, collisions for fxShapes are turned off. A lot of new users have this problem and complain about it vociferously in the forums. I'm here to save you the embarrassment of being told, "Set the Interactions field to true. Duh." Hey, nobody is perfect.

8.9.7 Retries
Well, with all these rules determining whether an object can be placed, you must wonder what the replicator does if it finds it can't place an object. Well, just like you or me, it tries again. You can control the number of attempts the replicator

320

Mission Objects

Chapter 8

will make per object with the FoliageRetries or ShapeRetries parameter. Why not just try until an object can be successfully placed, you ask? Consider the case in which there is no legal place left to put an object. In this case, without a retry limit, the replicator would attempt to place objects forever.

8.9.8 Foliage Dimensions


We've finished talking about the common attributes between the' fxFoliageReplicator and the fxShapeReplicator. Now let's jump into some of the additional features offered by the fxFoliageReplicator. Because we're going to be using the same image over and over to simulate some kind of foliage feature, we'd like an inexpensive way to make these images seem different. The dimension parameters give us this. For example, let's say we choose the following settings.
FixSizeToMax == false FixAspectRatio == false RandomFlip == true MinWidth == 0.5 MinHeight == 0.5 MaxWidth == 1.5 MaxHeight = 2.0

What we would get are billboards that are randomly between 0.5 and 1.5 times their default width and 0.5 and 2.0 times their default height. Additionally, the image may be randomly flipped around its vertical axis (Le., flipped horizontally). This flipping will be useful if we have a nonsymmetric image. So, what about that aspect ratio business? Well, if you are familiar with texture mapping, you wi! I understand that without maintaining the proper aspect ratio, images may look stretched. The FixAspectRatio parameter forces the randomly selected height/width to be a fixed multiple of the original. Some example images in Figure 8.24 show what I'm talking about.
Figure 8.24.
Maintaining the aspect ratio.

128x128 PNG

same PNG 2X Height FixAspectRatio == false

Same PNG 2X Height FixAspectRatio = = true

321

Part I'

Game Elements

Lastly, let's discuss Offset.Z. This is helpful to fu< little issues you run into where the texture may be slightly embedded or. slightly above a surface, If this happens, just increase or decrease Offset.Z slightly until the problem is fixed.

8.9.9 Shape. Dimensions and Rotation


fxShapeReplicators allow you to adjust the dimension and rotation of shapes with the parameters in the ObJect lransforms group. You can allow random scaling by setting ShapeScaleMin and ShapeScaleMax accordingly. Additionally, you can allow for random rotation by setting ShapeRo ta t ionMi n and ShapeRota tionMax to nonzero values. Values are chosen between the minimum and the maximum on a per-axis basis. Finally, Of fsetZ is offered under the group for fxShapeReplicators and has the same purpose as noted
above.

8.9.10 Foliage CUlling


Of all the attributes in the fxFoHageRepHcator, the culling parameters were the least intuitive to me. So, before we (ump into them, perhaps a quick description of view culling is in order.
View Culling

If you think about it for a moment, it will be apparent that it would be highly inefficient to render aJJ objects in a mission. when only a small fraction of them aJe in a position to be visible. In reality, the objects in front of the camera are the only objects that we really need to render. This set of objects is called the potentially visible set (PVS). There are many ways to build a PVS, In the case of fxFoliageReplicators, when the useeu lling parameter is fa lse. each billboard is individually tested for visibility. In the case of a small set of billboards. this is probably the most efficient way to culL However, once you have a large number of objects, this method quickly begins to consume too much CPU time.
Quadculling

At this point, you should consider (liming on culling by setting useCulli.ng to true. Now, culling is tested against a set of quads instead of individual billboards. A quad is a rectangular area (usually a square) with a fixed dimension. In the case of quadculling, a spedned area is subdivided into multiple quads. Each object that is within an area defined by a quad is algorithmically associated with that quad. Objects that cross borders between quads are assigned to each quad they touch. Finally, i1 a quad is deemed to be visible,
311

l __

Mission Objects

Chapter 8

Figure 8.25. Visible feedback for


CUlling.

all objects associated with that quad are marked as visible and subsequently rendered. The images in Figure 8.25 are taken from an in-game shot to demonstrate what the visible feedback for quadculling looks like. They demonstrate the discussion thus far.

Configuring (Quad) Culling


I'm sure that this is all just fascinating, but it still leaves us with the dilemma of how to choose whether to cull, and then if we choose to cull, how to set up our culling. Unfortunately, the number of factors involved turns this more into an art than a science, and the final test is always going to be frame rate. However, I'll supply some rules of thumb to help you out in your choice.

To Cull or Not to Cull


Do not use culling for small sets (1-100) of billboards. Generally, it is better to use culling if the total billboards number at least two to three times the number of quads (accounts for overhead associated with algorithm). For a large number of objects (hundreds to thousands) spread over a large area (one quarter of map or more), it is best to use culling. Culling will not help much if your objects are not evenly distributed between the quads.

Selecting a CullResolution
Select your CullResolution such that the number of objects comes out to at least two to three times the number of quads. Select your CullResol ution such that it can evenly divide OuterRadi usX and OuterRadiusY. You may need to adjust these slightly to assist this process. Powers of two are nicest, if possible.
323

Parr III

Game Elements

Testing Efficiency of Culling


As noted above. the best way to test the efficiency of your culling is to check your average frame rate. An easy (if possibly slightly inaccurate) way of doing this is the following. 1. Get out of Mission Editor Mode.

2. Start the console' (N ).

3. lYpe: metrics (fps) ;.


4. Exit the console (N). S. Walk/fly around your scene and observe your frame rate. Look for hot spots where it dips. The metrics (fps) ; command will create a CUI in the upper left corner of the screen, showing frame rate and mspf (milliseconds per frame). This cur will be shut off when you start the Mission Editor and does not render properly while it is running. Additionally, after hitting Apply (when setting your culling parameters). you can get additional data from the console (N). Each time you hit Apply, something like the following is printed in the console.
fxFoliageReplicator - Lev: 3 PotNodes: 85 Used: Obj5: 656 Time: 0.00165 fxFoliageReplicator - Approx 0.06Mb allocated. 58

From this, we can see that the culling level is 3, which means it is a 2 3 x 23 (8 x 8) set of quads. The quads are approximately 58/85, or 68 percent, utilized (i.e., billboards are in 68 percent of the testable nodes). There are a total of 656 objects (500 billboards and 156 phantom objects due to retries). It takes about 16 milliseconds to build and render the fxObject. And finally, the entire fxObject takes up about 0.06 MB.

Other Culling Features


In addition to quadcuJling, there are some other features in the culling parameters section, specifically the view, fade, and alpha parameters. These parameters are not affected by the useCull parameter and are always on. ViewDistance and fadelnRegion work together to determine when an object begins to fade into view and when it is fully faded in. These two parameters form concentric spheres around the camera, where ViewDi stance defines the radius of the inner sphere and FadelnRegion + ViewDistance defines the radius of the outer sphere. When an object is at the perimeter of the outer sphere, it will begin to become visible, fading completely in at the perimeter of the inner sphere. If you wish your objects to stop rendering at an

324

Mission Objects

Olaprer8

alpha greater than 0.0, you can cause this to happen by setting AlphaCutoff to the desired alpha, between 0.0 and 1.0. See Table 8.25 and Figure 8.26. ViewClosest and FadeOutRegion also work together, but their effect is the opposite of ViewDistance and FadelnRegion. Conversely, these two parameters are used to determine when an object begins to fade out of view and then become fully transparent or not rendered. Again, these two parameters form concentric spheres around the camera, where ViewClosest defines the radius of the inner sphere and FadeOutRegion + ViewClosest defines the radius of the outer sphere. When an object is at the perimeter of the outer sphere it will begin to fade, fading completely out at the perimeter of the inner sphere. See Table 8.25 and Figure 8.27.
BIllboard'. DIItance to Clmen
Distance > ViewDistance + FadelnRegion ViewDistance < Distance < ViewDistance + FadelnRegion View Distance < Distance

Render?
no yes (if alpha> AlphaCu tof f) yes

Table 8.25. Using ViewDistance and FadelnRegion.

Figure 8.26.

Figure 8.27.

ViewDistance and FadelnRegion.

ViewClosest and FadeOutRegion.

IUlboIInf. I*Iance to CIIner8


Distance > ViewClosest + FadeOutRegion ViewClosest < Distance < ViewClosest + FadeOutRegion ViewClosest < Distance

bnder?
yes yes (if alpha> AlphaCutoff) no

Table 8.26. Using ViewClosest and FadeOutRegion.

325

P<'lrllll

Game Elements

You may wonder why you would want to do this. Consider the case where you are in a vehicle. Fading out will keep objects from suddenly being inside the vehicle. Last, l'll mention GroundAlpha. This parameter can be used to force the bottom of billboards to have a lower alpha value. This can be used to moderate the harsh intersection between billboards and the ground, giving the transition a cleaner look. Just set it to a value lower than 1.0 to see its effect. Adjust it until you are pleased with the end result.

8.9.11 Foliage Animation


Foliage animation is a feature that allows us to make a more interesting and convincing scene. Consider the .case where your foliage is long grass and fronds. Wouldn't it be more realistic if the grass and fronds blew in the wind? Yes, of course it would be, but how do we achieve this look? With foliage animation, of course! Setting SwayOn to true will enable the animation. You may cause your billboards to sway side-to-side or and front-to-back using the SwayMagSide and SwayMagFront parameters, respectively. Furthermore, you can add a little spice to the swaying by allowing the sway times to vary between MinSwayTime and MaxSwayTime. Last, you may choose to enable SwaySync, where all objects will sway together in the same way, or you may disable it and all objects will sway on their own pattern. One word of caution. If billboards sway so much that they touch each other, you will get rendering artifacts.

8.9. 12 Foliage lighting


Foliage lighting is the last parameter group we will discuss. It is another group that is used to make the scene look more interesting. With these parameters, you may enable self-lighting (LightOn). Furthermore, if you set LightSync to false and give different values for MinLuminance and MaxLuminance, each billboard will be self-lit with its own randomly selected level of light. Please note that this lighting can be animated. If all of the above lighting parameters are set as noted and then you set 1 ight Time to a nonzero value, each billboard's lighting will vary over time. lightTime is the time for a fade in one direction. So, to fade from MaxLuminance to MinLuminance back to MaxLuminance will require (lightTime .. 2) seconds.

8.10 fxSunlight
326 As previously mentioned, the Sun object controls scene lighting and fxSunlight provides the ability to have a visible sun(s) in the sky. Upon first inspection,

I~-

---

----

-----

Mission Objects

Chapter 8

this mission object may seem a bit daunting, with its myriad parameters (lerps, animations, etc), but it is really quite easy to use. You've got to hand it to Melvin May, though. He hardly makes a resource without a "few" options.

8. 1O. 1 fxSunlight Features


fxSunlight has the following features. Local flare. A bitmap representing the lens flare of a camera. Remote flare. A bitmap representing the sun itself. Position/orientation parameters. To make life easy, the fXSunlight parameters that control its position are similarly named to those found in the Sun mission object: namely, azimuth and altitude. Animations. Just about every characteristic of the fXSunlight object can be animated. Furthermore, the animation system is a very flexible key-based animation system.

8. 10.2 Adding a New fxSunlight


1. Start the Creator. 2. Find and click Mission Objects ~ environment ~ fxSunlight. 3. Enter a name for this Sun in the pop-up box. (e.g., "Smiley"). 4. Click OK. At this point, if you look around, you should see the default fxSunlighl. Now, do the following. 5. Switch to the Inspector. 6. Locate your new sun ("Smiley"). 7. Select it.

8.10.3 Changing the Sun Images


fxSunlight has two texture parameters.
Media

LocalFlareBitmap

This texture represents a lens-flare effect. If you do not wish to have this effect, just clear this parameter. This texture will render if it is in line of sight. If it is blocked by terrain or an object, it stops rendering.
It is best to use a texture with an alpha layer.
Media
~

RemoteFlareBitmap

This texture represents the sun itself. It, too, can be disabled, just by clearing this parameter. 327

Parr III

Game Elements

Unlike the local flare, this texture renders all the time, although the terrain and objects can occlude it Again, it is best to use a texture with an alpha layer. Note that you should make both textures the same way; that is, if one has an alpha layer, the second one should too.

8.10.4 Positioning the Sun (Render Position)


Unlike most mission objects, the standard position, rotation, and scale are meaningless and do not control where the fxSunIight object is rendered. However, there is a marker at Transform 7 position. I would just select a value for this such that the marker does not get in your way while editing. Render position, when it is not being animated, is based on the same two concepts as those used for the Sun object, azimuth and elevation. If you do not understand these concepts, I suggest you quickly reread the Sun object description in Section 8.5.
SunOrbit

7 SunAzimuth

This controls the horizontal angle of the fxSunlight effect's bearing about the z-axis. Legal values: [0, 360}. Make this the same as Sun 7 Mise 7 azimuth.
SunOrbit

7 SunElevation

In simple terms, this controls the elevation, but in reality, this is a polar angle. Again, if you don't understand this, see the Sun object description in Section 8.5. Legal values: [-90, 90]. Make this the same as Sun ~ Mise 7 elevation.

8.10.5 Changing Lens Flare Effects


You can modify various effects, such as the following.
LensFlare ~ FlareTP. If this is not checked, the lens flare will not ren-

der in 3rd POV.


LensFlare ~ Colour (r g b i)

If you find a white lens Hare boring, you can give it a different fixed color with this parameter. Each individual value can be between G.O and 1.0. Intensity has no effect.
328

Mission Objects

Chapter 8

LensFlare 7 Brightness

You can set a fixed brightness with this parameter. Legal values: [0.0.1.0].
LensFlare 7 FlareSize

This parameter can be used to scale the flare size to your preference. This modifies the size of the sun. too. Legal values: (0.0. inf).
LensFlare7FadeTime

This parameter determines how long it takes the lens flare to fade away when it is occluded. Remember. occlusion turns it off. Legal values: (0.0, inf)o LensFlare 7 BlendMode. Understand that the flare is rendered. meaning it needs to be blended with the prior contents of the frame buffer. To accommodate various effects. fxSunlight supports three blending modes ( 0 .. 2J.
O.glBlendFunc(GL_SRC_ALPHA, GL_ONE) Flare < r g b a> replaces frame buffer < r g b a > . 1.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

Flare < r g b a> is linearly blended with the frame buffer < r g b a> .
2.g1BlendFunc(GL_ONE, GL_ONE) Flare < r g b a > is added co frame buffer < r g b a > .

If you stopped righc now. you would know 90 percent of what you need

to know about the fxSunlight object. However. if you want Co do some really cool things. like animate the color, brightness. and size. or if you want it to rotate and to move around over time. then continue reading.

B. 10.6 Animating the Sun and Lens Flare


Now that we have an fxSunlight object set up. we can make it more interesting by animating some of the sun and lens flare effects. However. before we take a brief tour of the fxSunlighc animations, let's discuss some common animation parameters.

Animation Overview
The fxSunlighc animations are all similar in nature. So. we'll discuss how they work in general and then limit our discussion to specifics for each in the following pages. Animations provide the parameters in Table 8.27. Tables 8.28-8.33 list the specific parameters for color, brightness. rotation, size. azimuth, and elevation, respectively.
329

Part II

Game Elements

',1

Table 8.26.
Animation parameters.

",L

~.,!

.. ~

PurpD18
The names of these fields vary, bt,lt they all have the same purpose. They are Boolean values enabling or disabling animation for this fxSunlight feature. The LERP enables are Boolean values, enabling linearly interpolated (smooth) vs. noninterpolated (stepped) transitions. Only color animation supports this feature. If this Boolean field is set to true, the colour animation will use its corresponding RedKey for all colour animations. These values define the outer limits of the animation range for this particular feature. Their types are feature-specific. Each animatfon has at least one key string, and some may have more. These keys are used for determining how the animation transitions occur. Key strings contain the letters a through z, where beginning of a sequence and z is the end.

Enable Option

LERP Enable

Single Key Enable

Min and Max Values (Extents) Key String(s)

a is the

Animation Time

This floating-point value is used to define how long the animation takes to play in seconds and fractions of a second. This time is the round-trip time, i.e., Begin -7 End -7 Begin.

Table 8.27.
Color animation. Enable Option LERP Enable Single Key Enable Min and Max Values (Extents)

,.......
AnimColour LerpColour SingleColourKeys MinColour,MaxColour
(four-element floating-point vector) Key String(s) Animation Time

RedKeys,BlueKeys,GreenKeys ColourTime

Table 8.28.
Brightness animation, Enable Option LERP Enable Min and Max Values (Extents) Key String(s) Animation Time

AnimBrightness LerpBrightness MinBrightness, MaxBrightness (floating-point) BrightnessKeys BrightnessTime

330

Mission Objects

Chapter 8

GenerIc Parameter
Enable Option LERP Enable Min and Max Values (Extents) Key String(s) Animation Time

Table 8.29.
AnimRotation LerpRotation MinRotation, MaxRotation (floating-point) RotationKeys RotationTime

Rotation animation.

Table 8.30.
Enable Option LERP Enable Min and Max Values (Extents) Key String(s) Animation Time

AnimSize LerpSize MinSize, MaxSize (floating-point) SizeKeys SizeTime

Size animation.

Table 8.31.
Enable Option LERP Enable Min and Max Values (Extents) Key String(s) Animation Time

AnimAzimuth LerpAzimuth MinAzimuth, MaxAzimuth (floating-point) AzimuthKeys AzimuthTime

Azimuth animation.

Table 8.32.
Enable Option LERP Enable Min and Max Values (Extents) Key String(s) Animation Time

AnimElevation LerpElevation MinElevation, MaxElevation (floating-point) ElevationKeys ElevationTime

Elevation animation.

331

Part III

Game Elements

8. 10.7 Maze Runner lesson # 13 (10 Percent Step}-Celestial Bodies


If you are building the Maze Runner game while you read this guide, we

are now going to create some celestial bodies to go with our game. I have to apologize, but the celestial bodies we will implement are just too darn big (in terms of code) to show in the book. Instead, I will summarize their behaviors here and allow you to look at the scripts yourself.
Loading the Celestial Bodies The celestial bodies example as been created for you. To add it to the Maze Runner mission, follow these steps:

1. Open the file "\MazeRunner\prototype\data\missions\mazerunner.mis". 2. Open the file "\MazeRunner\Lesson_013\CelestialBodies.cs" and copy the contents into your copy buffer (like you are doing a copy-paste operation). 3. Paste the data you just copied into the "mazerunner.mis" file before the following lines:
I;
//--- OBJECT WRITE END ---

Now, you can restart your Maze Runner prototype, load the Maze Runner mission, and you should see three celestial bodies in the sky.
Dying Star

The first celestial body is the "Dying Star." This celestial body is designed to represent a sun in our game-world solar system. This sun is approaching the end of its life and has shifted from yellow to red. To create the effect of a sun with moving sunspots, I have animated the brightness, the coloration, the size, and the rotation. Together with the image we are using for the sun, it may give the illusion of an active sun surface.
Far Planet

The next celestial body is the "Far Planet." This celestial body is designed to represent a distant planet in our game-world solar system. It is stationary relative to the planet we are on.
Near Moon The last celestial body is the "Near Moon." This celestial body is designed to represent a moon rotating about our planet. Its azim uth changes slowly over time; during this transition, it rises and falls in the sky.

332

l_

Mission Objects

Chapter 8

8.11 Physical Zones IP-zonesJ


Physical zones are one of those simple, "Gee whiz, ain't that cool" kinds of constructs. In fact, of all the standard Torque mission objects, these are probably my favorite. Physical Zones, or p-zones for short, allow you to define areas in your game with modified gravity and/or velocity modifiers and/or an applied force. First, we will cover the vety few parameters p-zones have, and then we'll leap right in.

8. 11. 1 veloci tyMod


The velocityMod attribute does pretty much what it sounds like it will do. Let's say we have a p-zone with a veloci tyMod of 2. If the player enters the p-zone with a velocity of 10.0 world units per second, that player will leave the zone with a velocity of 20.0 world units per second. Actually, the velocity modification is instantaneous, occurring directly after entering the p-zone (Figure 8.28). It should be noted that there are some issues with extraordinarily high veloci tyMod values. If the multiplier is too high, the engine can freeze for long periods or even crash. So, my suggestion is to keep the values low while you experiment. The upper bounds of [-40.0, 40.0] are really too high for most practical uses.

Figure 8.28. Example of

veloci tyMod.

8.11.2 gravi tyMod


The gravi tyMod attribute specifies a local (area inside p-zone) gravity multiplier. In other words, if gravityMod is -2 and the game gravity is set to 1.0, then when the player enters the p-zone, the player will float upward (Figure 8.29). If the player has enough forward velocity upon entering the p-zone, the player will end up "skipping" across the p-zone until the player falls off the end or encounters an obstacle. Be careful with 0 or negative gravity zones. If the player gets stuck with his feet off the ground, he will be unable to move. Again, high values can cause problems for the engine. Caution is the word.
Figure 8.29. Example of gravi tyMod.

333

Part III

Game Elements

8. 11.3 appliedForce
Finally, we have the appliedForce vector. This attribute allows you to create an area where an invisible force will be applied to "the character. This force can point in an arbitrary direction with a variable strength (Figure 8.30). Table 8.34 shows values and their effects on the character while on a flat surface.
Table 8.34
Effects of appliedForce values.

0-99
Practically no movement.

100-399
Sorta slides along.

400-1999
Forced walk

5000
Forced run

40000
can you say cannon?

Figure 8.30.
Example of

appliedForce.

8. 11.4 Maze Runner Lesson # 14 (90 Percent StepJ-Teleport Stopper


When the player runs into a teleport station, we'd like the avatar to be stopped. To do this, we can use a p-zone set up as follaws.
%P-zone = new PhysicalZone() position vectorAdd("l -1 0" , %Obj.getPosition() rotation = "1 0 0 0"; scale = "2 2 4"; velocityMod = "0"; gravityMod = "1"; app1iedForce = "0 0 0"; polyhedron "0 0 0 1 0 0 0 -1 0 0 0 1";
};

);

As can be seen, this code is meant to be script-driven; that is, we'll be substituting values in for posi tion when we drop the object into the world. The key things to notice are the following.
1. The position is offset by a vector (we haven't discussed vectorAdd ()

yet, but it adds two vectors and returns the result). The reason for this is that the polygon used to define the polyhedron field is offset. Its corner is at the origin, and therefore the cube is not centered. This can be corrected either by changing the polyhedron values or by offsetting while placing. I chose the latter. 2. ve10ci tyMod is set to zero. This means that shapes entering the p-zone should stop moving. That is pretty much all for now. Later (Lessontt15, "Teleport 1tiggers"), we'll use this code in our teleporter-building scripts.
334

Mission Objects

Chaprer 8

8.12 fxLight
This is another one of those fun mission objects provided by Melvin May. It is similar to the fxSunlight object, but instead of representing a sun, it is used to represent in-game lights. Unlike fxSunlight, this object casts light in the scene. It renders a representation of the light-source flare and casts light on terrain and other objects. One major difference between fxLight and fxSunlight is that the fxLight object requires a datablock.

8. 12. 1 fxlight New Features


fxLight has the following features.
Offset. fxLight objects can animate their position along a vector. This vec-

tor is relative to the fxLight's placement position. Radius. fxLight objects light the area around them in a sphere. The radius parameters control the size of this sphere. Size. fxLight objects, like fXSunlight objects, have a flare. However, because the distance of fxLights is near, versus the nearly infinite distance of fxSunlight objects, it is more realistic for their flare sizes to vary based on distance. The size fields enable this effect.

8. 12.2 fxlight Sample


The sample below can be used to produce a simple light that varies between dark purple and light purple over a three-second period. The light from this object will extend up to five world units from the center. To mark the location of the light center, the flare is enabled and uses the file "corona.png" found with the GPGT Lesson Kit (Figure 8.31).
datablock fxLightData(TestfxLightO) FlareOn = true; FlareBitmap = "-!data!GPGTBase!particletextures!corona"; LightRadius = ~5"; AnimColour = true; MinColour = "0.25 0.0 0.25"; MaxColour = "1.0 0.5 1.0"; ColourTime = 3.0; I;
Figure 8.31.
Flare texture "corona.png."

335

-------

Part III

Game Elements

8.13 Paths and Markers


Path mission objects are used to constrain the motion of objects, such as AI players, cameras, and shapes. They may contain a limited number of markers (more on this below). As their name would imply, markers mark a point on a path. Additionally, they supply some information that may or may not be used by objects that follow the path. _.......

8. 13. 1 Path Object


The Path object is a simple container, derived from the SimGroup class. The only new features added to Path, which are accessible from script, are the isLooping field and the getPathID () method.

isLooping
This field is an indicator used by PathCamera objects to determine if a path is a closed loop (isLooping = true) or open (isLooping = false). This affects the way the PathCamera's internal algorithms consume (follow) the path.

getPathID ()
In TGE, interiors (buildings, etc.) can be created with paths embedded in them. These special interiors (pathedInterior) need a means of tracking their paths. Thus, beyond having a unique sim ID, a path may have a path ID. Normal paths, those we put in the world, do not have this.
Limited Number Of Markers?

I noted above that there may be a limit on the number of markers a path can contain. This is not a limit imposed by the path but by the objects that use the path. Due to they way these objects transmit their data across the network (between server and client), paths containing 40 or more markers may cause issues-specifically an overrun in the number of bits a packet may contain. I only mention this here so those who are experimenting with pathedCameras, or the PathShape GG resource (qid = 4849) be aware that you may hit a snag using 40 or more markers in anyone path. Beyond this, you should feel free to use as many markers as you want.

8. 13.2 Marker Object


The Marker object is the little beastie that does most of the work defining how our paths will behave. The fields of a marker define the following .
position. Position of this marker.

336

Mission Objects

Chapter 8

Rotation of this marker. scale. Not used. seqNum. Sequence in path this marker represents. Valid sequence numbers are 0 .. NumMarkers - 1. type (Normal, Position Only, Kink) Normal. The object hits this point with both position and rotation. Position. The object only uses the position information during interpolation and retains its current rotation. Kink. This point in the path is discontinuous. msToNext. Time to next point in path.
rotation. SmoothingType

Figure 8.32.

Linear. Changes in path direction are abrupt (straight line). Spline. Changes in path direction are smooth (curved). Position of next "target" on path. This is determined by the position of the marker, and its seqNum (sequence number).
seqNum.

These values must start at 0 and continue to NumMarkers -

1.

Wow! All that sounds pretty techy and cool. Unfortunately, most of this information is just a hint. The camera is the only object that cares about all those parameters. If you want to have an AI character care about how smooth a path is, you'll have to write the appropriate scripts and examine the contents of these fields yourself. For most simplistic pathing purposes (an AI player following a path, or a shape following a path), a SimGroup of markers will be sufficient. You need not specify the remaining information, unless you actually care to use it in your scripts.

Figure 8.33.

~.,,

Figure 8.34.

Proper Creation of Path


We're not going to be doing any work with paths in this book since paths are mainly used for camera pathing and AI pathing, which we don't discuss here. However, we'll talk briefly about placing a path. Placing a path might seem a bit confusing at first, but just follow these simple steps. 1. Add a Path object. When you add a path object, it will show up in the Inspector tree, but not in the world. Don't worry; it's just a container (Figure 8.32). 2. Select our new path marker as the instant group (Figure 8.33). 3. Add as many path markers as you need (Figure 8.34).
337

Part III

Game Elements

Figure 8.35.
Path with four nodes.

Spline Smoothing

Linear Smoothing

In Figure 8.35, I added four nodes. The first image shows the path with all nodes using the spline smoothing type. The second shows the same path using linear smoothing type. Notice (in the second image) how the turn points are sharp and the lines between nodes are straight.

8.14 Triggers
TGE Trigger objects are rectangular regions of space that react to the presence of other objects within that space. In versions prior to 1.4, only players and vehicles tripped a trigger. Now, items do also. Triggers track three basic events: Enter. Something entered the trigger. Exit. Something exited the trigger. Inside. Something is inside the trigger region.

8. 14. 1 Placing a Trigger


Figure 8.36.
Object builder dialog.

338

To place a trigger, we simply open the mission we want to contain this trigger, move to the right location, and start the Creator. Then, find the trigger object under Mission Objects -7 Mission -7 Trigger and select it. The object builder dialog will pop up and offer us some choices (Figure 8.36).

I~.

Mission Objects

Chap[er 8

I usually just give it a name and press OK, but if you are using a custom datablock or if you wish to specify the Polyhedron dimensions numerically, this is your opportunity. I will normally resize, rotate, and translate the trigger manually.

8. 14.2 Trigger Scripting


As noted above, the behavior of triggers is controlled by scripts. The triggers themselves require a datablock to be built, and subsequently, if we want to interact with them, we must provide callbacks.

Trigger Datablocks
The trigger datablock specifies exactly one field: tickPeriodMS. This parameter tells the trigger how many times to wake up and check for objects inside the trigger region.
datablock TriggerData( defaultTrigger ) { tickPeriodMS = 100; II Wake up -ten times per second
};

Trigger Callbacks
Once we've created our datablock, we need to specify what the trigger does when triggered. As we discussed above, a trigger has three basic triggering actions. Enter. Something entered the trigger. When this happens, the trigger's onEnterTrigger () callback is called.
function TriggerData::onEnterTrigger( %TriggerDB %Trigger I %EnterObj ) { II Do something
);
I

Exit. Something exited the trigger. When this happens, the trigger's onLeaveTrigger () callback is called
function TriggerData: :onLeaveTrigger( %TriggerDB %Trigger I %LeaveObj ) { II Do something
};
I

Inside. Something is inside the trigger region. Every tickPeriodMS, the trigger will wake up and check to see if there is something in this region. If something is inside the trigger, the onTickTrigger () callback is called.

339

Part III

Game Elements

function TriggerData: :onTickTrigger( %TriggerDB , %Trigger , %InsideObj ) { II Do something


};

Group Triggers
Group triggering is 'a method whereby we associate objects with a trigger and these objects are triggered when the trigger is activated (triggered). For this to work, two conditions must be satisfied.

Figure 8.37.

Trigger and shapes stored In same SimGroup.

.v

1. Any objects to be group triggered and the trigger that triggers them must be contained within the same SimGroup/SimSet; i.e., if we looked at the Inspector, our object tree would look something like Figure 8.37. 2. Each object that is to be triggered must specify an onTrigger () and/or an onTriggerTick () callback.
function ShapeBaseData: :onTrigger( %DB , %Obj , %TriggerState ) ( II Do something
);

1955, . St.tll:Shi

function ShapeBaseData:: onTriggerTick ( %DB , %Obj ) { II Do something


};

Now, each object in the SimGroup/SimSet will get an onTrigger () event when a player or vehicle enters or exits the trigger and an onTriggerTick () event every tickPeriodMS while a player or vehicle is inside the trigger.

B.14.3 Maze Runner Lesson # 15 (90 Percent

StepJ-Teleport Triggers

In this lesson, we will examine the scripts needed to teleport the player from one teleport station to another. We will also look at the code that combines the prior parts of the teleport trigger components.

Trigger Datablocks
We could in theory use the DefaultTrigger datablock that comes with the kit, but it would be better to define a new one so we can guarantee that we have a unique namespace with which to scope our methods and callbacks. So, we will define our datablock as follows.
datablock TriggerData(TeleportTrigger) tickPeriodMS = 100;
};

340

Mission Objects

Chapter B

Teleport Scripts
Later, when we are writing our level-building scripts (Lesson 1117 (Section 9.5.10)), it will be nice jf we already have a method for attaching the particle effects and the physical zone to our teleporter triggers. With a little preplanning, this won't be that hard to do.

Teleport Trigger Planning


We haven't discussed it much yet, but the user (and we) will be able to define new levels by creating simple text files. These files will have "maps" of the level in them made up of various numbers and letters, representing the positions of level pieces like blocks, coins, and teleporters. Knowing that our teleporters will be associated with letters in this file, we can plan on the teleporter trigger being a sort of parent. We will read the level file, create a trigger where it tells us to, and store information in the trigger that tells the trigger which of the three types of teleporters it is. Recall (assuming you jumped ahead to Chapter 14) that there are three types of teleporters (all of them function the same, but this allows us to have distinct sets that are connected to each other). So. let's just assume that the letters used to represent teleporters are going to be x, y, and z. Furthermore, let's assume that the trigger is created first and then the type is stored in a field named type. Lastly, we will assume that the level loader will then call our teleport-builder script to add a particle emitter node and a p-zone in the same position as the trigger.

Teleport Trigger Implementation


All of that planning and assuming gets us some code like the following.
function Trigger::AttachEffect( %Obj ) echo("\c5 Added Teleport Trigger"); %emitter[X] %emitter[Y] %emitter[Z] "TeleportStation_PEDO"; "TeleportStation_PEDl"; "TeleportStation PED2";

%effect = new ParticleEmi tterNode () ( position = vectorSub(%Obj.getWorldBoxCenter(), "002"); rotation = "1 0 0 0"; scale = "1 1 1"; dataBlock = "basePEND"; emitter = %emitter[%Obj.type]; Ilemitter = "TeleportStation_PEDO"; velocity = "1";
};

341

- - - - ..- - - - - - - - - -

Part ,1/

Game Elements

%Obj.myEffect

%effect;

%Pzone = new PhysicalZone() position = vectorAdd( "1 -IOu, rotation = "1 0 0 OU; scale = "2 2 4 u ; velocityMod = "OU; gravityMod = "l appliedForce = "0 0 OU; polyhedron = "0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
U ;

%Obj.getPosition()

);

0.0000000 1.0000000 0.0000000 -1.0000000 0.0000000 1.0000000 u ;

};

%Obj.myPzone

%Pzone;

Basically, this function creates an array of particle emitter datablock names indexed by x, y, and z. Then, it creates a particle emitter in the position of the trigger (ID passed as argument to this function) and looks at the stored type to dereference the datablock array, getting the correct datablock name to match the type. Next, the function creates a p-zone (remembering to offset it a little) in the same position as the trigger. After this function is finished executing, there will be a trigger, a particle emitter node, and a p-zone all in the same location. VoiliI! A teleporter station. So, how do we make the teleporter "go"? Let's do that next.
Trigger Callbacks

To make the teleporters do work for us, we need to implement the onEnterTrigger () and onLeaveTrigger () callbacks. Instead of showing you the code (which you can just load and examine), I will present the methodology used to teleport correctly.
...

onEnterTrigger() This callback has the lion's share of the work. Initially, all triggers will start off active. When the avatar runs into a teleporter trigger, that trigger executes the following steps.
1. Check to see if it is disabled. If so, the callback aborts.

2. Enable the p-zone it owns (to be sure the avatar gets stopped). 3. Check for the existence of other triggers. When we create triggers in our level-building scripts, all triggers are added to one of three trigger groups
342

Mission Objects

Chapter 8

based on their type. Then, if we recall that all SimObject children can determine what group (if any) they are stored in, it will become clear that each trigger can get the group it belongs in and choose a trigger from that group until it finds one that is not itself. 4. Disable the p-zone on the target trigger (so player is not stopped on exiting that trigger).

5. Disable the target trigger (to prevent releport loops).


6. Do some fading effects and schedule a setTransform () call, moving the avatar to the location of the target trigger. Once the avatar arrives at the target trigger, it has to leave that trigger to reactivate it. Also note that the setTransform () call moves the avatar and causes the current trigger to call its own onLeaveTt"igger () callback, thus reactivating it. onLeaveTrigger () This callback has very little to do. Basically, when the avatar leaves the trigger area, the trigger will be told to re-enable the trigger and to reacti vate () the p-zone.

Tricky Bits
While examining the scripts, you may notice a couple of bits of code that we have not yet discussed. The first of these is a sall to getRandomObject (). It is being called on a SimGroup. This is a method I have provided in the included "systems" script files (loaded when we set up our environment). This method simply iterates over a SimSet (or child) and randomly selects an entry from the set, returning the ID of the selection .
... %Trigger.getGroup() .getRandomObject()

The second bit involves the use of the function getwords ( ) . In this line of code, we're replacing the position part of the player's transform with a new position while retaining the player's orientation information. This is done by getting the "words" representing the orientation. As you will learn later, a word is any string, and words are separated by spaces. Thus, we can look at the transform as a string containing seven words. Using getwords () , we simply get the top four words and then paste them onto a new position matrix, making a new transform.
%newTransform

%newPos SPC getWords( %oldTransform,'3 , 6);

343

Parr III

Game Elements

Trigger Cleanup
It is also worth mentioning that, when the trigger is destroyed, it will call its
onRemove () callback, which will delete the effects attached to this trigger.

Nice and clean.


function TeleportTrigger:: onRemove ( %DB, %Obj ) if( isObject( %Obj.myEffect ) ) %Obj.myEffect.delete(); if( isObject( %Obj .myP-zone ) ) %Obj.myPzone.delete(); (

8 .. 15 Mission Objects Summary


In this mega-chapter, we have completely examined 14 major missionplaceable objects. Terrains. We learned about how the terrain is a 2 kiloworld unit x 2 kiloworld unit square that tiles infinitely in the world plane. Water Blocks. We learned how to represent liquid bodies using this object and how to interpret the myriad features and special effects it provides. Sky. We learned how this object is responsible for the sky box, douds, general fog, up to three fog bands, and general visible distance limits. Sun. We learned that this object controls the scene lighting. We learned to control the source of the scene lighting through azimuth and elevation and the coloration and intensity of scene lighting through the color and ambient fields. Precipitation. We learned to create a "rain box" that can be used for a whole lot more than just rain. Lightning. We learned about both generated lightning and textured lightning. Audio Emitters. This meaty topic took a while to cover, but we came to understand that the versatile audio emitter object can be placed in the scene to produce 2D and 3D sounds. We learned all about general gain equations, looping and loop gaps, the 3D sounds zones (inner cone, outer cone, and outside volume), 3D gain distances (reference distance and max distance), and how to interpret the visual feedback this object provides while in debug mode. Particle Emitters. Following one meaty topic with another, we jumped into a discussion of the particle system and learned to differentiate a ParticleEmitterNodeData (PEND) from a ParticleEmitterData (PED) from a ParticleData (PD), as well as about the features each of these classes provide.

344

Mission Objects

Chapter 8

fxShapeReplicators and fxFoliageReplicators. We had fun discussing placement rules, the concepts of seeds and counts, how to select restrictions and restraints, how to enable or disable shape interactions, and how to set up the best culling for our needs. fxSunJight. We learned that this object provides the ability to render celestial bodies and supports a humongous set of features. Physical Zones. We saw that a lot' of fun can be had combining the veloci tyMod, gravi tyMod, and appliedForce fields of these objects. fxLight. We covered this dynamic light object briefly. Paths and Markers. Here we examined how to create proper paths with these two classes and what their individual features mean and do. Triggers. Lastly, we discussed one of the most important mission objects, the trigger, which enables a wide variety of interactIons with its individual and group triggering features.

345

---------- - -

---------

Chapter 9 Game Setup Scripting


There are several scripting tasks that we will deal with in just about every game we make. This chapter gives an overview of the tasks related to setting up and maintaining a game. It will familiarize you with the fundamental Sim scripting classes and then examine I/O scripting. The following specific topics are covered in this chapter.

SimSet and SimGroup. These are two container classes acting as base classes to the GuiControls and to the ScriptObject and ScriptGroup classes, respectively. ScriptObject and ScriptGroup. These two classes are used to create scripted classes. These special classes give us the ability to associate fields and methods with scripted classes, thus allowing us to neatly compartmentalize our scripts. Device Inputs and Action Maps. We will discuss how to build and use action maps to capture and redirect device inputs. File I/O. Here we will review the use of the file I/O classes. Compiling and Executing Files. At some time, we'll want to compile and execute scripts from files, so we'll talk about this briefly.

9.1 SimSet
SimSet is the root class in a hierarchy of SimObject containers. It is responsible for providing the base functionality and structure for all subsequent SimObject containers. It (and its children) should be treated as a traditional queue. A SimSet is designed to hold a list of handles to SimObjects (or children of SimObjects). The SimObject is a fundamental class upon which all other objects that we will deal with are based. Anyone SimSet may contain only one instance of a handle to an existing SimObject, but a SimObject may be tracked by any number of SimSets; that is, no matter how many times we add () a handle to a SimSet, it is only stored there once, but we can add the handle to as many SimSets as we like.
/ /ts04 (a) ;

Remember, when you

see a code snippet


with a statement like: \ \ts04 (a);, this means you can run the GPGT Lesson Kit. start either of the included missions, and then in the console ( - J you may type: \ \ t s 0 4 (a) ; to run the sample.

%SO = new SimObject(); %SetO new SimSet(); %Setl = new SimSet();

347

Part III

Game Elements

%SetO.add( %SetO.add( echo( "Set %Set1.add( %Setl.add( echo( "Set

%50); %50); 0 contains ", %SetO.getCount() %50 ); %SetO ); 1 contains ", %Set1.getCount()

"

objects." );

"

objects." );

The above code produces the following output.


Set 0 contains 1 objects. Set 1 contains 2 objects.

SimSet containers do not assume responsibility for their contents; that is, if we delete a SimSet, the handles "and therefore the objects they represent are not deleted. However, when an object is deleted, it is automatically removed from all SimSets.
//ts04(b); %Setl.delete(); II Self delete echo( "Set a contains ", %SetO.getCount() Setl is now deleted, but

"

objects." );

so and Seta still exist, so the above code produces

the following output.


Set a contains 1 objects.

Because SimSets behave like queues, they have a front and a back. Objects added to a queue are added to the back of the queue. Furthermore. the front of the queue is index O. and the back of the queue is index ge tCoun t () - 1.
//ts04(c); %S1 = new SimObject(); %52 = new SimObject(); %Set2 = new SimSet(); %Set2.add( %S1 ); %Set2.add( %S2 ); echo( "The 10 of Sl is: ", %Sl.getIO() ); echo( "The 10 of 52 is: ", %S2.getIDO ); echo( "Object at front of Set 2 is ", %Set2.getObject(O) ); echo( "Object at back of Set 2 is ", %Set2.getObject(1) );

The above code produces the following output "(IDs may vary).
348

Game Setup Scripting

Chapter 9

The 10 The 10 Object Object

of of at at

Sl is: 2391 S2 is: 2392 front of Set 2 is 2391 back of Set 2 is 2392

We can manipulate the position of objects directly as long as we have a handle to the SimSet and the object.
//ts04(d)i echo( "The 1D of Sl is: %Sl.get1D() }i echo( "The 10 of S2 is: %S2.get1D(1 Ii %Set2.bringToFront( %S2 ); echo ( "Object at front of Set 2 is If, %Set2.getObject(O) }i echo( "Object at back of Set 2 is If, %Set2~getObject(l} Ii %Set2.pushToBack( %S2 ); echo( "Object at front of Set 2 is If, %Set2.getObject(O} Ii echo( "Object at back of Set 2 is %Set2.getObject(11 };
If, If, If,

The above code produces the following output (IDs may vary).
The 1D The 1D Object Object Object Object of of at at at at Sl is: 2409 S2 is: 2410 front of Set 2 is 2410 back of Set 2 is 2409 front of Set 2 is 2409 back of Set 2 is 2410

We can remove objects from our list at any time. This does not delete them.
/ /ts04 (el i %SetO.remove( %SO }i II Take %SO our of SimSet 1 echo( "Set 0 contains If, %SetO.getCount() , " objects.

1f )

The above code produces the follOWing output.


Set 0 contains

objects.

We can also empty a SimSet in one fell swoop.


//ts04(f)i echo( "Set 2 contains

If

%Set2.getCount(1

, " objects.

1f

}i

349

Part III

Game Elements

%Set2.clear(); II Remove all objects from SimSet 2 echo( "Set 2 contains ", %Set2.getCount() , " objects." );

The above code produces the following output.


Set 2 contains 2 objects. Set 2 contains 0 objects.

Lastly, for debug purposes, a function is provided to dump the contents of a SimSet to the console:
//ts04(q); %S3 = new SimObject(); %S4 = new SimObject(); echo( "The ID of S3 is: " echo( "The ID of S4 is: " %Set3 = new SimSet(); %Set3.add( %S3 ); %Set3.add( %S4 ); %Set3.1istObjects();

%S3.getID() %S4.getIO()

); );

The above code produces this output (object IDs may be different):
The 10 of S3 is: 2418 The 10 of S4 is: 2419 2418: SimObject 2419: SimObject

A SimSet cannot hold a reference to itself. The reason for that is explained in the next section.

9.2 SimGroup
A SimGroup is similar to a SimSet with a few exceptions. Anyone SimObject may only be tracked in one SimGroup at a time. It can simultaneously be in any number of SimSets, but if we add a SimObject to a SimGroup when it is already present in another SimGroup, the reference to the SimObject will be removed from the prior SimGroup automatically,
//ts05(a) ; %SO = new SimObject(); %GroupO = new SimGroup(); %Group1 = new SimGroup(); %SetO = new SimSet();

350

Game Setup Scripting

Chapter 9

%SetO . add ( %SO); %GroupO.add( %SO); echo ( "Set 0 contains ", %SetO.getCount() , " echo( "Group 0 contains ", %GroupO.getCount() echo( "Group 1 contains ", %Groupl.getCount() %Groupl.add( %SO ); echo I "Set 0 contains ", %SetO.getCount() , " echo I "Group 0 contains ", %GroupO.getCount() echo I "Group 1 contains ", %Groupl.getCount()

objects." ); "objects."); , " objects." ); objects." ); " objects." ); , " objects." );

so can only be in one SimGroup at a time, but it can be in both a SimSet and a SimGroup at the same time, as the following output shows.
Set 0 Group Group Set 0 Group Group contains 1 0 contains 1 contains contains 1 0 contains 1 contains objects. 1 objects. 0 objects. objects. 0 objects. 1 objects.

Second, and of great importance, if we delete a SimGroup, this causes the automatic deletion of all objects in the SimGroup. This is the reason SimSets may not reference themselves (SimGroup is a child of SimSet).
//ts05(b) ; echo( "Set 0 contains ", %SetO.getCount() , " objects." ); %Groupl.deletel) ; II Self deletes, and automatically deletes %SO echol "Set 0 contains ", %SetO.getCount() "objects.");

By deleting Groupl, which contained so, we have also deleted so, thus removing it from SetO, as can be shown by the following output.
, Set 0 contains 1 objects. Set 0 contains 0 objects.

9.3 ScriptObjects and ScriptGroups


When I first ran across these two classes, I was a bit puzzled and didn't see the value of having a class dedicated to scripting. I mean, hey, we have variables and functions. We've got packages. What else do we need? Then, little by little, I experimented and soon found that these two classes are practically indispensable.
351

--

---------

Part II/

Game Elements

9.3.1 ScriptObject
The ScriptObject is a noncontainer class provided to allow the creation of TorqueScript-only classes. It is derived from the SimObject (not SimSet) class. This class provides the ability to group data fields and to associate the class with one or more namespaces. The general syntax of a ScriptObject is as follows.
II In TorqueScript %handle = new ScriptObject( [ Name] [class = AClassName;] [superClass = AnotherClassName;]
[dynamic fieldO [dynamic fieldN
};

) {

InitialValueO;] InitialValueN; ]

This syntax is simpler than it looks. Let's break it down in Table 9.1.
Table 9.1
ScriptObject syntax.

SyntIIx Element
%handle Name (optional) class (optional)

oe.atptlon
The variable where the object's handle will be stored. Any expression evaluating to a string, which will be used as the object's name. A special field that tells the Torque engine to insert AClassName into the namespace calling sequence for this object between Name and ScriptObj ect. A special field that tells the Torque engine to insert AnotherClassName into the namespace calling sequence for this object between Name and Scr iptObj ect.

superClass (optional)

dynamic fieldN (optional)

As with any other object created in script, you may add as many dynamic fields as you wish.

Note that, if you use both class and superClass, the object's calling sequence will be the following. Name 7 AClassName 7 AnotherClassName 7 ScriptObject 7 SimObject

9.3.2 ScriptGroup
The ScriptGroup class is a container class that provides all the same features as a ScriptObject with one minor difference-it is derived from SimGroup instead of SimObject. Thus, objects created from this class have all the behaviors of a ScriptObject while also having the behaviors of a SimGroup container.

352

Game Setup Scripting

Chapter 9

The namespace chain for this object looks like the following. Name -7 AClassName -7 AnotherClassName -7 ScriptGroup -7 SimGroup -7 SimObject

It's an Object
Instances of ScriptObject and ScriptGroup classes are objects. This means they can have fields associated with them.
%obj = new ScriptObject ( Square ) { width = 10.0; height = 5.0;
};

Now, we can write little functions to manipulate them.


/ /ts06 () ; function printAreaOfSquare ( %Square ) echo("The area of this square is: ", %Square.width * %Square.height);

To run this, we would have to type printAreaOfSquare ( %obj producing:


The area of this square is: 50

);,

They Support Namespaces


In truth, it wouldn't be great if we had to write a named function for each case we wanted to handle; better would be to use namespaces and overload a single method name. Because ScriptObjects and ScriptGroups support namespaces, we can do the following.
/ /ts07 () ; %objO = new ScriptObject( Square) width = 10.0; height = 5.0;
};

%obj 1 = new ScriptObj ect ( Circle ) { radius = 10.0;


};

353

Peinlll

Game Elements

function Square: :printArea( %this ) echol"The area of this square is: ", %this.width * %this.height); function Circle: :printArea( %this ) echo ("The area of this circle is: " %this.radius * %this.radius * 3.1415927 );
)

Square.printArea() ; %objl.printArea(); The above code would give us the following output. The area of this square is: 50 The area of this circle is: 314.16 This is better, but now it seems we have to name all of our circles "Circle" if we want this to work. That kind of kills the ability to use names in addition to IDs to reference objects. Fortunately, there are two key words that we can use to add generic namespaces to the objects we create-class and superClass.
/ /ts08 () ;

%obj = new ScriptObject( SquareD) { class = "Square"; width = 10.0; height = 5.0;
);

%obj = new ScriptObject( Square1 ) { class = "Square"; width = 10.0; height = 50.0;
) ;

SquareO.printArea() ; Squarel.printArea() ; The above code would give us the following output. The area of this square is: 50 The area of this square is: SOD
354

Game Setup Scripting

Chaprer 9

So, class seems pretty useful, but what is this superClass business? It allows us to use yet another class name in the chain, below the one added by the class keyword,
/ /ts09 () ;

function Doberman:: pr intMessage' ( %this ) ( echo("A H, %this.getName(), " is a ... H); Parent: :printMesage( %this ):

function Canine: :printMessage( %this ) echo(" ... H, 'this.class, " which is a .. . H): Parent: :printMesage( %this ):

function Animal::printMessage( %this ) echo(" ... H, %this.superClass, ".H):

%obj = new ScriptObject( Doberman) class = "Canine H : superClass = "Animal";


);

%obj.printMessage():

The code above produces the following interesting output.


A Doberman is a ... Canine which is a ... . .. Animal.

Callbacks, Too? ScriptObjects and ScriptGroups support the : : onAdd () and : : on Remove () callbacks, This means that we can have them do initialization and cleanup work when we create/delete them, just like when we create other mission objects. If this is not very clear, please continue reading. In Section 10.4, [ will give an overview of the callback concept and discuss a few important standard callbacks.
355

Part III

Game Elements

Not Networked

Just like dynamic fields, neither ScriptObject nor ScriptGroup are networked; that is. instances of these classes created by the server will be visible only to the server (except for the single-player and listen-server cases (see Section 2.1.15), where the client is local). If you want information shared with the clients from either of these classes, you will have to write networking scripts to do so.

9.4 Device Inputs and Action Maps


When we speak of inputs in the context of TCE, we are talking about user inputs from keyboards. mice, joysticks, and other devices. Although other types of inputs are possible, the only ones we are interested in are those that are used to control gameplay. That said, inputs flow into and through TCE as follows (Figure 9.1). The OS processes inputs and passes them to the TCE platform layer. 2. The TCE Platform identifies and categorizes the inputs and passes them on to the game. 3. The game processes the input if it can or ignores it if there is no defined action for the input.
1.

The game input processing is the part we are interested in. As can be seen from Figure 9.1, the input is processed within the game as follows. 1. The ClobalActionMap gets first dibs on the inputs. If it has no mapping for the input, the input is passed on to the GUI, more specifically the Canvas. 2. The Canvas attempts to process the input, but passes the input on if it has no GUI controls programmed to use it. 3. The input is passed to any active action maps for processing. If none of the currently stacked action maps is coded to use the input, the input is dropped.

"'
Figure

9.'
TOE
PIat10rm la.yer
Mouse

Flow of inputs for TGE.

356

Game Setup Scripting

Chapter 9

ActionMap is a special class designed to capture and redirect inputs. There are two kinds of ActionMap: the GlobalActionMap and the normal ActionMap. The main differences between these are as follows.
GlobalActionMap. This is the daddy of input processors and supersedes all other processing methods. This action map should not be popped from the processing stack. ActionMap. This is a generic action map. It takes lower priority than all other processing methods. These action maps can be pushed and popped from the processing stack as the game's requirements change.

9.4. 1 Defining Action Maps


To create a new (blank) action map, we use the folloWing syntax (explained in Table 9.2).
new ActionMap( ActionMapName );

Table 9.2.
new

A keyword instructing TGE to create a new instance of the following console class. Returns a handle to the new ActionMap. Console class name for action maps. The name for the new action map.

Creating a new action map.

ActionMap ActionMapName

Binding Inputs to Actions


Subsequent to creating a blank action map, we must bind inputs to actions (or responses). This binding associates a specific input with a specific function or scripted response. Th add new mappings, use one of the following two functions .
ActionMapName.bind( device, action, [ modifier spec

...

] , command );

This first binding method, bind, is used to bind a single command to an action (Table 9.3). It has the further ability to modify the behavior of pointing devices via special modifiers. The command (a function) bound to this action will be automatically passed a value (as the first and only argument to the function) corresponding to whether the device is in the on or off (pressed or released) state. A function that is used for binding should have the following form.
357

Part III

Game Elements

II Assume this is bound to a mouse button


function aBindFunction( %val ) ( if( %val) echo("Mouse button was pressed.");
}

else ( echo ("Mouse button was released.");

The second

bin~ing

function is the following.

ActionMapName.bindCmd( device, action, makeCmd, breakCmd );

This second binding method, bindCmd, will bind one function to the on (break) event and one function to the off (break) event (Table 9.4). Both functions are optional, but at least one should be specified. A function used for this kind of binding takes no arguments at all. Tables 9.5-9.7 are provided for your reference and describe the most commonly used devices, actions, and modifiers. For a full listing, please see the appendices.
Table 9.3.

Adding new mappings using bind.

ActionMapName bind device action modifier spec command

Previously defined action map. Console method used to add a new action for the specified key. Device name (see Table 9.5). Device action (see Table 9.6). Special modifiers (see Table 9.7). Command to execute when this binding triggers.

.. Table 9.4.

Syntax Element
ActionMapName bindCmd device action makeCmd breakCmd
Previously defined action map Console method used to add a new action for the specified key. Device name (see Table 9.5). Device action (see Table 9.6). Command to execute on key press. Command to execute on key release.

Adding new mappings using bindCmd.

358

l_

Game Setup Scripting

Chapter 9

Deiu.....
keyboardN
This is the Mh keyboard hooked up to the system. For the first keyboard, either keyboard or keyboardO is acceptable. This is the Nth mouse hooked up to the system. For the first mouse, either mouse or mouseO is acceptable. This is the Mh joystick or gamepad hooked up to the system. This is the Mh unknown device hooked up to the system. In other words, some device has been sampled, but TGE doesn't know what it is.

Table 9.5.
Devices.

mouseN

j oystickN

unknownN

Table 9.6.
buttonO, buttonl, button31 a ..
Z

This is a mouse, joystick, or gamepad button press. For the mouse, buttons 0, 1, and 2 are left, right, and middle buttons, respectively. See the appendix for other button mappings. These are keyboard inputs. Because this list is so long and in order to accommodate possible variances for special keyboards and other devices, a GUI has been proVided with the GPGT Lesson Kit that displays the current action, be it keyboard, mouse, joystick/gamepad, or other device. Simply start the GPGT Lesson Kit and click GUIsSampler -7 guiInputCtrl. Follow the instructions provided in the sample. These are modifiers and are not used standalone, but they are included in the action string; for example, shi ft p is the SHIFT key and the P key pressed at the same time. These are special modifier actions.

Actions.

A .. Z

0 .. 9 Fl..Fl2 etc.

shift ctrl alt lshift, rshift, lctrl, rctrl, lalt, ralt

.
D %x %y

Table 9.7.
Has dead zone. This is used to add a dead zone for the mouse. Motions in this zone will not be recorded. This can be used to remove the jitter caused by a "nervous hand." Has scale. This is used to scale the mouse motion (by a multiple). Inverted. This is used to invert the mouse. Has scale. Same as S. Special modifiers used to modify mouse inputs.

S %5
I

R %5

359

PanJlI

Game Elements

Bind Samples
Before going any further, let's look at a few binding examples and break them down. moveMap.bind(keyboard
f

"alt

CU

toggleCamera);

In the above binding, we have bound the toggleCamera () function to the alt c event. As soon as the ALT and C keys are pressed (together), toggleCamera () will be executed on that client. When the toggleCamera () method is called, the engine will pass a 1 or a 0 as the first argument to the function. A 1 represents a make (key-press) event, while the 0 represents a break (key-release) event. So, remember that the function will be called twice per key press, not once. moveMap.bindCrndlkeyboard , "n , "NetGraph::toggleNetGraph();U, "U); In the above binding, we have bound theNetGraph: : toggleNetGraph () method to the n make event. As soon as the N key is depressed, NetGraph: : toggleNetGraph () will be executed on that client. Nothing is scheduled to occur on the key-break (release) event. moveMap.bindCmd(keyboard , "n "NetGraph: :toggleNetGraph();U); In the above binding, we have bound the NetGraph:: toggleNetGraph () method to the n break event. As soon as the N key is released, NetGraph: : toggleNetGraph () will be executed on that client. Nothing is scheduled to occur on the make (key-press) event.

Multiple Binds It should be noted that binding more than one key to the same action (using the same action map) is not allowed. TGE will do its best to preempt this kind of assignment. Normally, if one attempts to bind two inputs to the same response, the second binding will silently fail. However, this behavior is not consistent. So, it is best to be aware of this and to check your action maps for duplicate assignments. To be clear, the following is a multiple bind.
moveMap.bindCmd(keyboard , "n "NetGraph: :toggleNetGraph();U); moveMap.bindCmd(keyboard , "rn U , "d , "NetGraph::toggleNetGraph();U);
I "U I

360

-----

------

Game Setup Scripting

Chapter 9

This will only bind the toggleNetGraph function to the N key. Overriding Binds Overriding binds is a different story. You may override a bind at any time you wish. Simply specify the bind with a new set of functions.
moveMap.bindCmd(keyboard , "n" "" , "NetGraph: :toggleNetGraph();"); moveMap.bindCmd(keyboard, "n" , "" , "nukeEM();");

The above example rebinds the N key. It will now call the function nukeEm () on a break (key-release) event. Unbinding There will be cases where we want to undo a binding. To do this, we use the following syntax (explained in Table 9.8).
ActionMapName.unbind( device, action); moveMap.unbind( keyboard, "n" );

Saving Binds

We will also find it useful to save our binds on occasion. The ActionMap class provides this ability as follows (Table 9.9).
ActionMapName. save ( [ filename ] , [ append ] ); moveMap.save( "-/client/myActionmaps/movemap.cs" , false );
DelcrlpIIon
ActionMapName unbind device action
Previously defined action map. Console method used to remove an action from an action map. Device name (see Table 9.5), Device action (see Table 9.6),

.. Jrr;l;' "

Table 9.8. Undoing a binding.

DeIcrIption
ActionMapName save filename (optional) append
(optional) Previously defined action map.

. ~:.~t:': '.f

:::;

Table 9.9. Saving binds.

Console method used to save/dump an action from an action map. A valid filename to dump the action map to. If no filename is specified, the action map is dumped to the console. A Boolean value specifying whether to append to the file or overwrite it. The default (false) is to overwrite.

361

Part III

Game Elements

Table 9.10. Activating a nonglobal action map.


ActionMapName
push

Previously defined and bound action map. Console method used to activate the action map and place it on the top of the nonglobal action map stack.

Table 9.11. Deactivating a nonglobal action map.


ActionMapName pop
Previously defined, bound, and activated action map. Console method used to deactivate an action map and remove it from nonglobal action map stack.

Activating Action Maps Once an action map has been created and the bindings have been assigned, it must be activated in order to be used. To activate a nonglobal action map, we use the following syntax (Table 9.10).
ActionMapName.push() ;

Subsequently, an active nonglobal action map can be deactivated using the following syntax (explained in Table 9.11).
ActionMapName.pop();

Please note that popping only removes the specified action map from the nonglobal action map stack. All other action maps stay in place. Deactivating the GlobalActionMap
It should also be noted that the GlobalActionMap does support both the push () and the pop () console methods. However, it is not suggested that

..

you use these methods on the GlobalActionMap. That said, you may use the pop () console method to temporarily disable the GlobalActionMap and the push () console method to reactivate it at a later time. Nonglobal Action Map Stack We have alluded to the concept of stacking but have not clearly stated what this means in the context of action maps. The concept of stacking only applies to normal action maps as no stacking order will allow them to take precedence over either the GlobalActionMap or the Canvas. An action map is placed on the top of a virtual stack of
362

Game Setup Scripting

Chapter 9

nonglobal action maps when it is activated (pushed). Action maps higher in the nonglobal action map stack will be first to process any inputs which have made it past the Canvas. Therefore, if an action map redefines a binding defined by an action map lower in the stack, the binding of the higher action map will take precedence. Stacking action maps is a nice way of compositing action bindings based on current context.

9.4.2 Maze Runner Lesson # 16 (90 Percent StepJMoveMap


In this short lesson, we will examine the action map that is included with the prototype content and discuss some small changes to it and other scripts that will ensure the behavior we are expecting from our game.

Required Behavior
In our game, we want the following key mappings. W A S D Move forward. Move left. Move backward. Move right. SPACEBAR Jump. Mouse Move Camera yaw and pitch. TAB Switch 1st and 3rd POY.

We would specifically like to disable (eventually) the following key mapping.


ALT + C

Free camera mode.

We don't want people using free-camera mode to cheat and find coins without risking their avatar's life.

The MoveMap defined in the prototype content used for MazeRunner is standard and matches the one prOVided with all TGE samples. including the free Demo and the FPS starter kit that comes with the full SDK.

MoveMap
The prototype content we copied into our "\MazeRunner" directory contains two files that define an action map already implementing the above mappings as well as many others. The name of this action map is MoveMap. One of the things that new users find confusing is the fact that MoveMap is created in two places. It is created in the file "\MazeRunner\prototype\client\scripts\defauILbind.cs" and in the file "\MazeRunner\prototype\client\ config.cs ". So, where do we go if we want to modify this action map? Well, if we look in the function initClient () in the file "\MazeRunner\prototype\ client\init.cs", we will see the following code.
363

Part /II

Game Elements

II Default player key bindings exec("./scripts/default.bind.cs"); exec("./config.cs");

The first file, "default.bind.cs", is the correct location to define new bindings for the moveMap action map. However, if you do decide to modify or add bindings, be sure to delete the "config.cs" file first. Otherwise, it will wipe out the changes you made in "default.bind.cs". The "config.cs" file is automatically stored by the scripts that come with the prototype and is meant to reflect any changes we might make using the options dialog. However, adding new bindings and/or functions needs to be done by hand, so remember to always stop the engine, delete the "config.cs" file, and (only) then add your new bindings to "default.bind.cs".
Making Our Changes

In our game, we are happy with the current mappings, except that we wish to eventually disable free-camera mode. So, when we want to do this, simply remove the following line from "default.bind.cs".
moveMap.bind(keyboard, "alt c", toggleCamera);

For now, I suggest leaving this in, but when we get ready to release our game to the public, this line needs to be removed. Additionally, we might want to remove the following code from "default.bind.cs".
function toggleCamera(%val) { i f (%val) commandToServer('ToggleCamera');
If you are having trouble finding newly added files, you may at any time do the following to refresh the file manager.

9.5 File I/O


Torque has a file manager that maintains a working list of all the files found in the game directory and all subdirectories. This list is created on start-up. Additionally, TGE 1.4 is capable of finding files added to one of the mod paths after the game has started.

"

$oldModPath = getModPaths(); setModpaths (''''); setModpaths( $oldModPath);

9.5.1 locating Files


In order to locate files listed by the file manager, we use two functions provided by TGE: findFirstFile () and findNextFile () . These functions are meant to be used together.

364

Game Setup Scripting

Chapter 9

When using the file finding functions, remember that file names consist of a path, a name, and an extension. You must add the appropriate wildcards when searching for files down a path.

echo ( findFirstFile ( "* /specialPrefix *. cs" ) );

findFirstFile()
This function will locate the first instance of a specified file or filename pattern in the file manager's list. It then marks the location of this file in the list and returns the filename. The pattern supplied to this function may contain wildcards.
findFirstFile( pattern );

TIy the following example.

/ /tsll () ; echo ( findFirstFile ( "*. cs" ) );

Please note that subsequent calls will return the same value. It is important to understand that each time this function is used, it sets the location of firstMatch. Thus, having two functions calling this function in an overlapping fashion will have undesirable results.

findNextFile ()
Having found the first instance of a filename or filename pattern, we may wish to find subsequent instances. This function does that for us. It will return one new match each time we call it until it finds no more matches, at which time it will return the null string ("").

'.

findNextFile( pattern );

'Tty the following example.


/ /ts12 () ; echo( findNextFile( "*.cs" ) );

If we specify a pattern that does not match that of our call to findFirstFile () , results will be indeterminate. The following function lists all files found matching a specified pattern.

365

Part III

Game Elements

I Its13 () ;
function listAllFiles( %pattern ) ( %filename = findFirstFile( %pattern ); while ("" ! $= %filename ) ( echo(%filename); %filename = findNextFile(%pattern );

listAllFiles("*gui*");

9.5.2 Wildcards
It was mentioned above that we can use wildcards in our file patterns. Table

9.12 shows the wildcards that TGE supports.


Table 9.12.

Wildcards supported by
TGE.

The standard "matches all" wildcard. This equates to the mod directory. For example, when using the GPGT Lesson Kit, "~/*.cs" is the same as "gpgtj* .cs". This file location relative wildcard can be interpreted as "current directory of this file," i.e., this equates to the current directory of the script file it is used in.

"*.C5"

"I'I/main.cs"

" ./test.cs"

9.5.3 Counting Files


We can count the number of files matching a specified pattern using the following function.
getFileCount( pattern );

9.5.4 Calculating File CRC


A CRC (cyclic redundancy code) is a useful thing to have if you need to ensure that users are using the correct version of a file prior to starting the game. Thus, TGE provides a function for calculating the CRC of a file:
getFileCRC(
i~ename

);

In a multiplayer scenario, the server and the client can compare CRCs, and if a client has a file with the same name but a .different CRC, then that client's file is either corrupted, modified, or of a different version than that found on the server.
366

Game Setup Scripting

Chapter 9

9.5.5 Filename Expansion


Frequently, it is useful to skip a lookup on a file and do a direct expansion of the file from a filename using wildcards. This can be accomplished with the following function.
expandFileName( filename );

The Slash

[/J

versus the Dot [.J versus the Tilde [-)

There are three special file path prefixes used in TorqueScript. The first of these is the the slash (j), the second is the dot (.), and the third is the tilde (rv). A slash as the first part of a path tells the engine to start searching in the root directory. The root directory for a TGE game is the directory in which the first "main.cs" file is found. A dot means to start looking in the current directory. That is, look in the directory where the file that contains this script was found. A tilde means to look in the mod paths. In our scripts, we can build up the mod path by calling setModPath () and passing a path or semicolon-specified paths. All mod paths are children of root.

9.5.6 Filename Subelements


The file finding/expanding functions return strings that may contain a path, a file prefix, and a file suffix. It is often necessary to examine just one of these filename subelements. Fortunately, the authors of Torque foresaw this need.
Extracting File Path

Filename expansion is context-based. So, do not make the mistake of trying to use this function from the console. For this function and other functions like it to work, the function requires context. A file provides context to the console while it is parsing that file, but when we open the console and type commands in the command line, we are working in a contextless environment. Thus, this function cannot expand a filename to match the context.

To extract a file path from a filename, use the following method.


filePath( filename );

If the filename contains no path or is not valid, this function will return a null string.

Extracting File Name

A file "name" is considered to be everything but the path; i.e., the prefix + suffix. To extract a file name from a filename, use the following method.
fileName ( filename );

This will remove the path, if one exists, and return the remainder of the string.
367

Parr III

Game Elements

Extracting Prefix The prefix of a filename is the last part of a file's name, before the dot (.J. For example. the prefix of the filename "test.png" would be "test". File paths are not part of a file's prefix. To extract a file prefix from a filename, use the following method.
fileBase( filename );

Extracting Suffix The suffix of a filename is the last part of a file's name, including and after the dot (.J. For example the suffix of the filename "tesLpng" would be ".png". To extract a file suffix from a filename. use the following method.
fileExt( filename );

9.5.7 Before Reading or Writing


We're almost ready to start discussing the actual reading and writing of files, but before we do, let's discuss two more functions. Is It a Valid File? Before attempting to read or write a file. we should always verify that it exists or is valid. TGE provides the following Boolean-returning function for this purpose.
isFile( filename );

This function will return true if the file is a valid member of the file manager's list and false otherwise. Of course, writing to a file that does not exist will create that file, including any subdirectories that may be required as part of the filename's full path. Can I Write to It?

If we want to write to a file, we'd better check that it is writeable.


isWriteableFileName( filename);

Please note that this will return false if the file does not exlst; thus. for writes, we can be lazy and just check this, skipping the isFile () check.

368

---

-------

Game Setup Scripting

Chapter 9

9.5.8 Reading Files


So, we've talked about how to find our files and get some information about them, but how do we read them? TGE provides a class called FileObject. We can use this class in our scripts to both read and write files. In order to read a file, we must do the following. 1. 2. 3. 4. Open the file for reading. Read a line from the file. Repeat step 2 until we reach the end of the file or have completed our task. Close the file. The following function was extracted from a post and modified slightly.

I Its14 ()

function readFile( %filename ) ( %file = new FileObject()i if(%file.openForRead(%filename)) while (! %file. isEOF ()) ( %input = %file.readLine()j echo(%input) j
)

else ( %file.delete() ; return false; I %file.close (); %file.delete (); return truei
)

readFile( expandFilename( "-!prefs.cs H

);

In this example, we create a new instance of a FileObject and then use it to open and to read the file. When we are done, we use it to close the file, and then we delete the object. The key methods used are the following.
openForRead ( fileName ). This method will attempt to open the files specified by the string fileName for reading. If it is not successful, it will return false. readLine () . This methods returns the next full line (terminated by a new line) in the file. If no lines remain, a null string will be returned. isEOF () . This method checks to see if the end of the file has been reached and returns true if it has.

369

- - - - - - _ . _ - --

-- -

Part III

Game Elements

close ( ) . This method closes the file. Do not forget to do this.

That is pretty much it. Very simple.

9.5.9 Writing Files


Writing files is only slightly more complicated than reading them. Before we write to a file, we must answer one very important question: do we want to overwrite the file or append it?
Overwriting Files

To overwrite a file, we do the following. 1. 2. 3. 4. Open the file for writing. Write to it. Repeat step 2 until we are done. Close the file.

function writeFile ( %filename , %data ) ( %file = new FileObject()i if(! %file.openforWrite( %filename ) ) { %file.delete()i return false:
}

%file.writeLine( %data ): %file. close () i %file.delete() ; return true:

[n this example, we create a new instance of a FileObject and then use it to open and to write the file. When we are done, we use it to close the file, and then we delete the object. The key methods used are the following.
fileName ). This method will attempt to open the file specified by the string f i leName for writing. If the specified file does not exist, it is created, but not yet added to the file manager list. If the file already exists. it is cleared. and we start writing at the front of the file. If the open fails, this method will return false. writeLine () . This method writes a string to the file and appends a newline character. close () . This method closes the file, just as it did for reading, but with one exception. If we opened a new file, at this time the filename is added to the file manager's list. Only now can we read it.

openForWrite (

370

Game Setup Scripting

Chapter 9

Appending to Files To append to an existing file, we do the following.


1. 2. 3. 4. Open the file for appending. Write to it. Repeat step 2 until we are done. Close the file.

function appendToFile( %filename , %data ) { %file = new FileObject(); if(! %file.openforAppend( %filename ) ) ( %file. delete () ; return false; %file.writeLine( %data ); %file. close () ; %file.deleteO; return true;

In this example, we create a new instance of a FileObject and then use it


to open and to append to a file. When we are done, we use it to close the file,

and then we delete the object. The key methods used are the following.
openForAppend ( fileName). This method will attempt to open the file specified by the string fileName for appending. If the specified file does not exist, it is created but not yet added co the file manager list. If the file already exists, it is opened, and we start writing at the end of the file. If the open fails, this method will return false . writeLine (). This method writes a string to the file and appends a newline character. . close () . This method closes the file, just as it did for reading, but with one exception. If we opened a new file, at this time, the filename is added to the file manager's list. Only now can we read it.

9.5.10 Maze Runner Lesson # 17 (90 Percent StepJ-leveJ loader


In this lesson, we will discuss the level-loader code. We will not be listing all of the code here, as it is rather lengthy. Instead, we will describe how it works and how the code is structured. Please note that the level loader is responsible for loading and starting all elements of the level. This includes the fireball-shooting block, which we have

371

Part III

Game Elements

not completely covered yet. Specifically, we have not discussed the fireballs themselves, nor have we spoken of the code that fires them. If you wish, you may skip ahead to Lesson tt20 (Section 11.4.3) to see how they work. Not doing so will not affect the current lesson, but until we complete that lesson, the level loader won't start the fireball blocks correctly. Copy Required Files From the accompanying disk, please copy the file "\MazeRunner\Lesson_0l2\ teleporters. cs" into "\MazeRunner\prototype\server\scripts \MazeRunner". Now, edit the function onSerVerCreated () in the file "\MazeRunner\ prototype\server\game.cs" to look like the following (bold lines are new or modified) .
exec("./MazeRunner/teleporters.cs"); II MazeRunner exec("./MazeRunner/levelloader.cs"); II MazeRunner

Levels versus Layers In the following description, we will be using the words level and layer. A level is comprised of one or more layers of game elements. A level may have any number of layers.

Level Files
The premise of this level loader is quite simple. Our goal is to load a single mission and then, at any time we wish, load the components that make up a level. By using a level map and a level loader, we may define as many levels as we want without needing to hand-create an entire mission and then load the mission (which is generally slower than placing items by script for singleplayer games). The first thing we need to do is define the parts of a level file. Level File Format We want to be able to make levels with multiple elements and multiple layers. To do this, the level file cannot be constrained to a fixed length. Instead, it must be dynamic. After some thought, I came up with the syntax for this file shown in Table 9.13. That is it for the syntax. Now, let's designate what letters mean in the actual layer definitions (those 16 lines).

372

------

~-

._--

Game Setup Scripting

Chapter 9

Une/Element
Line 0 LAYER UP LAYER DOWN LAYER DEFINE BLOCKS OBSTACLES

.........

"",",, . I'"

r,'W'l

Table 9.13.

This line is used to store the numeric ID of the level that follows this one. This will increment the current elevation at which blocks and other elements are being placed by 4 world units. This will decrement the current elevation at which blocks and other elements are being placed by 4 world units. This indicates to the level loader that the next line in the file will specify a layer type. This indicates to the level loader that the next 16 lines will contain a map that designates where blocks are placed. This indicates to the level loader that the next 16 lines will contain a map that designates where obstacles (telepor:t: stations and fireball blocks) are placed. This indicates to the level loader that the next 16 lines will contain a map that designates where pickups (coins) are placed. This indicates to the level loader that the next 16 lines will contain a map that designates where the player will be dropped at the beginning of the mission.

Level-file elements,

PICKUPS PLAYERDROPS

Tokens
Each layer definition is composed of 16 lines of 16 characters, meaning that each layer definition may have up to 256 elements in it. Because we have a multitude of things to place (and because we want to leave room for expansion), we will be reusing letters (tokens) between layer types. Table 9.14 lists what individual tokens mean in the various layer contexts.
L8yerType
BLOCKS

T......
A-J

",-

'~~

Table 9.14.

These designate one of the level blocks we discussed in Lesson #5 (Section 6.5.6). These are the fade blocks. The number specifies the number of seconds until the block fades. We will discuss how this fading works in Lesson #18 (Section 10.3.7).

Definitions of tokens,

0-9

x,
OBSTACLES

Y,

One of these will produce a teleport station. These are the fireball blocks, where each number is a block firing in a specified direction. For example: D-North, i-NorthEast, ''', 7-East, 8-NorthEast, and 9 is random. A coin. A player drop point. The player is dropped at the first point found.

0-9

PICKUPS PLAYERDROPS

C P

373

--

"-

---------

Part IJI

Game Elements

Level-Loader Mechanics
The mechanics of the loader are pretty straightforward. It will consume whatever file it has been told to load until it has placed all of the contents or until it hits some kind of error in the level file.

Level-Loader Definition
So. we have some rules upon which to base our level building. and thus we have rules upon which to base the design of the loader. Furthermore. we know the loader must read the file until it is consumed. regardless of how many layers are defined in the file. Let's get started. Go ahead and load up the "levelloader.cs" file in your favorite browser and then follow along as we discu?s it here.
Elevations and Level Increments

The first thing we do in our loader is define some global variables for tracking.
SBaseElevation. Beginning elevation for every new level (not layer). SLevelIncrement. Level up/down step size. $CurrentElevation. Current elevation we are building at (current layer).

Classifying Tokens

We are dealing with a lot of different tokens. We will need to categorize these tokens into groups to minimize our code size. Because we don't want to waste time doing multiple comparisons to determine if anyone token is a teleporter, a fireball block, etc., we need a way to reduce the effort required to categorize tokens. The trick is to create an array where the index of the array is the expected token and the value in the array gives us the information we need. For example:
$BLOCKCLASS[A] $BLOCKCLASS[B) $BLOCKCLASS(Cj / / ... $BLOCKCLASS(Oj SBLOCKCLASS(l) SBLOCKCLASS(2) / / ...
NORMAL;

NORMAL;
NORMAL;

FADE; FADE; FADE;

In the above code. we're saying that any token A.. C will correspond to a normal block while 0.. 2 will be a fade block. So, we don't want to write the code like the following.
if ( ( %token $= A) I I ( %token $= B ) I I ( %token $= C ) ) { II Normal Block Code Here

Game Setup Scripting

Chapter 9

else i f ( ( %token $= 0 ) I I ( %token $= 1 ) I I ( %token $= 2 ) ) ( II Fade Block Code Here

Instead, we can write it like the following.


switch$ ( $BLOCKCLASS [%token] case NORMAL: II Normal Block Code Here case FADE: II Fade Block Code Here ) {

As you can see, this code is not only more elegant but also significantly faster than the multiple comparison case before (and that was with only 6 of the 20 possible block cases shown). buildLevel () We've prepared the globals and helper variables we'll need; now let's write the loader function. The buildLevel () function takes a single argument containing the numeric ID of the level to load. The function assumes that all level files are stored in the directory "\MazeRunner\prototype\data\Missions\LeveIMaps". Given the number 0, the loader will attempt to open a file named "\MazeRunner\prototype\data\Missions\LeveIMaps\leveINumO.txt". If the level loader successfully opens this file, the first thing it will do is read the first line, which contains the numeric ID of the level that follows this one. If no next level is defined, the loader fails out. So far, nothing mysterious has been done, but the next bit of code may seem strange. For several lines, you will see bits of code like the following.
if( isObject( gameLevelGroup ) ) gameLevelGroup.delete() ; MissionCleanup.add( new SimGroup( gameLevelGroup gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( new new new new new new new new SimGroup ( SimGroup( SimGroup( SimGroup ( SimGroup ( SimGroup ( SimGroup( SimGroup (

);

) ; mazeBlocksGroup fade Group ) ) ; FireBallMarkersGroup ) ) ; TeleportStationGroupX ) ) ; TeleportStationGroupY ) ) ; TeleportStationGroupZ ) ) ; TeleportStationEffectsGroup CoinsGroup ) ) ;

) ;

375

Part III

Game Elements

Remember that we are building our levels dynamically. As part of this effort, we are destroying the prior level if it exists. Also, to make our lives simple, we will be tracking all of our objects in named 5imGroups. This is ideal because much of the processing our game does is of an iterative nature, and it is easy to iterate over a 5imGroup. 50, the above statement and the remainder like it in the function are merely removing the last level's SimGroups (if they exist) and then creating the following named 5imGroups.
qameLevelGroup. This is the big daddy of all level SimGroups. It will

contain all of our subsequent 5imGroups for this level. Thus, deleting just this group kills all the child groups and their contents.
mazeBlocksGroup. All normal blocks are stored in this group. fadeGroup. All fadable blocks are stored here. Later, we will iterate over

this group to maintain the fadeblocks' behaviors. fireBallMarkersGroup. All fireball blocks are stored here. Like the fadeGroup, we iterate over this group to keep the fireball blocks firing. TeleportStationX .. TeleportStationZ. These three sets are used to store the three types of teleporter. Teleport stations stored in the same group will target each other. TeleportStationEffectsGroup. Although we have an onRemove ( ) method that deletes the pzone and particle emitter node attached to a trigger when the trigger is deleted, I prefer to store the IDs of these effects in a SimGroup, too. That way, there is no question that they will get deleted on level load (or on mission exit). CoinsGroup. This last group stores all coins (that have not been picked up). We will use this later to determine when the level is complete and it is time to load the next one.

Next, we will see the beginning of the level-loader's main processing loop.
while ( ! %file. isEOF () ) (

From this point on, the level loader will read in lines from the file until the tile is empty or an error occurs. Upon tirst entering this loop, the function reads a line and checks to see what task it represents: LAYER_UP, LAYER_DOWN, or LAYER_DEFINE. For a LAYER_UP or a LAYER_DOWN, we increment/decrement and then go back to the top of the loop (by using continue) to get the next task. If the task does not match any known task type, the function aborts. Eventually, the task we get will be a LAYER_DEFINE. This tells the loader that the next line will be a LAYER_TYPE. 50, the level loader reads the next
376

Game Setup Scripting

ChClpter 9

line and decides which layer type it is: BLOCKS, OBSTACLES, PICKUPS, or PLAYERDROPS. If it is none of these, the function fails out. Assuming the function read a valid layer type, it will use another of those system scripts supplied with the kit and load the next 16 lines into an arrayObject (a scripted class I created so that we may create arrays that can be passed between functions and methods) .. After reading in the next 16 lines (into our arrayObject), the level loader will then pass this array to a specialized function that does the layout for that level type.
layOutBlocks () . This lays out normal blocks and fade blocks. layOutObstacles (). This lays out fireball blocks and teleport stations. layOutPickups () . This lays out coins.

playerDrop (). This will drop the player into the level at a specified

point. After the current layout pass, the loader goes back to the top of the file-reading loop and continues until the end of the file (or error). Eventually, the file will be empty, and we will drop out of the loop. At this point in the code, you will see a function (in two places) that may not yet be familiar to you.
if( fadeGroup.getCount() ) fadeGroup.schedule( 5000 , fade Pass );

if( rireBallMarkersGroup.getCount() ) FireBallMarkersGroup.schedule( 5000

firePass );

In both of the above statements, the script is telling the engine to schedule an event to occur in 5000 milliseconds. The first event is the calling of the method fadePass (). It will be called as follows.
fadeGroup.fadePass();

The second event is the calling of the method firePass () . It will be called as follows.
FireBallMarkersGroup.firePass();

Each of these statements will cause a special function (not yet covered) to iterate over the specified SimGroup and to "do something" to each entry. We will cover this in Lesson #18 (Section 10.3.7).

377

Parr III

Game Elements

layOutBlocks() We will talk about the first of the four layout functions, and then I will leave you to examine the other three on your own. This function has the responsibility for creating content in the world based on the values in the arrayObject it has been passed. To do its jobs, the function uses a nested loop and reads every token of every line and parses these tokens by category (using the trick we discussed at the beginning of this lesson) and then by specific type. It is assumed that every token represents a world space of "4 4 4". Thus, the current position is incremented by "4 4 0" to keep us on the current layer. When a token is found that matches a known category. an object in that category is created. Being smart, we named our files and datablocks in such a way that we can merely append the token to a generic version of the filename or datablock name when loading a file or building an object from a datablock. Once this function has consumed all of the lines in the arrayObject, it deletes the object.

Temporary Spawn Point


One side effect of destroying a level is that the player will fall into the lava because there is no place to stand. So, to solve this problem, while the level loads, we should create a place for the player to stand temporarily. This can be accomplished by editing the file "\MazeRunner\prototype\data\Missions\mazerunner.mis" and adding the following to the end of the mission file (bold lines are new).
new TSStatic() { position = "0 0 295 N ; rotation = "1 0 0 ON; scale = "1 1 1"; shapeName = "-/data!MazeRunner/Shapes/MazeBlock/blockA.dts";
};
);

//--- OBJECT WRITE END ---

Additionally, to get the spawn to work properly, we need to move the spawn point. So, in the same file, locate our spawn point and change the position to the following.
378

Game Setup Scripting


new SimGroup(PlayerDropPoints) new SpawnSphere(} { position "0 0 300"; rotation = "1 0 a 0"; scale = "1 1 1"; dataBlock = "SpawnSphereMarker"; radius = "I"; sphereWeight = "100"; indoorWeight = "100"; outdoorWeight = "100"; locked = "False"; lockCount = "0"; homingCount = "0";

Chi1pter 9

};

};

Now, when we start the mission, the player win be high above the cauldron, and on subsequent loads the avatar will be moved here temporarily.

Testing the Level Loader


At this point, you should be able to test the level loader. Simply start the GPGT Lesson Kit, open the "Maze Runner" mission, open the console, and type buildLevel ( 0 );. Your player should be moved to the extra block we just inserted for a few seconds, and then it should drop onto a new level.

9.6 Compiling and Executing Files


Any file containing a valid script can be compiled and/or executed. We have the freedom to only compile a file, but executing a file implies that it will be compiled if we have not already done so.

9.6. 1 Compiling
We can compile a file without reloading it. This way we don't override functionality accidentally or in some other way corrupt our environment. A successful recompile produces a new file with the same name plus the extension ".dso" appended. In order to compile a file, we use the follOWing syntax (Table 9.15).
compile ( filename);

Please note that compile () will always return 1 if the file is oC nonzero length and it exists. Thus, at this time, the return value is not very useful.
319

Part III

Game Elements

Table 9.15.
Compiling a file.

"""
compile filename
This is the function name.

Delat....
This is a string containing a complete or partial path and the name of the file to be compiled. compile () can expand relative paths.

9.6.2 Executing
As was noted above, executing a file implies that we will recompile it (if necessary) and then reload it. Reloading basically runs the contents of the file, replaces any redefined functions with the new ones, and creates any datablocks defined in the file. The following syntax is described in Table 9.16.
exec( filename [ , noCalls [ , journalScript ] ] );

Please note that it is illegal to exec () zero-length (empty) files. So, if you need one as a placeholder, put the following statement in the file.
return;
Table 9.16.
Executing a file.
exec filename
This is the function name. This is a string containing a complete or partial path and the name of the file to be compiled. exec () can expand relative paths. If this Boolean value is true, it instructs the engine not to execute any code found in the file. Only packages and functions are reloaded. All functional code is skipped. Boolean value specifying whether this is a journal file or not.

noCalls

journalScript

9.7 Game Setup Scripting Summary


In this chapter, we learned about some of the classes and features that Torque provides for setting up and maintaining a game. We learned about the SimSet and SimGroup containers. We then discussed how to create scripted objects using the ScriptObject and ScriptGroup classes. We also learned how these special classes provide namespace scoping and callbacks. We learned about action maps and how to connect user inputs to game actions and reactions.
380

Game Setup Scripting

Chapter 9

Next, we discussed how the file subsystem operates (from the scripted viewpoint) as well as how to locate files and to parse the components of a file path and filename. We closed by learning how to read from, create, and write to new files, as well as how to append to existing files. Last, we discussed how to compile and execute script files.

381

Chapter 10 Gameplay Scripting


This chapter gives an overview of the scripting tasks that are related to implementing gameplay. It will familiarize you with the following specific topics.

Callbacks. We need to understand what they are and what a small set of them do in order to discuss some larger topics later. Event scheduling. As an event-driven simulation, our games often require events to occur at some time in the future. The event-scheduling features of TGE make this possible. Manipulating strings. TorqueScript deals with all data as strings (before these strings are converted to their proper types). Here, we talk about the many ways we can manipulate this string data. Scripted math. Another big part of making games is math. We'll take a little time to review the math features available via scripting. Dynamic scripting. We'll discuss how to write dynamic code; that is, we'll talk about how to assemble and execute scripts at run-time. Basic client-server communication. We'll briefly discuss how basic clientserver communications are achieved.

10.4 Callbacks
For the purpose of this guide, a callback is any console method that is automatically (or directly) called by the engine (or scripts) in response to some event. These callbacks are part of what drives a game; that is, game events are processed by the engine, firing callbacks, which subsequently trigger chains of scripted events. Having defined what a callback is, we are not going to carry on about why we have them or how they work. Instead, we will discuss a few significant callbacks and then move on. There is also an appendix that documents all of the default TGE callbacks (see Appendix A).

10. 1. 1 onAdd () and onRemove ( )


For now, you need only be familiar with eight callbacks. The first two of these callbacks are the onAdd () and the on Remove () callbacks. The onAdd () callback is called just after an object is instantiated. The onRemove () callback is called just prior to the object being deleted.

383

Part III

Game Elements

The calling sequence may still be a bit foggy, so see the following code.
/ /tslO () ;

function myTestDatablock: :onAdd( %theDB, %theObj ) echo("A new object: \cp\c2", %theObj.getName(), "\cO was created with the datablock: \c2", %theDB.getName() ) ;
}

function myTestDatablock: :onRemove( %theDB, %theObj ) { echo("Deleting: \cp\c2"~ %theObj.getName(), "\cO created with the datablock: \cp\c2", %theDB.getName() ) ;
}

datablock StaticShapeData( myTestDatablock ) { category = "LessonShapes";


};

%obj = new StaticShape ( testObject ) { datablock = "myTestDatablock";


};

%obj.delete();

Running this sample produces the following output.


This was mentioned before, but it is very important to remember that both the data block ID and the object ID are passed automatically to callbacks when they are called by the engine, but if you call them manually, you may be responsible for passing one or both of these values yourself. Reread the sections on objects and data blocks in Chapter 4, "Introduction to Torque Script" if this is fuzzy.

A new object: testObject was created with the datablock: myTestDatablock Deleting: testObject created with the datablock: myTestDatablock

So, what happened? The (nearly) exact sequence is as follows. 1. An instance of StaticShape is created using the myTestDatablock datablock and named testObject. 2. onAdd () is automatically called with two arguments, the ID of myTestDatablock and the ID of the newly created StaticShape. 3. The method delete () is called on the instance of StaticShape named
testObject.

..

4. Before the deletion occurs, the onRemove () callback is automatically called with two arguments, the ID of myTestDatablock and the ID of the to-be-deleted StaticShape.

384

Gameplay Scripting

Chapter 10

10. 1.2 onCollision ()


The next callback you need to know about is the onCollision () callback. This callback is called whenever a collision between objects is registered by the engine. The onCollision () callback takes several arguments, and an instance of this callback could be defined as follows.
function PlayerData: :onCollision( %colliderDB , %colliderObj , %collidedObj , %vec , %speed ) { / / ...

Describing the exact details of how and when this callback is called is beyond the scope of this volume. For now we'll just restate that it is called when there is a collision, and then we'll describe the arguments in Table 10.1.
"

:.!

..

~~,

;.; I.-

Table 10.1.
Arguments for onCollision ().

%colliderDB

This argument contains the datablock ID of the object that did the colliding. This argument contains the ID of the object that did the colliding. This argument contains the ID of the object that was collided with. This argument contains a three-element floating point vector describing the direction and magnitude of the collision. This last argument is provided to ease our coding work. It contains the magnitude of the prior vector, i.e., the velocity (or speed) of the collision.

%colliderObj %collidedObj %vec

%speed

10. 1.3 onWake () and onSleep ( )


These two callbacks are associated with GUI controls. The onWake () callback is called when a GUI control or its parent is made the current content of the canvas. The onSleep () callback is called when the GUI control or its parent is removed from the canvas. Alternately for dialogs, onWake () is called when the dialog is pushed, and onSleep () is called when the dialog is popped. This may not mean a great deal to you yet, but it will make more sense when we get to Chapter 12, "Standard TGE GUI Controls."
385

Part III

Game Elements

10.1.4 create ()
There are some who would argue that this is not a callback, and I would almost be willing to concede that point, except that this method is called as the result of a scripted action. That script is part of the standard TGE release. It is the World Editor scripts that call the create () method automatically when we attempt to create a new instance of a class in the world. Some folks may argue with me yet, because this "callback" is scoped to object class names, not to datablocks. This is not a valid argument, however. Yes, almost all callbacks are scoped to datablocks, but there are some callbacks that are scoped to object instances instead. This is one of those exceptions to the rule. I repeat: this special method,is needed for any GameBase child if we wish to be able to add an instance of it from the World Editor Creator. The GPGT Lesson Kit fully defines all the create () methods. If you would like to see how they are written, do a search for the string": : create".

10.1.5 onEnterTrigger () and onLeaveTrigger()


We already discussed these callbacks in Chapter 8, "Mission Objects," but just to refresh your memory:
onEnterTrigger () is called when a shape enters the bounds of a trigger,

and
onLeaveTrigger ()

is called when a shape leaves the bounds of a

trigger. In Chapter 8, we did discuss other callbacks associated with the trigger, but we won't be using them in the prototype for our game.

10.2 Event Scheduling


We have discussed callbacks, and thus we understand the concept of a method being called due to an engine event, such as a collision, an object creation or deletion, etc. However, what if we want to create our own event sequences? Is there a way to do this? Yes; read on.

10.2. 1 Motivation and Concepts


There will be times when we would like to schedule "something" to happen at a future time. Furthermore, we might only want this something to occur if a specific object exists. We might also want this something to execute stand386

Gameplay Scripting

Chapter 10

alone like a function, or on an object (like a callback). Thinking ahead, we might also want to be able to check if the event has executed or if it is still pending. Knowing that it is pending, we may choose to cancel an event(s) we previously issued. All of this we can do.

10.2.2 Scheduling OUf Own Events


TGE provides both a function schedule () and a method schedule () for scheduling events, allowing us to schedule standalone events (using the function) or events that execute on an object (using the method).

The schedule () Function


This form of schedule () is used to call a function at some future time. It has the following syntax (Table 10.2).
%event1D = schedule ( time1nMS , obj 10 I I 0 , argO, ... , argN ); functionName,

Syntax
Element
eventID

!,

-~ .>.

'c,

", ~ ,,~~.-:~i ,",~:

~;~w.:4oi.

::,~

'!IfJ ,:

Table 10.2. The schedule () method.

Upon successfully scheduling an event, the schedule () function returns a unique ID for the event. Time in milliseconds until this event will be executed. For this argument, we can supply a handle to an object or we can pass O. If an object handle is passed and the object associated with the handle is deleted prior to timelnMS, the event will automatically be canceled. This is the unadorned name of the function to execute, e.g. "echo" Optionally, we may supply extra arguments to the event. These arguments may be constants or strings.

timelnMS objID

functionName argO, ... , argN

We use this function as follows.


/ /ts15 () ;
schedule ( 1000 , 0 , echo, "Hello world!" );

After 1 second passes ...


Hello World!

Alternately, we could attach our event to an object.


387

Part III

Game Elements

/ /ts16 () ; %obj = new ScriptObject( test); schedule ( 1000 , %obj , echo, "Hello world!" ); %obj.delete();

After 1 second passes ... nothing happens, because the delete canceled the event.

The schedule () Method


This form of schedule () is used to call a function at some future time. It has the following syntax (Table 10.3).
%eventID
=

objID.schedule( timelnMS , functionName, argO, ... , argN );

Table 10.3.
The schedule () function.

SyI8X
eventID objID

'-.

4e..

:?:~."

TJ

~;~~J;"~d.
'-~'/ ~

.'~f

Upon successfully scheduling an event, the schedule () method returns a unique ID for the event. Because this version of schedule () is a console method, it must be executed on a object, thus we use any acceptable form of an object handle. Time in milliseconds until this event will be executed. This is the unadorned name of the function to execute, e.g. "echo". Optionally, we may supply extra arguments to the event. These arguments may be constants or strings.

timelnMS functionName argO, ... argN

We use this function as follows.


/ /tsl 7 () ;

%obj

new ScriptObject(test);

function test: :doit( %this , %val echo(%this.getName(), " says ", %val );

%obj.schedule( 1000 , doit , "Hello world!" );

After 1 second passes, we see the following.


test says Hello World!

388

Gameplay Scripting

Chapter 10

As with the function version, which watches an object handle, if we were to delete the object, the event would be canceled.
/ /ts18 () ;

%obj = new ScriptObject( test ); %obj.schedule( 1000 , doit , "Hello world!" ); %obj.delete() ;

After 1 second passes ... once again nothing happens because the delete canceled the event.

10.2.3 Checking For and Cancelling Pending Events


So far, we know how to schedule an event, but it can often happen that we need to check if an event has executed prior to doing something new. Or, if the event is pending, we may need to cancel it.

isEventPending()
TGE provides a function to check for pending events. This function checks to see if an event is still queued in the event queue. It returns true if the event is found and false if not. The syntax is as follows (Table lOA).
%pending
I;
=

isEventPending( eventID );

DeIafptIon
%pending

Table 10.4.
Checking for pending events.

As noted, this method returns a Boolean value indicating true (the event is pending) or false (the event is not pending). This is an ID preViously returned from either the schedule () function or method .

eventID

.
Event Times If an event is in fact still pending, we can gather additional data about it, including the time since it started (in milliseconds).
%sinceStartedMS
=

getTimeSinceStart( eventID );

We can also find out the time left until it executes (in milliseconds).
%remainingMS
=

getEventTimeLeft( eventID );

389

Part III

Game Elements

And we can find out the total duration for the schedule.
%durationMS

= getScheduleDuration( eventID );

The syntax element eventID is an ID previously returned from either the schedule () function or method.

cancel ()
Events dependent on an object are automatically canceled if the object is deleted; thus, if we know our event is tied to an object, we can just delete the object. However, this may not always be suitable, and in fact often it is not. Thus, TGE provides a cancel () function with the following syntax:
cancel( eventID );

The syntax element eventID is an ID preViously returned from either the schedule () function or method.

10.2.4 Event Scheduling and Accuracy


It is important to step back for a moment and ask the follOWing question:

"Just how accurate is my event timing going to be, and do I care?" Regardless of when an event is scheduled, it will not be executed until there is a tick. Additionally, there are other factors that can affect timing, including engine internals, network latency, etc. $0 the answer for the first part of the question would be, "Not entirely accurate." In fact, you may experience a small amount of drift (positive or negative) in the actual time before an event occurs. This is significant for very short-order events (less than 32 milliseconds), and very long-order events that are cascaded (i.e., event A schedules event B, ...). Thus, you must consider the second part of the question carefully. Overall, the accuracy of the event-scheduling system is usually sufficient to handle most of our needs, but you will sometimes find it is not. At that point, you may need to write engine code instead of relying on event-driven scripts. The following code demonstrates the timing variances that can occur for scheduled events.
function accuracyCheck( %scheduledTime, %time , %repeats ) I %actualTime = getRealTime() - %scheduledTime; echo ("Requested Execution Time: " , %time , .. Actual Execution Time:." , %actualTime " :: Difference (ms): " , %actualTime - 'time);

390

Gameplay Scripting

Chaprer iO

if ( %repeats) { %repeats = %repeats - 1; testscheduleAccuracy ( %time ,%repeats);

function testScheduleAccuracy (%time , %repeats ) { schedule ( %time , 0 , accuracyCheck , getRealTime(}

, %time ,

%repeats );

Here is some sample output from a call to testScheduleAccuracy () .


/ /ts19 () ; testScheduleAccuracy( 1 , Requested Execution Time: . Difference (ms) : -4 Requested Execution Time: . Difference (ms) : -4 Requested Execution Time: . Difference (ms) : -4 Requested Execution Time: . Difference (ms) : -4 Requested Execution Time: . Difference (ms) : -4 Requested Execution Time: . Difference (ms) : -4 Requested Execution Time: . Difference (ms) : 12 Requested Execution Time: Difference (ms) : 12 Requested Execution Time: Difference (ms) : 12 Requested Execution Time: . Difference (ms) : 12 Requested Execution Time: . Difference (ms) : 12
10 ) ; 1 . Actual Execution Time: -3
1

Actual Execution Time: -3

1 1 1
1

.
.

Actual Execution Time: -3 Actual Execution Time: -3 Actual Execution Time: -3

Actual Execution Time: -3 Actual Execution Time: 13 Actual Execution Time: 13 Actual Execution Time: 13 Actual Execution Time: 13 Actual Execution Time: 13

1 1 1 1 1

.
. . . .

. .

Rerun this a few times. Your results should vary.

Repeating an Event
It may not be obvious at first, but if you want to create an event that repeats, you must rescheduLe that event. The simplest way to do this is by putting a call to schedule () in the function that you are scheduling, or in function that the scheduled function calls. Here is a simple example.

391

Part III

Game Elements

function repeatForever( ) II do something schedule ( 1000, a , repeatForever

It is generally safer to tie scheduled events to the existence of an object. Otherwise, it is easy to get runaway schedules occurring in the background. Over time, these may eat a lot of CPU time.

In this example, we could either schedule repeatForever () or call it directly, and it would continue to be rescheduled every 1000 milliseconds until the engine stopped.

10.3 Manipulating Strings


As noted in Chapter 4, "Introduction to TorqueScript, " all operands in TorqueScript are treated as strings. Therefore, it would be good for us if there were facilities for parsing and otherwise manipulating these strings. TGE provides a good-sized list of console functions dedicated to string manipulations ranging from the mundane to the complex. I'll list them all here and demonstrate the more tricky functions/concepts.

10.3. 1 Words
In TorqueScript, every whitespace-separated element in a string is called a word. For example, in the string "Torque Is Cool", we have three words: word a is "Torque", word 1 is "Is", and word 2 is "Cool". Table 10.5 shows the functions for manipulating words.
Table 10.5.
Manipulating words.

I~}V.

."

. ,::,,:,.~

.
~.-,

:.

"".

'.;,','.C:

.
Returns first word in string text.

','

;,;

'

firstWord( text

)
)

getWord( text , index

Returns word at index in string text. index o is first word and so on. Returns count of all words in string text. Returns all words in string text between index and (optional) endindex. If endindex is not supplied, end of string is assumed.
)

getWordCount( text

getWords( text , index [ , endindex J )

removeWord( text , index

Removes the word at index in string text, Also removes the whitespace following the word located at index. Returns all words in string text excluding first word.
)

restWords( text

setWord( text , index , replace

Replaces the word in string text at index with the string in replace.

392

Gameplay Scripting

Chapter 10

/ /ts20 () ; %test = "Torque cool!"; echo( %test , " has" , getWordCount( %test ) , " words." ); %test = setWord( %test , 0 , "Torque is is" ); echo( %test , " has" , getWordCount( %test " words." ); %test = removeWord( %test, 1 ); echo ( %test , " has" , getWordCount( %test ) , " words." ); while ( "" ! $= %test ) { echo ( firstWord( %test ) ); %test = restWords( %test ) ;

10.3.2 Tokens
Frequently when we parse files, we will read in strings that are actually a series of tokens separated by some delimiter. Some common delimiters are:
\\ i : ,

I " .

TGE supplies a single function that can be used to pull these tokens out of a string in an iterative fashion. The way this function works seems a little mysterious at first, but is actually pretty straightforward. So, let's define it, then use it (Table 10.6).
Table 10.6.
nextToken ( str , tokenVar , delim )
This function returns a truncated version of the passed-in string str, where the part that has been truncated is equal to the first token along with the first instance of delim. Furthermore, the function places the removed token into the named variable tokenVar. Note that tokenVar does not include the delimiter.

Pulling out tokens using delimiters.

/ /ts21 () ; function printTokens( %tokenString ) { %tmpTokens = %tokenString; while ( "" ! $= %tmpTokens ) { %tmpTokens = nextToken( %tmpTokens , "myToken" , ";" ); echo( %myToken );
}

printTokens( "This;is;a;sample;string;of;tokens;." );

In this example, we've take a string of tokens separated by semicolons and iteratively stripped them out of the string from left to right. The next Token ( )
393

Part III

Game Elements

method places each token in a variable, which we specify, named %myToken. After stripping out a token, the remainder of the string is returned by the method. Because we're actually manipulating the string as part of our processing loop, it is a good idea to work with a copy of the string. There is one more thing you need to know. We actually named the temporary token variable "myToken", and TGE was smart enough to know that that means %myToken. However, TGE did this because nextToken () was called from within a function. If you use next Token () in a file that is being executed outside the scope of a function, or if you use it directly in the console, "myToken" will become the global variable $myToken. Pretty smart, eh?

10.3.3 Records
So far, we have words and tokens. The next data-organizing methodology to understand is records. A record is nothing more than a string that ends in a newline character. Thus, if we have a string containing elements separated by newlines, each element is considered to be a record. Records can have multiple words and/or tokens. In fact, this is really a kind of special case where the delimiter in our token string can only be the newline character "\n" (same as the operator "NL"). Table 10.7 explains the functions used with records.
Table 10.7.
Working with records.

getRecord( text, index) getRecordCount( text) getRecords( text, index [ , endlndex removeRecord( text, index)

Returns the record in string text at

index.
Returns the number of records in string

text.
Returns the records in string text between index and endlndex (or end of string if endlndex is not specified). Removes the record in string text located at index. Also removes newline character following the record located at

.
setRecord( text, index, replace

index.
Replaces the record in string text at index with the string replace.

IIts33 () ;
function testRecords( %recordString ) ( %tmpRecord = %recordString; echo( %tmpRecord, "\n" ); for( %count = 0; %count < getRecordCount( %tmpRecord ); %count++ ) {

394

Gameplay Scripting

Chapter 10

%theRecord = getRecord( %tmpRecord , %count ); echol "Current record: ", %theRecord ); if I %theRecord $= "test" ) ( echol"\c3Replacing records ... "); %tmpRecord = setRecordl %tmpRecord , %count , %theRecord NL "of~ NL "records." );

while I getRecordCountl %tmpRecord ) ) ( %concatRecordString = %concatRecordString SPC getRecordl %tmpRecord , 0 ); %tmpRecord = removeRecordl %tmpRecord , 0 ); echol "\n", %concatRecordString ); testRecordsl "This" NL "is" NL "a" NL "test" );

10.3.4 Fields
The final data-organizing methodology to understand is fields. A field is nothing more than a string that ends in a newline character or a TAB character. Table 10.8 explains the functions used with fields.

function
getField ( text , index ) getFieldCount( text ) getFields( text , index [ endlndex ] ) removeField( text , index

'.

'-

Table 10.8.

Returns the field in string text at index. Returns the number of fields in string text.

Working with fields.

Returns the fields in string text between index and endlndex (or end of string if endlndex is not specified). Removes the field in string text located at index. Also removes newline character or TAB character following the field located at index. Replaces the field in string text at index with the string replace.

setField( text

index , replace )

It would be wasteful to show the whole sample routine, so an abbreviated version is shown below. It is nearly identical to the above records test with the few exceptions shown.
395

-_._-------

Part III

Game Elements

/ /ts34 () ;
function testFields( %fieldString ) (

II ... Same as testRecords() except using field functions


echo("\c3Replacing fields ... "); %tmpField = setField ( %tmpField , %count , %theField NL "of" TAB "fields." ); II ... Same as testRecords() except using field functions
}

testFields( "This" TAB "is" NL "a" TAB "test" );

10.3.5 Conversion
Table 10.9.
Converting characters.

strlwr( string strupr( string

Converts all alpha characters in string to lowercase. Converts all alpha characters in string to uppercase.

Alphabetic characters in a string can be converted from uppercase to lowercase and vice versa (Table 10.9).
/ /ts35 () ; echo( strlwr("YEAH these ARE") SPC strupr("pretty OBVIOUS.") );

10.3.6 Metrics
The function strlen ( string ) returns the length of a string.

10.3.7 Searching and Replacing


TGE provides a few standard ways of searching for strings and characters within strings. These searches are all case sensitive.
, Table 10.10.
Searching for strings and characters.

getSubStr( string, start, numChars

Returns a string composed of all the characters in string beginning at start and continuing for numChars or until the end of the string, whichever comes first. Returns a string composed of all the characters in string beginning at the first instance of char and continuing until the end of the string. Returns null string if char not found (case sensitive).

strchr( string, char)

396

Gameplay Scripting

Chapter 10

1.<,1V":'.~
I'JP>.".

;..<,;

,(""

.if./.

..:.

".

'~";1-.,'5: ;~.:;:t;:.::

Table 10.10 (continued).

strpos( source , target [ , offset ] )

Returns the location of the first instance of string target in string source. Optionally, a starting offset may be provided, telling the function to ignore all characters before that point. If no match is found, -1 is returned. Replaces all occurrences of the string from with the string to in string and returns this new string (case sensitive). Returns position of first occurrence of substr in string. Returns -1 if not found.

strrep1ace( string, from , to )

strstr( string , substr

//ts36 0 ;
%testString
=

"TGE is cool. TGE is fun. TGE Rocks. Use TGE to make a game!";

echo ( %testString, "\n" );

II Get string length %len = strlen(%testString); echo ( "\c3This string is ", %len , " characters long.", "\n" ); II Count instances of TGE %lastTGE = -1; while ( %foundAt >= 0 ) { %foundAt = strpos( %testString , "TGE" , %lastTGE + 1 ); if ( %foundAt > -1 ) { %lastTGE = %foundAt; %count++;

echo ( "\c3It contains ", %count, " instances of the substring TGE.", "\n" );

II Replace all instances of TGE echo ( "\c3Replacing all instances of TGE ... ", "\n" ); %testString2 = strReplace( %testString , "TGE", "Torque Game Engine" ); echo ( %testString2, "\n" ); II Only replace last instance of TGE echo ( "\c3Replacing last instance of TGE ... ~, "\n" ); %testString3 = getSubStr( %testString , 0 , %lastTGE ) "the Torque Game Engine" @ getSubStr( %testString , %lastTGE + 3 , %len );

397

Part III

Game Elements

echo ( %testString3, "\n" );

II Modify and print the last sentence. echo ( "\c3Modifying and printing last sentence only ... ", "\n" ); %testString4 = strchr( %testString , "UN ) ; %testString4 = strReplace( %testString4 , "a game" , "\cp\c3games\co that Rock" ); echo ( %testString4, "\n" );

10.3.8 Comparisons
We have a means of comparing arithmetic values (1, 2, 3, ...), and we have the string comparison operator $=, but there are no string operators corresponding to the arithmetic operators> and <. Thus, TGE provides two functions to accomplish this work. The first is case sensitive, while the second is not (see Table 10.11).
Table 10. 11. Making lexicographic comparisons.
strcmp( stringl , string2

Does CAse-sensitive lexicographic comparison of stringl and string2. Returns the following. -1 if stringl comes before string2 in alphabetiCAl order. a if stringl is equivalent to string2. 1 if stringl comes after string2 in alphabetiCAl order. Does CAse-insensitive lexicographic comparison of stringl and string2. Returns the following. -1 if stringl comes before string2 in alphabetiCAl order. a if stringl is equivalent to string2. 1 if stringl comes after string2 in a!phabetiCAI order.

stricmp( stringl , string2

IIts37 () ;
echo("\c3Lexicographic comparisons are not the same as arithmetic comparisons ... "); echo ("lOa - 10 == 90, but strcmp ( \"100\" , \"10\" ) == " strcmp ( "100" , "10" ) ); echo("\n", "\c3Don\'t forget about case-sensitivity ... "); echo ("strcmp( \"ABC\" , \"abc\" ) == " , strcmp( "ABC" , "abc" ) , ", but" ); echo ("stricmp ( \"ABC\" , \"abc\" ) == " , stricmp( "ABC" "abc" ) );

398

Gameplay Scripting

Chapter 10

10.3.9 Trimming and Stripping


It will often be necessary to clean up strings before displaying or storing them. To

enable this task, TGE supplies some standard utility functions (Table 10.12).
Table 10.12.
Itrim( string)
Returns string with all leading whitespace removed. Returns string with all trailing whitespace removed. Returns string with all characters in string chars removed. Returns string with all TorqueML characters removed. Returns string with all trailing spaces and underscores removed. Returns string with both trailing and leading whitespace removed.

Cleaning up strings.

rtrim ( string )

stripChars ( string, chars)

stripMLControlChars ( string)

stripTrailingSpaces ( string)

trim ( string )

/ /ts38 () ; %toClean = "<tab:60> I'm,<spush><font: arial: 8> "all, clean, <SpOp>H; echo ("\c3Cleaning up an ugly string ... HI; echo(%toCleanl;
echo("\n H, "\c3Remove Mark-up language .. . H); %toClean = stripMLControlChars( %toClean ); echo(%toClean);

echo("\n H, "\c3Remove leading and trailing spaces .. . H); %toClean = trim( %toClean I; echo(%toCleanl; echo("\n H, "\c3Remove commas .. . H); %toClean = stripChars( %toClean , ",HI; echo(%toCleanl; echo("\n H, "\c3Get rid of underscores ... HI; %toClean = stripTrailingSpaces( %toClean I; echo(%toClean);

399

Part III

Game Elements

10.4 Scripted Math


TGE provides a rich set of console math functions. The majority of these functions are centered around 3D mathematics, but there are a few other categories as well. All of these functions are documented in the "Console Functions" appendix. However, we will take a brief tour of these functions so you know what is available to you.

10.4. 1 Floating-Point Arithmetic


All the arithmetic functions take floating-point values and return floating-point results, but they can be used for integer mathematics, too (Table 10.13).

I Its39 () ;
echo( "I-51 == ", mAbs( -5 ), "\n" ); echo( "Next greatest integer from 4.3 ", mCeil( 4.3 ), "\n" ); echo( "Next smallest integer from 4.3 ", mFloor( 4.3 ), "\n" ); echo( "2 raised to the power of 3.14159 == ", mPow( 2 , 3.14159 ), "\n" ); echo! "Square root of 2 == ", mSqrt( 2 ), "\n" );

10.4.2 Trigonometric Functions


We use trigonometric functions frequently to solve problems in the realm of 3D games; thus, TGE has provided a complete set of these functions to be used in TorqueScript (Table 10.14).

10.4.3 Vectors
In addition to trigonometric calculations, we will frequently be calculating vector results to move game objects, check for intersections, and various other tasks. A good set of vector functions simplifies this work (Table 10.15).

10.4.4 Matrices
Hopefully, you won't find yourself needing to do too many matrix calculations, but if you do, TGE provides some functions (Table 10.16). Most of these have obvious uses, but MatrixMulPoint () may seem a bit mysterious. This can be used'to translate a point by a transform. Having this operation available makes it possible to check for collision between an object's scaled objectBox and other objects like the terrain. In fact, this exact
400

Gameplay Scripting

Chapter 10

problem was solved in 7hbes 2 using MatrixMulPoint (). The gist of the solution went something like the following.
Pseudocode for scaled objectBox vs. terrain penetration 1. Obtain objectBox for shape and scale both vectors appropriately to match object's scale. %objBox = %obj.getObjectBox(}; II ... do scaling here ... 2. Create array of eight points containing untranslated position of objectBox bounds. %transform = %obj.getTransform(); 3. Acquire object's transform. Iterate over array and use 'MatrixMulPoint(}' to calculate translated position of vertices. for(%count = 0; %count < 8; %count++) { %newBoundPos[%countl = matrixMulPoint( %transform, %oldBoundPos[%countJ };

II II II

II II II II

4. Iterate over new bounds points using rayCast to check for collision between bounds and terrain. %collision = 0; for (%count = 0; %count < 7; %count ++} { %obj = containerRayCast( %oldBoundPos(%countl , %newBoundPos(%countl , $TypeMasks: :TerrainObjectType ,

II II

o }i
%collision 1= %obj;

II
if

5. If %collision is not zero, a collision occurred. (%collision) echo ( "Oops, got a collision!" );

10.4.5 Quadratics and Cubics


If you're not a mathematician, these functions may sound a bit spooky, but if you dredge up your old algebra and calculus notes, you'll recall that they are simply the following.
Quadratic. Second-order polynomial of the form: ax2 + bx + e = O. Warning: This function produces the inverse of the solution '(see example below). Cubic. Third-order polynomial of the form: ax 3 + bx2 + ex + d function works as expected.
=

O. This
401

Part III

Game Elements

Table 10.13.
Arithmetic functions.

FunctIon
mAbs ( operand
)

DeIcrIption
Returns the absolute (nonnegative) value of operand. Returns the next greatest integer (as a float) starting at operand and rounding up: mCeil (4.5) returns 5.0.

~::~j

mCeil( operand ) mFloor( operand ) mLog( operand


)

Returns the next smallest integer (as a float) starting at operand and rounding down: mFloor (4.5) returns 4.0. Returns the natural log of operand. Returns the value operandA ~ operandB; i.e., operandA raised to the power of operandB. Returns the square root of operand.

mPow( operandA , operandB ) mSqrt( operand )

Table 10.14.
Trigonometric functions.

FunctIon
mAcos( operand ) mAsin ( operand
)

DeIcrIpdon
Returns the inverse cosine of operand. Returns the inverse sine of operand. Returns the inverse tangent of operand. Returns the cosine of operand. Converts operand from degrees to radians. Converts operand from radians to degrees. Returns the sine of operand. Returns the tangent of operand.

mAtan ( operand ) mCos( operand ) mDegToRad( operand ) mRadToDeg( operand mSin( operand mTan( operand
) )
)

Table 10.15.
Vector functions.

t ,

FunctIon
)

DeIcrIption
Adds vectl and vect2.

VectorAdd( vectl , vect2 ) VectorCross( vectl , vect2 VectorDist( vectl , vect2 ) VectorDot( vectl , vect2 VectorLen( vect ) VectorNormalize( vect
)

calculates cross product of vectl and vect2. calculates distance between points specified by vectl and vect2. Returns scalar dot product of vectl and vect2. Returns length of vector vect. Returns unit-length version of vect. Returns a 3 x 3 matrix containing the orthogonal basis of the vector described by the aXis-angie representation <x y z> angle.

VectorOrthoBasis( "x y z angle" ) VectorScale( vect VectorSub ( vectl

, ,

scalar ) vect2
)

scales the vector vect by the amount scalar.


Subtracts vect2 from vect 1.

402

Gameplay Scripting

Chapter I a

FunctIon
MatrixCreate( Pos , Rot)
Creates a 3 x 3 matrix from the three-element floating-point position vector Pos and the fourelement floating-point axis-angle vector Rot. Creates a matrix from the Euler angles "Ax Ay Az". Multiplies the three-element floating-point po i n t vector by the standard seven-element floating-point transform. Multiplies the 3 x 3 matrices Left and Right.

Table 10.16.
Matrix functions.

MatrixCreateFromEuler ( "Ax Ay Az" ) MatrixMulPoint( transform, point )

MatrixMultiply( Left, Right)

Table 10.17.
mSolveQuadratic( a , b , c )
Solve for xO, xl in second-order polynomial equation with factors a, b, c. Quadratic and cubic functions.

Warning: xO and xl are inverted.


mSolveCubic( a , b , c , d )
Solve for xO, xl, x2 in third-order polynomial equation with factors a, b, c, d.

The functions in Table 10.17 return a vector of values in the form" sol xO xl ... xn", where sol is the number of solutions and xO ... xn are the values of those solutions. The number of solutions, sol, should be 2 for a quadratic and 3 for a cubic or else the calculation has failed. A failure will occur (sol == 0) if there is no solution to the equation you are trying to solve. This always means that you have entered factors for an equation of the form ax2 + bx + e*-O or ax3 + bx 2 + ex + d! *- O. These functions can only solve for equations that result in O. xO and xl are the factored values for x. See the examples below for clarification.

II II II II
II

All samples below drawn from 1728 Software Systems Sample Calculations: http://www.1728.com/. Cool calculators and converters; check it out.

Quadratic Test (x + 2) (x + 3) 0 => x"2 + 5x + 6 = 0 echo( "Solutions: ", mSolveQuadratic( 1 , 5 , 6 ) ); II Produces: 2 2 3 meaning there are two solutions, II 2 and 3, but it is easy to see that we should have II received 2 -2 -3. Be aware of this bug.

403

Part III

Game Elements

echo ( "X

=- " ,

rnSolveQuadratic( 2 , 10 , -100 ) );

(x - 1) = a => 2x 3 - 4x 2 - 22x + 24 = 0 echo ( "Solutions: ", rnSolveCubic ( 2 I -4 , -22 , 24 ) ) i II Produces: 3 -3 1 4 which matches the factored solution II to the cubic 'above.
A A

II Cubic Test: I I (x - 4) (x + 3)

10.4.6 Miscellaneous
Centroids
A concept we deal with frequently is that of the center, or centroid, of an object or a space. It will often occur that we know the bounds of a space and want the exact center of that space. The method getBoxCenter ( Box) does that for us, It takes a single string containing two three-element floating-point vectors representing the outer bounds of a (possibly irregular) rectangular solid region and returns a three-element floating-point vector representing the center of that rectangular solid.
/ /ts22 () ;
%cube = "-1.0 -1.0 -1.0 1.0 1.0 1.0"; echo( getBoxCenter( %cube ) );

Random Numbers
You will, almost invariably, need random numbers at some time in the design of your game. Knowing this, the authors of TGE have provided some methods to produce them.

Initializing the Random Number Generator

-,

You don't necessarily need to initialize the random number generator, hut if you want to be able to repeat your random results (e.g. you're doing some debugging and want the same random sequence every time), simply set the seed to the same value before starting the sequence (Table 10.18). You can also retrieve the seed value, prior to your sequence, in case you need to plug it in later.

Getting Random Values


There is only one function supplied for getting random values, but it can be called in a variety of ways (Table 10.19).

404

Gameplay Scripting

Chapter 10

Function
setRandomSeed( seed getRandomSeed ()
)

.~

.':'/.
.

'.

.~

'::.'

:'~

:;;'. . ;.:<~\~

Table 10.18.
Initializing the random number generator.

Sets random seed to seed. Returns current seed.

Function
getRandom(
)
)

,"'~

i.'

Table 10.19.
Getting random numbers.

Returns a random value in the range [ 0.0 , 1.0 ]. Returns a random value in the range [ 0.0 , max ].
)

getRandom( max

getRandom( min , max

Returns a random value in the range [ min, max ].

IIts23 () ;
%seed = getRandomSeed(); for (%count 0; %count < 100 ; %count++ %x[%count] = getRandom( %count );

setRandomSeed( %seed ); for (%count 0; %count < 100 ; %count++ ) { %y[%countj = getRandom( %count );

%mismatches

0;

for ( %count = 0 ; %count < 100 ; %count++ ) { if( %x[%countj != %y[%count] ) { error( "Failed to reproduce same sequence of random numbers!H ); error("Seed: H SPC %seed ); error("Count: H SPC %count ); error(%x[%count] SPC "!=H SPC %y[%count] ); %mismatches++;

echo("There were H,

%mismatches, " mismatches. H);

Floating-Point Manipulation
On occasion, when you're doing a floating-point calculation, it would be nice if you could force the result to have a fixed number of decimal places. TGE provides the function mFloatLength ( operand, numDecimals ) that forces a floating-point value to have a specified number of decimal places.

405

Part III

Game Elements

Furthermore, TGE will round the last place up if the actual value extends beyond the specified range and if the next decimal place is greater than or equal to 5.

I Its24 () ;
echo( mF1oatLength( 1.196 , echo( mF1oatLength( 1.196 , 2 ) 10 ) ); );

10.4.7 Maze Runner lesson # 18 (90 Percent StepJ-Game Events


In this lesson, we will examine the scripts used to fade blocks in and out, and we will examine the functions used to shoot fireballs on a regular basis. Now that we have covered callbacks, scheduling, string manipulation, and scripted math, we should be ready to examine how these gameplay scripts work. Please note: This lesson depends on Lesson /14 (Section 6.4.4). Fade Blocks There are three blocks of code we are interested in for the fade blocks. The first of these is in the file "\MazeRunner\starter.fps\server\scripts\ MazeRunner\levelloader.cs ". At the end of the function Bui1dLeve1 () , there is a little snippet of code that checks to see if there are any fade blocks in the fadeGroup SimGroup. If there are, the loader schedules a fadePass () in 5000 milliseconds.
i f ( fadeGroup.getCount() ) fadeGroup.schedu1e( 5000 , fadePass );

In the code on this page, you will see a call to forEach () . This is not a standard function, but rather one of several utility functions that has been provided with the GPGT Lesson Kit as well as separately on the accompanying disk. Please see Appendix A.7, "Scripted Systems Quick Reference," under "GPGT Utilities" to learn more about this utility method and the others that have been supplied with this book.

fadePass ()

This function has the task of coming back every $ s tepT ime (1000) milliseconds and updating all of the fade blocks. The motivation for updating all the blocks simultaneously is that it gives us greater control over the behavior ~ of the blocks than if each block scheduled its own maintenance. Also, by maintaining a single entry and exit point, we only use one schedule, thus reducing overhead.
function SimSet:: fadePass ( %theSet ) { %theSet.forEach( fadeStep , true ); %theSet.schedu1e( $stepTime , fadePass );

406

As can be seen, this function merely iterates over the blocks in the set and runs fadeStep () on each of them.

,~---

GamepJay Scripting

Chapter 10

fadeStep ()

This function has the responsibility for advancing the fade status of an individual fade block by one time period. A fade block can be in one of three states.
waitToFadeOut. The block is waiting to begin a fade. waitToFadeln. The block is faded out and waiting to begin fading in. wait. The block is in a dead cycle waiting for all other blocks to complete

the current fade cycle. A fade cycle is always 10 seconds long (as implemented in "fadeblocks.cs"). During a single fade cycle, every single fade block will fade out, fade in, and wait for its peers to finish their fade cycle. By using this method instead of allowing blocks to fade in, fade out, fade in, ad infinitum, without synchronizing, we avoid chaos. The game would be no fun if the blocks faded in and out chaotically. But, because we can rely on a cycle always taking 10 seconds and then repeating itself, the player can plan ahead after observing a cycle or two. Enough talking. Let's look at the code.
function StaticShape: :fadeStep( %theBlock ) ( %theBlock.timer = %theBlock.timer - $stepTime;

II Check for flip-time


if( %theBlock.timer <= 0 ) ( swi tch$ (% theBlock. action) ( case "waitToFadeOut": %theBlock.timer = $basePauseTime; %theBlock.startFade( $fadeTime , 0 , true ); %theBlock. schedule ( $fadeTime , setHidden , true ); %theBlock.action = "waitToFadeln"; case "waitToFadeln": %theBlock.timer = SbasePauseTime; %theBlock.setHidden( false ); %theBlock.startFade( $fadeTime , 0 , false ); %theBlock.action = "wait"; case "wait": %theBlock.timer = %Obj.maxTime; %theBlock.action = "waitToFadeOut";

>.

407

-_.

__

..

_-~

Part III

Game Elements

As we can see, individual blocks have an internal timer containing some predefined value. When that timer gets down to (or below) zero, it is time to change the block's state and do some work. Initially, all blocks will have the following values. This value will be between 1000 and 10,000 milliseconds. maxTime. This value will be the same as timer. The value in this field is never changed after the block is implemented. action. All blocks start out executing the action waitToFadeOut.
timer.

Now, if we restrict our discussion to just one block and assume that the block has a timer and maxTime of 1000 milliseconds, over time, we will see the behavior described in Table 10.19.
Table 10.19. Fade behavior of one block.

~
0

b:;~

:.

.'

,'~-i, ._:.;,.~.,

"

.1

.,.,'

J:

"",",

.'.. '~~~j

timer = timer - 1000 (0 <= 0 continue executing) (block is visible). action == wai tToFadeOut. Block starts to fade out. Block schedules a hide. action = wai tToFadeln. timer = 10000.

1000 2000 3000 4000 5000 6000 7000 8000 9000 10000

timer = timer - 1000 ( 9000> 0 skip) (block is invisible). timer = timer - 1000 ( 8000> 0 skip) (block is invisible), timer = timer - 1000 ( 7000 > 0 skip) (block is invisible). timer = timer - 1000 ( 6000 > 0 skip) (block is invisible). timer = timer - 1000 ( 5000 > 0 skip) (block is invisible). timer = timer - 1000 ( 4000> 0 skip) (block is invisible). timer = timer - 1000 ( 3000> 0 skip) (block is inJisible). timer = timer - 1000 ( 2000 > 0 skip) (block is invisible). timer = timer - 1000 ( 1000 > 0 skip) (block is invisible). timer = timer - 1000 (0 <= 0 continue executing) (block is invisible). action == wai tToFadeln. Block unhides. Block starts to fade in. action = wai t. timer = 1000.

11000

timer = timer - 1000 (0 <= 0 continue executing) (block is visible). action == wai t. timer = 1000. Sequence repeats.

408

...

,--

- - - - ------

- - - --

Gameplay Scripting

Chaprer 10

The important thing to note about this behavior is that the fade blocks support up to ten blocks with incrementing (by 1000 milliseconds) fade times to be placed in order. Subsequently, these blocks will fade out in order. Then, one second after the last block fades out, the first block will start to fade back in. Thus, the fade in and out is deterministic and cyclic, allowing a player to observe a pattern and to memorize it.

Fireballs
There are three blocks of code we are interested in for the fireball blocks. The first of these is in the file "\MazeRunner\starterJps\server\scripts\MazeRunner\levelloader.cs". At the end of the function BuildLevel (), there is a little snippet of code that checks to see if there are any fireball blocks in the FireBallMarkersGroup SimGroup. If there are, the loader schedules a firePass () in 1500 milliseconds.
if( FireBallMarkersGroup.getCount() ) FireBallMarkersGroup.schedule( 1500

firePass );

firePass ()
This function has the task of coming back every $stepTime (1000) milliseconds and checking each fireball block to see if that fireball block should fire a new fireball. Again, controlling fireballs this way (as with fade blocks) allows us to use a single schedule () event to handle all of our fireball blocks. This is easy to understand and efficient.
function SimSet:: fire Pass ( %theSet ) ( %theSet.forEach( doFire , true ); %theSet.schedule( $fireTime I doFire };

doFire ()
Again, we have created a function that will operate on individual blocks to enact each block's action if it is time to do so. Here is a summarized listing of the function.
function StaticShape: :doFire( %marker } ( if( isObject( %marker.bullet ) ) return;

II Handle random fire marker case %firePath = ( %marker.type == 9 ) ? getRandom( 0 %marker.type ;

9 )

409

Part /II

Game Elements

switch ( %firePath ) {

II II II

NORTH

case 0: %marker.shootFireBall( FireBallProjectile , "0 1 0" , 20 }; // ... similar code for case 1 .. 7

II
II
DOWN

II
case 8: %marker. shootFireBall ( FireBallProj ectile , "0 0 -1" , 20 );

We have not examined the shootFireBall () method, but when this. method executes, it will create a projectile and store the ID of that projectile in the block's bullet field. When a projectile strikes an object, the projectile will explode and then self-delete. So, our doFire () method first checks to see if this block has a butlet by seeing if the value in the bullet field is still an object. If it is, then we do not yet need to fire another bullet, and the method exits. If there is no current bullet, the method will next check to see if this is a random block. In the case that this block shoots in a random direction, it will get a random value between a and 8 and then continue. Having selected a firing direction (or going with the fixed direction) we now enter a long case statement that shoots a new fireball by calling shootFireBall () and passing in the following information (in this order). Projectile datablock. This is the projectile to shoot. Direction. This is the direction to shoot in. Velocity. This is the velocity we want the fireball to move with. Please note, we will examine the method shootFireball () in Lesson 1120 (Section 11.4.3).

10.5 Dynamic Scripting


This topic isn't a real mind blower, but it is something to remember that you have in your arsenal of TorqueScript options. .
410

Gameplay Scripting

Chapter 10

First, remember that we are working within an interpreter. Furthermore, you should understand that code is evaluated during execution-not beforehand like in C or Java. This means that we can use certain parts of TorqueScript's syntax to build up powerful and flexible scripts that morph over time. r call this dynamic scripting.

10.5. 1 Square Brackets [ ]


In Chapter 4, "Introduction to TorqueScript," we discussed the fact that Torque uses [} to build up strings as follows.
/ /ts25 () ;

%var [OJ '" 10; echo(%varIOj) ;


II same as echo(%varO) ;

The interpreter evaluates statements with square brackets, removing the brackets and replacing our original string with a more compact form. In essence, the square brackets are concatenation operators. Using them, we can concatenate two (or more) strings on the fly, building up a new variable name. Recall that, in the case of multi-dimensional arrays, not only are the elements inbetween the brackets concatenated, but all commas (,) are replaced with underscores C). Consider the following code snippets.
Sa=l; Sb=m; Sc=n; SxISa,Sb,Sc] = 10; echo( Sxl_m_n ); II Prints 10

In this example, we constructed a new name from the composite of the contents of several variables. Notice that the engine inserts "_" for the comma (,) separators. Next. let's try including the dot C.) operator.
$x. [$a,Sb,$cj
=

10; II Gives syntax error

In this example. we try to combine both the dot (.) operator and square brackets, but TGE does not allow square brackets to follow a dot (.) directly. Let's get a bit more creative. .
411

Part III

Game Elements

$x,_[$a,$b,$c] = 10; echo( $x._[$a,$b,$c] ); echo( $x._1_m_n );

II II II

works, but gets 'lost' somehow hmmm ... nothing darn! nothing again

OK, that looked like it should work, but when we tried to print our values using the eXGt copy and what should have been an equivalent, neither worked. Why? Well, the dot operator only works on objects. We fooled TGE into thinking we had an object, but when it did not find an ID in $x, the remainder of the operation went into the wastebasket. Fine, so let's try this with an object.
$x = new simObject(); $x,_[$a,$b,$c] = 10; echo( $x._[$a,$b,$c] ); echo($x. 1 m n);

II II II

works and is retained Yeah! Sweet!

Excellent. Now, we know some ways of creating compound names dynamically on both variables and objects. So, how do we put this to use?

10.5.2 Precedence Operators r J


Square brackets alone can't do it all. Sometimes, we need to use the precedence operators to force the engine to build our variables first. In particular, we are not allowed to follow a closing square bracket with an open curly bracket. Consider the following code.
/ /ts27 () ; %anObject = "ScriptObject"; %obj = new %anObject(); if( isObject( %obj ) ) echo("It is an object. Congratulations!"); else echo("It is NOT an object. Try again ... ");

This just won't work. The interpreter doesn't know that it needs to expand the contents of %anObj ect first. So what about the following?
/ /ts28 () ; %anObject = "ScriptObject"; %obj = new [%anObj ect] () ; if ( isObject ( %obj ) ) echo("It is an object. tongratulaiions!");

412

Gameplay Scripting

Chaprer 10

else echo("It is NOT an object. Try again .. . H); This doesn't work either. It violates the syntax rules for the interpreter. The actual solution is to use the precedence operators.
/ /ts29 () ;

%anObject = "ScriptObject H ; %obj = new (%anObject) () ; i f ( isObject ( %obj ) ) echo("It is an object. Congratulations! H) ; else echo("It is NOT an object. Try again .. . H); Another useful example occurs when we want to dynamically build an object's name. For example if we had three CUI controls named tile_top, tile_ middle, and tile_bottom, we could access fields or methods of these controls as follows. "top"; %name[2] %name [l] "middle" ; %name[Oj "bottom"; for ( %count '= 0; %count < 3; %count ++ ) { %id = ( tile @ "_" @ %name[%countJ ) .get1D(); echo("Tile ", %name[%count], " has 1D H, %id );

10.5.3 eval ()
We still haven't dealt with creating function names on the fly. You may recall in our discussion of ScriptObjects (Section 9.3.2) when I said it is nice to be able to use regularly formatted (versus specialized) names for our functions; i.e., it's better to always call printArea () vs. printCircleArea (), printsquareArea (), etc. The reason we like this is because it reasonably leads us in the direction of building our function names on the fly from known, regular parts. So, to solve the final part of this puzzle, we need to use a special function provided by TCE: eval () . The function eval ( scriptString ) will execute any valid script contained in the string scriptString. This function will execute a string as if it were a script. With the use of TorqueScript's various string-building tools, we can build any function name, variable name, or string of script we please. Then we simply eval () it.
413

---

- - - . _ ,.. _. - -

._---

Part III

Game Elements

/ /ts30 () ; %test = 10; %printTest = "echol\"" @ %test @ "\");"; echo("evall", %printTest, ") produces -->" ); eval( %printTest ); eval I) can be used to create and modify both local and global variables: / /ts31 () ; %makeVarTest = "%newVar = 100;"; echol"evaluating script -->" %makeVarTest); evall %makeVarTest ); echo ("%newVar == ", %newVar );

10.5.4 call ( )
There is one more way of executing functions dynamically in script. This only supports function-style calling, not method-style calling. It isn't as much fun as eval () , but it is very straightforward and useful in a great number of cases. TGE provides a function named call (). call Ifuncname, [argO, ... , argN]) executes the function named in the string funcName and passes the function any arguments provided in argO, ... , argN.
/ /ts32 () ; %tmpVal = 100; calli "echo" , "$", %tmpVal , " for TGE is a good price, Yes?" );

10.6 Basic Client-Server Communications


Although you can, in practice, ignore the client-server divide in the design of a single-player game, if you do and if you try to take that game to a multiplayer environment, you may find yourself reworking great gobs of code. For example, it is easy in a single-player game to write scripts called by the action maps that manipulate server objects and variables. In the following example, we use the key stroke CTRL + W to make the current player playa hand-waving animation. All of this "bad" code might be placed in the "default.bind.cs" file.

Gameplay Scripting
II Bad Implementation of a Wave!!! moveMap.bind(keyboard, "ctrl w", celebrationWave); II ...
function celebrationWave(%val) { if(%val) $Game: :Player.setActionThread("celWave");

Chapter 10

So, why is the above code bad? Let's break it down. The code uses a variable $Game: : Player which we are assuming has the server ID of the player in it. This has the following problems. Action maps are in the client space, so no server variables should be visible, or at least should not be touched, in this scope. The implication of this variable is that there is only one player, which breaks down as soon as there is another player in the game. There is a function celebrationWave () associated with CTRL + W. In and of itself, this is correct. The problem is that this function directly modifies a server object. This is wrong for the same reasons as listed above. So, how do we solve this? Well, before we solve this specific problem, let's first talk in general about how client-server communications work.

Client

-+ Server Commands

Clients communicate with the server by requesting that the server execute a named command. The syntax of this request is as follows.
comrnandToServer( comrnandTag [ , argO, ... , argN ] );

Calling this command (on the client) tells TGE to request that the server execute a console function with the name serverCmd + comrnandTag, using the arguments (if any) that were passed to comrnandToServer () . Regarding the comrnandTag, this can be a string ("xyz") or a tag ('xyz'), but tags are generally preferred. ~ bandwidth savings. A concrete example of this would look like the following. ~
II This method would be defined in one of the script II files that is loaded by the server:
function Player:: Doi t () II do something {

Tags are a feature that Torque uses to save networking bandwidth. Basically, a tagged string is stored locally (by the first sender) and given a unique numeric ID. Then, the first time .the sender transmits this string to a new receiver, it informs the receiver that the string is a tagged string and tells the receiver what that tag ID is. Subsequently, when the sender wants the same receiver to use this 'tagged' string, it only needs to inform the receiver that it is sending a tag and then transmit the tag ID. In general. tags are much shorter than the strings they identify. Thus, using tags for often transmitted strings can produce significant benefits in terms of networking

415

--

---

.-

------------

Part III

Game Elements

This method (likely in a separate file) would also be defined in one of the script files that is loaded by the server: function serverCmdDoit( %client ) ( %client.player.Doit()i

II II II

This command would be executed in function or method defined in a script file loaded by the client: commandToServer( 'Doit' );

II II

When commandToServer ( 'Doi t' ); is called, TGE will instruct the server to call serverCmdDoi t () and will pass in the ID of the calling client. I repeat: the engine automatically passes in the 1D of the calling client. Therefore, all server commands (serverCmd*) must take the client lD as their first argument. Subsequently, the server will execute the function, and the player method doi t () will be executed for the player associated with that client. Please understand that the implication is that the player ID is stored in a field named player in the client connection object (%clienLplayer). We do this in the "game.cs" file (take a look).

Server ~ Client Commands The server uses a similar method for executing commands on the client. The syntax of this request is as follows.
commandToClient( client1D, commandTag [ , argO, ... , argN )
)i

Calling this command (on the server) tells TGE to request that the numbered client execute a console function with the name clientCrnd + commandTag, using the arguments (if any) that were passed to commandToClient () . Regarding the commandTag, this can be a string ("xyz") or a tag ('xyz'), but tags are generally preferred. A concrete example of using commandToClient () would look like the following.

II This method would be defined in one of the script files II that is loaded by the client: function PlayGUI:: OoSomething ( %10, %x, %y ) { II do something
416

Gameplay Scripting

Chapter 10

II This method (likely in a separate file) would also be II defined in one of the script files that is loaded by
I I the client: function clientCmdTellPlayGUIDoSomething( %x , %y ), ( PlayGUI.DoSomething( %x %y);
I

II This sample executes the same function on all clients


II connected to the server: fori %clientlndex = 0; %clientlndex < ClientGroup.getCount(); %clientlndex++ ) ( %someClient = ClientGroup.getObject(%clientlndex); commandToClient(, %someClient I 'TellPlayGUIDoSomething ' %x I %y);

The above example iterates over each client (from the server side) and tells the client to do something with its PlayGUI at the coordinates x, y. In turn, each client executes the method PlayGUI. DoSomething () with those coordinates.

10.6.3 The Takeaway


So, we talked briefly about client-server communication here, but what should you take away from our discussion? Mainly, if you are going be using keystrokes (via action maps) to execute server commands or manipulate server variables, be sure to use the presented methodology. This way, if you decide to make your singleplayer game a multiplayer game in the future, you won't have to go back and fix all of the cases where you violated the client-server divide.

10.6.4 Waving Sample Solution


Below is the solution to our original "waving" problem from above.

II Server-Side Functions:
function serverCmdPlayCel(%client,%anim) if (isObject(%client.player %client.player.playCelAnimation(%anim);

function Player: :playCelAnimation(%this,%anim) i f (%this.getState() !$=- "Dead") %this. setActionThread ("c"el"@%anim);

417

Part III

Game Elements

II Client-Side Functions:
function celebrationWave(%val) iE(%val) comrnandToServer ( 'playCel', "wave");

II MoveMap (client-side) Mapping moveMap.bind(keyboard, "ctrl w", celebrationWave);

10.7 Summary
In this chapter, we discussed the features and classes that Torque provides for enabling gameplay and interaction from scripts. We introduced the idea of callbacks and discussed the most significant (in the context of this guide) callbacks, including when they are called and how they are used. We next learned about the very important feature of event scheduling. We came to understand that we can schedule functions to execute, and console methods to execute upon specific instances of objects. We learned how to track the progress of an event, how to cancel it, and how to repeat it. Next, we talked about string manipulation and filled our heads with the concepts of words, records, fields, tokens, etc. Furthermore, we explored the purpose and usage of each of these concepts and the functions that Torque supplies to work with them. Math is a big part of game writing, and so we discussed scripted math in great depth, discussing all of the most basic and most advanced math features and functions supplied by Torque and available in TorqueScript. Our second-to-last discussion in this chapter explored the edges of scripting and taught us about some tricks and techniques that, if used properly, can create evolutionary and highly functional scripts. Lastly, we dipped into the client-server aspect of the Torque Game Engine. We learned some dos and don'ts when it comes to trading data between client and server. Then, we learned how to send commands from clients to the server and from the server to clients.

418

Chapter 11 Special Effects


Special effects, in the context of this chapter, are those effects that are for the most part visual. We're talking about such things as explosions, debris, particle emitters, splashes, etc. Because each of these objects is unique in some sense, yet similar to each other or used by other effects classes. I thought it best to gather them here. So, there is some logic, even if you do consider it madness to refer to projectiles as special effects. Please note that there is no direct path to discussing these due to their interconnectedness (try drawing the relationship tree some time); thus, this chapter will be alphabetically organized.

11.1 Debris
Debris objects are used to represent the refuse left behind by an exploding or destroyed object. However, this object is versatile enough to be used for various purposes, including a rockfall that blocks the road, the remains of a fallen building, etc.

11. 1. 1 Debris and DebrisData Features


Debris and DebrisData have the following features.

Rendering 2D debris (particle) 3D debris (shape) Physics Bouncing Sliding Falling Velocity limiters Spinning Limited lifetimes Behavior modifiers Subexplosions Bounce off water Replace debris with StaticShape

419

Part III

Game Elements

Modify resting orientation Particle emission Fading away

11. 1.2 Rendering


Debris can be rendered as a 2D or 3D object; depending on our needs.
2D Debris (Particle)
If we are viewing debris from a distance, it will probably be sufficient to use a billboard instead of a shape, which has a higher rendering penalty. In order to create debris using just a billboard (a single texture), we specify our databJock as follows.
datablock DebrisData( 2D_Debris ) ( render2D = true; texture = "path to texture file"; II
} ;

3D Debris (Shape) Of course, if 2D would always cut it, we wouldn't be using a 3D engine, would we? So, for those cases in which an object is needed, we specify a 3D debris datablock as follows.
datablock DebrisData( 3D_Debris) render2D = false; shapeFile = "path to DTS file"; II
};

11.1.3 Physical Properties


Debris can exhibit various random physical properties to give its behavior realism or a required effect.
Starting Radius

420

One of the first questions to answer is, "How far from the explosion point will the debris start?" As you will see in Section 11.3, most explosions take place in the centroid of the shape, and for big shapes, it might be expected that the debris starts some distance away from that point. By default, our debris will start 0.2 world units from this point, but the distance can be greater if necessary.

Special Effects

Chaprer II

datablock DebrisData( startingRadiusDebris ) {

I I ...
useRadiusMass ~ true; II Use defined radius if > 0.2 world units baseRadius ~ 4.0; II Start 4.0 world units from centroid

II
);

Bouncing
It may make sense for the debris from an explosion to bounce a few times. To accomplish this, we need to set a few parameters in our datablock.
datablock DebrisData( bouncyDebris ) {

I I ...
elasticity ~ 0.5; II A little bouncy, but not super-bouncy numBounces = 5; / I Bounce between: 3 and 7 times bounceVariance = 2; II ( numBounces +1- bounceVariance )

II
);

Note that elasticity can only be between 0.0 and 0.99. In addition to bouncing off of solid objects, we can cause debris to bounce off of water. Here, we are telling the engine to add the water type to our collision list.
datablock DebrisData( bounceOffWaterDebris ) I I ... ignorewater = false; II Bounce when we hit water too

II
);

Sliding
It might also be useful for our debris to slide a bit or, alternatively, to arrest

qUickly.
datablock DebrisData ( slidingDebris ) ( I I ... friction = 0.1; II Slide for a long while before arresting II
);

datablock DebrisData( quickArrestDebris )

I I ...
friction = 1.0; II Stop sliding quickly

II
I; 421

Part III

Game Elements

Velocity and Falling


Now having solved where the debris will start and how it will behave when it first hits something, we need to give it some oomph! We need to determine at what rate it is initially moving and decide how gravity will affect it.
datablock DebrisData( highSpeedDebris ) {

II ...
velocity= 20.0; Debris 19.5 velocityVariance 0.5; II terminalVelocity = 30.0 II

II II

II II
) ;

starting velocity of: 20.5 world units/second velocity +1- velocityVariance maximum velocity of 30 world units/second

The above datablock will produce a quickly moving debris effect, whereas the one below will create a slowly moving effect. Additionally, we've set the gravModif ier to a negative value, meaning that the debris will float up instead of falling down.
datablock DebrisData ( lowSpeedFloatUpDebris ) (

II ...
Debris starts with velocity of: 1.5 - 2.5 world units/second velocityVariance = 0.5; II velocity +1- velocityVariance gravModifier = -1.0; II Debris floats UP terminalVelocity = 3.0; II Prevent debris from II accelerating past 3 II world units/second velocity= 2.0;

II II

II
);

Spinning
Debris that maintains the same orientation would be a bit boring, so TGE provides a means of spinning the debris. The spin magnitude can be limited to a specific range of degrees per second, and TGE will randomly select a value in this range.
datablock DebrisData( slowSpinDebris ) {

II ...
minSpinSpeed maxSpinSpeed -60; 60;

II
422
};

Special Effects

Chapter I I

Lifetime

Well, we've gotten to the end of the physical properties list. Now, we have one more decision to make. How long will this debris last? In total, TGE will not allow debris to exist longer than 1000 seconds, but that should be sufficient for most needs.
datablock DebrisData( twoMinuteDebris )

I I ...
lifetime = 240.0; lifetimeVariance

II
=

This debris lasts exactly two minutes 0.0; II lifetime +1- lifetimeVariance

II
};

11. 1.4 Additional Behaviors


Beyond the physical properties, there are a few things we can modify to make our debris really work for us. We can instruct debris to exhibit several behaviors during its lifetime, on last bounce, or at the end of its life.
Explosions

We can cause debris to explode when it achieves maxBounce. And yes, this explosion can make more debris; just be sure not to use the same datablock or you could cause a cyclic explosion that will eventually crash the engine.
datablock DebrisData( explodingDebris ) I I ... explodeOnMaxBounce = true; II Blow up on last bounce explosion = "an explosion datablock"; II
};

Replace Debris with StaticShape

...

Sometimes we don't want our debris to disappear. Perhaps, for our gameplay, we need this debris to build up and remain for the remainder of the mission. Well, this can easily be accomplished.
datablock DebrisData( staticDebris ) {

I I ...
staticOnMaxBounce II
};

true;

II

Do not delete this shape

Because there are several endings for any particular debris, combining other endings with this one, such as explode or fade, may not give you the results you are looking for. Also, accumulating too much debris can kill your frame rates, so you may want to limit debris in some way.

423

I -

Pan III

Game Elements

Fixing Orientation In addition to causing debris to remain in the world. either permanently as a static or by giving it a long lifetime. we may wish for it to be oriented to the surface below it when it comes to rest. Thus, there is a way to tell the engine to correct the orientation of our debris when it achieves maxBounce.
datablock DebiisData( reOrientedD~bris ) (

I I ...
snapOnMaxBounce
=

true;

II

Snap to surface below me

II
) ;

Fireballs, Particle Trails, Etc.

Having just survived an explosion, the fundamental components of this destroyed shape (the debris) may be on fire and/or trailing smoke or dust. We need a way to simulate this. Fortunately. each debris can have up to two particle emitters attached to it. Thus. if we so choose. we can specify two PEDs.
datablock DebrisData( flamingDebris ) { I I ... emitters(O] "FireBall II A PED simulating a fireball. emitters (1] "SmokeTrail"; II A PED simulating smoke.
U ;

II
);

Fading Away
Perhaps appropriately. the last effect we can control for debris is fade. Specifically. if we so choose, we can specify that debris will fade out of sight over the last second of its lifetime.
datablock DebrisData( fadeoutDebris ) {

I I ...
fade
=

true; II Fade out in last second of lifetime.

II
};

datablock DebrisData( poppingDebris ) I

I I ...
fade = false; II Don't fade, II suddenly just pop out of existence

II
};

424

Special Effects

Chapler II

11. 1.5 Using Debris


Debris is used by a number of classes and can also be used standalone; i.e" it is possible to create a standalone debris object.

Used-by Classes
Debris is used by the following classes.
ShapeBaseData. Created when shape transitions to "Destroyed." ShapeBaseImageData. Used to represent ejected shell casings. ExplosionData. Used to represent explosion debris.

Standalone
To create a standalone instance of debris is as easy as the following.
datablock DebrisData( standaloneDebris ) { II Fill in parameters to suit your needs.
};

%myDebris = new Debris(} ( datablock = standaloneDebris; position = "a position vector";


};

Alternatively, if you don't wish to specify position and you would like to give this debris an initial velocity (prior to internally applied velocities), you could use the following code.
%myDebris = new Debris(} ( datablock = standaloneDebris: }: %myDebris.init( "a position vector" , "a velocity vector" );

11.2 Decals
Decals in the context of TGE are temporarily rendered textures that are applied to objects to represent things like footprints, bullet holes, other types of damage, etc. Most properties of decals are controlled by tne objects that use them, but there are a few things we can control.
425

Part III

Game Elements

11.2.1 DecalManager and DecalData Features


DecalManager and DecalData have the following features. Variable timeout Total decal caps Global enable

11.2.2 Decal Properties


For the decals themselves, we can only specify a minimal set of information via the datablock. Specifically, we can specify the size of the decal and the texture it uses.
datablock DecalData ( sampleDecal ) sizeX = 0.25 ; // 1/4 world unit 'wide' sizeY = 0.50 ; // 1/2 world unit 'tall' textureName = "Path to texture file";
};

That is about it. Table 11.1 shows a few other global parameters that are used by the decal manager.
Table 11.1.

Variables used by DecalManager.

:...
>;.

..

,.

NIl....

Delcrfptlon
This is the time a decal lives before self-deleting. It is specified in milliseconds. This is the limit on how many decals may exist at anyone time. Once this limit is passed, old decals are immediately deleted to allow for new decals. This is a global toggle to enable/disable decals.

Semple ...
Range
[ 0 , inf) (default is 5 seconds) 256 (default)

$pref: :Decal: :decalTimeout

$pref: :Decal: :maxNumDecals

$pref: :decalsOn

[ true, false] (true by default)

11.2.3 Using Decals


Decals are used by two classes, ProjectileData and PlayerData. They cannot be created standalone.

426

Special Effects

Chapter II

Used-by Classes

Decals are used by the following classes. ProjectiJeData. Used to specify a 'bullet' mark on collision. PlayerData. Used for footprint(s). See Section 7.3.2, "Player Special Effects," for an example of decals in use.

11.3 Explosions
The concept of explosions hardly needs to be introduced, but a review of the myriad features TGE provides to implement them would be worthwhile. Explosions can be thought of as a composite object and include the following subcomponents: particles, shapes, debris, and lighting. Additionally, the following may be associated with an explosion. camera shake. A nearby explosion can be programmed to shake the client's camera. Sound. A sound can be associated with each explosion. More explosions. Explosions can spawn subexplosions.

11.3. 1 Building up an Explosion


General Control

Ignoring all the components and focusing on the explosion as if it were a single entity, we can control the following elements.
Post-Creation Play Start Time
datablock ExplosionData( delayedFuseExplosion 1 {

I I ...
delayMS
=

4000;

delayVariance

II Play explosion between 3 and 5 II seconds after 1000; II creation. delayMS +1II delayVariance

II
};

In effect, we can delay the beginning of an explosion for a maximum of about 65.5 seconds (65,536, or 216 , milliseconds) after the actual explosion object has been created. The question that arises is, "Why do this?"

427

Part III

Game Elements

To answer that question, we first have to explain how explosions play out. The gross steps an explosion takes are the following. 1. 2. 3. 4. 5. Object created. Explosion event starts. 5ubexplosion objects created. Main explosion'event plays. Explosion ends.

The key thing to notice is that subexplosions are spawned at the same time the main explosion starts to play. So, if we did not have this delay mechanism, all of our explosions would overlap, and that would not be much fun.
Explosion End Time and Play Speed

In addition to specifying a starting time, we can specify how long the event lasts. TGE provides a knob for "scaling" the event.
datablock ExplosionData( longExplosion ) // ... lifetimeMS = 20000; // Play explosion for 19 to 21 seconds. lifetimeVariance = 1000; // lifetimeMS +/- lifetimeVariance //
};

So, what about this scaling business? What use is it if we can control the lifetime? Well, besides being nice for quick tuning, it is also nice to adjust an inherited explosion where the only thing we want to change is the rate it plays at.
datablock ExplosionData( halfAsLongExplosion longExplosion ) { // ... playSpeed 2.0; // Voila, scaled to play twice as fast! //
};

Initial and Subsequent Scaling

Not all explosions are made equal, and over time, the size of an explosion normally evolves. Thus, TGE provides two sets of features. One is for initial scaling.
datablock ExplosionData( humongousExp1osion } { // ...

428

Special Effects

Oklpter l'

explosionScale

5.0; II

Explosion fills a 5-world units cube

II
);

A second feature is for scaling over time. By the way, if you have already looked at the particle emitters description in Chapter 8, "Mission Objects," the follOWing should look familiar.
datablock ExplosionData( resizingExplosion ) (

I I ...
explosionScale = 5.0; sizes[O) "1.0 1.0 1.0"; sizes[l) "1.0 1.0 1.5"; sizes[2] "1.0 1.0 2.0"; sizes[31 "0.1 0.1 0.1"; times [0) 0.0; times(l] 0.33; times[2] 0.66; times (3) 1. 0

II
) ;

The above explosion starts out filling a S-world units cube. It smoothly increases in height until it hits 10 world units (5.0 x 2.0 world units) at twothirds of the way through its lifetime. Scaling then reverses direction and in the remaining third of its life it shrinks to a O.S-world units cube. Poof!

Facing
Depending on the effect we are trying to achieve, an explosion should or should not rotate to face the vjewer. Please note that this rotation is the entire explosion object and not related to the settings applied to the particles.
datablock ExplosionData( faceMeWhenYouExplode ) ( I I ... faceViewer = true; II This explosion rotates to face the camera

II
);

Initial Offset
The last of the basic explosion control mechanisms controls the initial position of the explosion center. Because it would be boring to have subexplosions always forming in the same location, TGE provides a' feature wherein we can

429

Part III

Game Elements

specify an offset, which is then multiplied by a unit-length vector with a random facing. The tip of the resultant vector will be the explosion's center.
datablock ExplosionData( formWithinTwoMeterRadiusExplosion
}

II

...
2.0;

offset

II Explosion will form at a random point II two world units from creation position

II
};

11.3.2 Particles
Now that we've got the basic parameters of our explosion set, we need to choose our particles. TGE explosions support up to five independent panicle emitters. Furthermore, one of these emitters is standalone and four are played together. The single emitter is not treated the same as the four other emitters.

Standalone Emitter
The standalone emitter has two knobs not available for the other four generic emitters. We can control the radius within which this emitter forms from the explosion center, which is similar to the offset principle for the emitter itself. Also, we can select a particle density for this emitter in addition to the controIs provided by the emitter definition itself.
datablock ExplosionData( uniEmitterExplosion ) (

II ...
particleEmitter = "Some PED particleDensity ~ 0.6; particleRadius = 1.2;
U ;

II

I;

The above explosion uses some particle emitter to randomly produce particles whose origin is somewhere within a sphere having a radius of 1.2 world units. This emitter will move continuously over the life of the explosion and has a density of 60 percent.

Those Other Emitters


The other emitters can optionally be used to specify up to four additional emitters whose position is the center of our explosion.
430

- - - ----------

-----

.._-------.-

Special Effects

Chapter 11

datablock ExplosionData( fourEmitterExplosion

I I ...
emitter(O] emitter(l] emitter(2] emitter(3] "Some "Some "Some "Some PED PED PED PED 0"; 1"; 2"; 3";

II
};

11.3.3 Explosion Shape


Alternately, or in addition to particles, we may choose to represent our explosion with a mesh. Furthermore, this shape can be animated. If we so choose, we can create an animation named ambient, which TGE will automatically start when the explosion starts.
datablock ExplosionData( shapeExplosion ) I I ... explosionShape ~ "Path to a DTS file";

II
};

11.3.4 Debris
Now that we know what our explosion is composed of, we can choose to add some debris to liven things up. Our debris is emitted in much the same fashion as particles from a particle emitter. Therefore, these parameters should mostly look familiar.
datab10ck ExplosionData( explosionWithDebris ) { I I ... debris ~ "Some Debris datablock name"; debrisNum ~ 1000; II Between 800 and 1200 debris ejected debrisNumVariance ~ 200; II debrisNum +1- debrisNumVariance debrisThetaMin ~ 0.0; II Straight up, to debrisThetaMax = 180.0; II Straight down debrisPhiMin = 0.0; II Straight down Y, to debrisPhiMax = 360.0; II All the way around (full rotation) debrisVelocity ~ 20.0; II Eject @ between 20 and 30 world units/second debrisvelocityVariance 10.0; II debrisVelocity +1- debrisVelocityVariance II
};

431

Part III

Game Elements

Actually, these controls are a little nicer than particle controls in a way because both phi (left-right) and theta (up-down) can be varied within a range, and the randomness is free.

11.3.5 Lighting Effects


So, what if we want our explosion to emit light? Can we do it? Heck yes. In fact, we can emit a light that changes both color and radius over the lifetime of the explosion.
datablock ExplosionData( lightedExplosion } (

I I ...
lightStartColor = "1.0 1.0 0.8"; II Start off light yellow lightEndColor = "0.6 0.0 0.0"; II End a deep maroon lightStartRadius = 5.0; II Start with a 5-world units radius lightEndRadius = 15.0 II End with a IS-world units radius

II
);

11.3.6 Camera Shake


Finally, we've completed the list of things we'll be seeing. Now let's look into a physical effect. Normally, if a viewer is near enough to an explosion and there is enough energy involved, you would expect the view to shake for a bit as a result. TGE allows us to do this, too.
datablock ExplosionData( rockMeExplosion ) {

I I ...
shakeCamera = true; camShakeRadius = 20.0; camShakeAmp = "1.0 1.0 1.5"; camShakeFreq = "8.0 10.0 8.0" camShakeFalloff = 2.0; camShakeDuration = 3.5;

II
);

432

The above explosion will cause all cameras within a radius of 20 world units to shake. The amplitude of this shaking will be moderate, though slightly stronger in the up-down direction. The oscillation for the shaking will be somewhat weak to normal in the 'y (front-back) direction. What this means is the shaking is stronger up-down but happens faster back-and-forth. Yes, it is weird, but it's an example! Finally, the strength of the shaking will fall off to half its strength at the outer limits and then fall off to zero abruptly. This

Special Effects

Chapter II

shaking will last for about 3.5 seconds from the start of the explosion. Boom! Rumble ... rumble ....

11.3.7 Sound
I know, you may be thinking, "What good is an explosion without sound?" Good, but not great by any means. Fortunately, we won't have to find out. We can specify a sound to accompany our explosion. This sound should probably be a 3D sound, but 2D works for some cases, too.
datablock ExplosionData( soundMeExplosion ) {

I I ...
soundProfile
=

"A sound profile name";

II
};

11.3.8 Subexplosions
That's it, right? I'm thinking, "Why have one of a good thing when you can have more than one?" And so were those canny GarageGames programmers. Each explosion can spawn up to five more subexplosions, which can each spawn five more, and so on. Well, don't get carried away, OK?
datablock ExplosionData( MamaExplosion )

I I ...
subExplosion[O] subExplosion[l] subExplosion[2] subExplosion[3] subExplosion[4] "BabyExplosionO"; II An explosion datablock name "BabyExplosionl"; II An explosion datablock name "BabyExplosion2"; II An explosion datablock name "BabyExplosion3"; II An explosion datablock name "BabyExplosion4"; II An explosion datablock name

II
};

11.3.9 Thinking about Damage


It would be a very strange explosion that did not have some kind of effect, be

it damage or something else. Therefore, TGE supplies a nice console function to calculate how much an object is affected by the explosion. The name is a bit misleading, but basically, the function returns a value telling us how covered by the explosion this shape is. A requirement for this to work is that we specify which shapes can be affected.
%boom
=

new Explosion()

II
};

433

Part III

Game Elements

II Check to see if player got hit %coverage = calcExplosionCoverage( %boom.getPosition() %player, STypeMasks::PlayerObjectType ); if ( %coverage > 0.0 ) echo("Ouch! ouch! OUCh!H);

1 1.3. 10

Usjn~

Explosions

Explosions are spawned in a number of ways and by a number of classes. Nicely, they can be made standalone, too.

Used-By Classes
Explosions are used by the following classes.
ShapeBaseData. Created when shape transitions to "Destroyed." SplashData. Created on precipitation impact. ProjectileData. Yeah, it's pretty obvious. Sure, an explosion would be good for this. DebrisData. Gee. Explosions spawn debris; debris can spawn explosions. It's a vicious circle. ExplosionData. Woohoo! Let's blow it up reeeaaal goooad.

Standalone
To create a standalone instance of an explosion is as easy as the following.
datablock ExplosionData( MyExplosion ) { II Fill in paramters to suit your needs.
}i

%myDebris = new Explosion () { dataBlock = MyExplosion; position = "a position vector H

.,

I;

11.3. 11 Maze Runner lesson # 19 ( 10 Percent Step)-FireBall Explosion


In this lesson, we will examine three databJocks that are supplied with the MazeRunner prototype code. These databJocks are used to implement the explosion that occurs when a projectile (see Lesson #20) explodes. If you look in file "\MazeRunner\prototype\server\scripts\MazeRunner\ FireBall.cs", you will find the following three datablocks.
434

Special Effects

Chapter I I

FireBallExplosionParticle. This datablock defines the particles that are used in the explosion. FireBallExplosionEmitter. This datablock defines the pattern for the explosion emission. FireBallExplosion. This datablock defines the way in which the emitter is played and the effects that the explosion has on the surroundings.

FireBallExplosionParticie
Let's look at the code for this emitter.
datablock ParticleData(FireBallExplosionParticle lifetimeMS = 750; lifetimeVarianceMS = 200; colors[O] = "1 0.2 0.2 1.0"; colors[l] = "1.0 0.6 0.2 0.0"; sizes[O] 1.5; sizes[l] = 3.5;
};

baseSmokePDO ) {

We will first notice that it is inheriting from datablock baseSmokePDO. This is very important for the following reasons. 1. A large variety of effects can be created using a small set of particle textures. 2. The GPGT Lesson Kit comes with a variety of predefined particle datablocks as well as emitters. You should use these as the base (through inheritance or good old cut-copy-paste) for your own particle effects and tweak just the parts that you need. 3. A large variety of effects can be created using a small set of particle textures. Yes, I just said this, but I want to drive the point home. You don't need to go crazy and create a ton of textures. Instead, tweak the datablock fields, and you will be surprised at the number of effects you can achieve. , In this case, we are inheriting a basic smoke particle and then adjusting the fields in Table 11.2.

.....

.........01

-" . *: ;;7l~~Xl}:

Table 11.2.
Fields being adjusted.

lifeTimeMS lifeTimeVarianceMS colors[O] colors[l] sizes[O] sizes[l]

The base particle has a rather long life, but we want our explosion particles to live for a shorter time. We're trying to get a reddish explosion that fades to a dark orange. The particle should start off fairly big and rapidly grow to a little more than double its original size.

435

------

---

Pan III

Game Elements

FireBallExpfosionEmitter
Next, we must define an emitter. In this case, our emitter is new and does not inherit from a base emitter.
datablock ParticleEmitterData(FireBallExplosionEmitter) ejectionPeriodMS 7; periodVarianceMS 0; ejectionVelocity 1; velocityVariance 1; ejectionOffset 0; thetaMin = 0; thetaMax = 60; phiReferenceVel = 0; phiVariance = 360; overrideAdvances = false; particles - "FireBallExplosionParticle";

I;

The above datablock will produce an emitter that will create a large number of particles in a short period. These particles will be ejected at between 1 and 2 world units per second with no offset. The direction of the emitter will vary from straight up to just above horizontal. Additionally, particles will be ejected in a complete circle about the up vector at the point of explosion. Lastly, this emitter uses the particle we just defined.

FireBaffExplosion
This last datablock uses the prior two to define the actual explosion.
datablock ExplosionData{FireBallExplosion) ( lifeTimeMS = 2000; particleEmitter = FireBallExplosionEmitter; particleDensity = 50; particleRadius = 0.2; faceViewer = true; / / Dynamic light lightStartRadius = 0; lightEndRadius = 6; lightStartColor = "1 0.2 1"; lightEndColor - "1 0.6 0.2";

I;

436

This explosion will live for 2 seconds, emitting particles the entire time. It uses the emitter we just defined and limits the number of simultaneous par-

1_-

Special Effects

Chapter I I

tides to just 50 at anyone time. It varies the point of ejection randomly by up to 0.2 world units about the point of explosion. The particles are made to face the viewer at all times, thus making sure that the clouds of particles are always nice and uniform. The explosion will produce light in a radius of 6 world units that starts off reddish and ends a dark orange. Please note that, because the blocks are self-illuminating,. this effect will not be very visible. You may wish to re-export the blocks without self-illumination enabled to see if the effect is more pleasing this way.

11.4 Projectiles
Although the concept of a particle has a very strong tieto weapons, in truth, these objects do not have to be associated with any weapon. Their real value is that they represent an object that can be put into motion and will eventually collide with another object and do something. Yes, it's vague, but that is the point. Projectiles are a versatile object and can be used for many kinds of interactions, not just to represent arrows, bullets, and balls of plasma.

11.4.1 Designing a Projectile


The Beginning As noted above, a projectile is an object "in" the world. It has a starting position. an ending position. and may interact with objects between those two points. Our first focus is on understanding how to get this particle into the world at its starting point.
%bullet = new Projectile() dataBlock = %projectile; initialVelocity : %muzzleVelocity; initialPosition = %ownerObj.getMuzzlePoint(%mountSlot); sourceObject = %ownerObji sourceS lot = %mountSloti
);

This sample is a snippet of code taken from some example code that comes with the CPCT Lesson Kit. As can be seen, it is completed parameterized. The important things to note are the following.
dataBlock. Initialized with some known datablock definition. Quite

standard.
ini tialVeloci ty. The projectile is told its initial velocity

on creation.
437

The implication here is that we can choose any velocity and direction for

-----_._---

._-------

Parr III

Game Elements

this projectile that we want, when we create it. It isn't magically determined by some engine code related to weapons or some such. initialPosition. Normally. we specify a position for objects when we create them, but a new field was added to reduce interdependency, and thus we have initialPosition. This is where our bullet starts. and it, too. can be anywhere we want it to be. sourceObject. As a rule, this should be the player or other entity that is responsible for the creation of this projectile. The main purpose of this field is to give a rendering priority hint to the engine. If the projectile "belongs" to the client's camera, it will get processing priority there. If there is no source object (i.e. this is created standalone), set this to O. SQurceSlot. This should match the slot the firing weapon is mounted to. If this projectile is not associated with a weapon/slot. it should be set to -1. That is it. We've created a projectile and set it on its way. Not very hard and not really interesting. As is often the case, the interesting stuff is embedded in the object's datablock.

The Datablock
Projectiles are fairly flexible, exhibiting a significant set of traits, all of which are configured via the datablock.

Projectile Representation
It is not strictly required, but if we want, the projectile can have an associated

shape.
datablock ProjectileData( HumongoProjectile } ( projectileShapeName "Some DTS file"; scale "20.0 20.0 20.0 H ;

II
l;

In this datablock, we have specified some mesh to represent the projectile and have scaled it 20 times in each dimension.

Shape Animations
If we have chosen to use a shape, we can additionally supply two animations named activate and maintain. The activate thread will play immediately after the shape is created. We can specify this to be a cyclic or a noncyclic animation. If the acti va te thread is noncyclic, and if we have sped438

Special Effects

Chapter I I

fied a maintain thread, the maintain thread will begin playing as soon as the activate thread finishes. The maintain thread can also be cyclic or noncyclic.

Ballistics and Gravity


A projectile may choose to ignore gravity and to follow a nonballistic trajectory, or to add some challenge to aiming. we can play with the way gravity affects our ballistic projectile.
datablock ProjectileData( NonBallisticProjectile )

I I ...
isBallistic
=

false;

II

Not affected by gravity

II
};

dataBlock ProjectileData( steepArcProjectile ) {

I I ...
isBallistic = true; II Is affected by gravity, and ... gravityMod = 3.5; II gravity affects this 3.5x more than normal objects

II
};

Bouncing Around and Arming Delays


It may not always be appropriate for a projectile to do damage right away. It might be nice to create a weapon that can bounce its projectiles off of obstacles for a certain amount of time prior to doing damage. We can accomplish this by making the projectile bouncy and by delaying its activation.
dataBlock ProjectileData( delayedBouncingProjectile ) I I ... armingDelay = 16; II Delay arming for ~1/2 second (16 ticks) bounceElasticity 1.0; II I'm pretty bouncy bounceFriction = 0.5; II Reduce projectile velocity by this factor and II a multiple of the tangent to impact. isBallistic = true; II Only ballistic projectiles can bounce. II
);

If a projectile is not yet armed, it will only bounce if it is ballistic. Nonballistic projectiles penetrate, instead.

439

Part III

Game Elements

Particles Projectiles have the ability to attach up to two emitters to them. However, these emitters play at different times. The rules for their activation are simple. The emitter specified by particleEmi tter always plays when the projectile is not underwater. The emitter specified by particlewaterEmi tter plays when the projectile.is underwater. Neither plays when the projectile is entering or leaving the water. That is a job for the splash object. Having clarified that, the following is how we specify them.
datablock ProjectileData( DualEmitterProjectile ) { I I ... particleEmitter = "Some PED for above water ONLY"; particleWaterEmitter = wS ome PED for below water ONLY"; II
} ;

Lit Projectiles Our projectile can emit a light for the duration of its life. Additionally, we can specify whether the light should be emitted when the projectile is under water and what the color should be for each case (below water or above water). Both cases share the same light radius.
datablock ProjectileData( LitProjectile ) {

I I ...
hasLight = true; lightColor = "0.8 0.8 1.0"; hasWaterLight = true; waterLightColor = "0.8 0.8 1.0"; lightRadius = 4.5; II
);

Explosions Many times. we will want some kind of explosion effect when our projectile is armed and strikes something. Explosions will not happen until the particle is armed. As with particles and light, we have the ability to specify above- and below-water behaviors. However, the relationship for these two explosions are a little different than the prior two effects. Table 11.3 is supplied to clarify which explosion we get based on what explosions are specified and if the projectile is currently underwater or not.

440

l_

Special Effects

Chapter I I

Table 11.3.
N N
y y y y y y

- none -

Explosions above and below water.

Y
N
y

waterExplosion explosion explosion explosion waterExplosion

N N
y y

N
y

Specifying our explosion datablocks works as follows.


datablock ProjectileData( NormalExplodingProjectile ) {

I I ...
explosion = "An Explosion Datablock"; waterExplosion = "An Explosion Datablock";

II
};

Splashes
It was mentioned above that a projectile entering or leaving the water will try

to render a splash, and this is true, as long as one is specified.


datablock ProjectileData( SplashOnWaterStrikeProjectile ) {

I I ...
splash
=

"A Splash Datablock";

II
};

Bullet Holes
It may be the case that we would like the projectile to leave a mark when it explodes. Currently, TGE allows us to make these marks using decals, but only for explosions that happen on interiors or the terrain. Because TGE shapes use simplified collision-detection meshes, it isn't very easy to apply decals to shapes. It can be done but will require some coding. As a bonus, TGE allows us to specify up to six different decals, one of which will randomly be applied to the interior or terrain when the projectile explodes.

441

Part III

Game Elements

datablock ProjectileData( MultiDecalProjectile ) { I I ... decals[O] "Decal Datablock 0"; decals[l] "Decal Datablock 1"; decals[2] "Decal Datablock 2"; decals [3] "Decal Datablock 3"; decals[4] "Decal Datablock 4"; decals [5] "Decal Datablock 5"; II
};

Sound

Although some projectiles are noiseless, it is often nice to have a sound associated with our projectile. Furthermore, if the sound is 3D and the projectile is not too fast, we can get a nice "just missed" effect with a good sound system. Simply specify an audio profile to use, and the projectile will play the sound starting when the projectile is created and ending when the projectile explodes or fades away.
datablock ProjectileData( NoisyProjectile ) { I I ... sound = "An Audio Profile"; II
};

Lifetime and Fading Away

Consider what would happen if all misses kept traveling forever and never got removed. Eventually, the game could have tens of thousands of objects consuming CPU time. Thus, TGE imposes a maximum life for each projectile of 128 seconds Oust over two minutes). The lifetime of a projectile is in ticks 0/32 of one second). Here is a particle that will live for one minute.
datablock ProjectileData( OneMinuteProjectile ) { I I ... lifetime = 32 * 60; I I Live for one minute II
};

Also, because slow-moving projectiles should not just pop out of existence, TGE has a feature that allows us to start fading the particle out of sight after a number of ticks.
442

Special Effects

Chapter r I

datablock ProjectileData( SlowFadeProjectile )

I I ...
lifeTime= 32 * 5; fadeDelay = 32 * 1;

II II

Lives for 5 seconds Starts fading at 1 second (i.e. 4 second fade)

II
};

Inherited and Muzzle Velocities

Recall when I said we (our scripts) are responsible for imparting actual velocities to the projectile? Well, the smart CarageCames programmers provided a couple of standard fields that we can use in our scripts. To specify the projectile's initial (muzzle) velocity, use the following.
datablock ProjectileData( SupaFastProjectile ) { I I ... II Tell scripts to set velocity @ 8000 world units/second muzzleVelocity = 8000.0; II
};

I I I

To specify the velocity that the projectile should inherit from any object it is attached to, use the following.
datablock ProjectileData( FallBehindProjectile ) { I I ... vellnheritFactor = 0.5; II Only inherit half of velocity II
};

11.4.2 Using Projectiles


Only one class has a field for projectiles, and that is only so that TCE can optimize for state-machine transitions. That class is the ShapeBaselmageData class. You don't need to specify a projectile, but if you are using one, it is a good idea, as this will help avoid rendering hiccups while the weapon is fired.

Standalone
Because projectiles are always created by scripts, it is our responsibility to initialize all pertinent parameters for them. If you find this confusing, you should refer to the code for the CPCT Lesson Kit's Projectiles Lesson. Because I know you're just dying to see some code, here is a truncated version of the code from the CPCT Lesson Kit for a standard projectile weapon (ihis code was derived from the standard TCE SDK crossbow script).
443

Part III

Game Elements

function EGWeaponlmage::onFire( %imageDB , %ownerObj , %mountSlot ) { %projectile = %imageDB.projectile;

II Determine initial projectile velocity based on the II gun's muzzle point and the object's current velocity %muzzleVector'= %ownerObj.getMuzzleVector(%mountSlot}; %objectVelocity = %ownerObj.getVelocity(}; %muzzleVelocity = VectorAdd( VectorScale(%muzzleVector, %projectile.muzzleVelocity}, VectorScale(%objectVelocity, %projectile.vellnheritFactor)}; II Create the projectile object %bullet = new Projectile () { dataBlock = %projectile; initialVelocity = %muzzleVelocity; initialPosition = %ownerObj.getMuzzlePoint(%mountSlot}; sourceObject = %ownerObj; sourceS lot = %mountSlot; client = %ownerObj.client;
};

MissionCleanup.add(%bullet}; return %bullet;

11.4.3 Maze Runner Lesson #20 (90 Percent StepJ-The FireBall


In this lesson, we will examine three of the six datablocks that are supplied with the MazeRunner prototype. These datablocks are used to implement the projectile representing the fireball. If you look in the file U\MazeRunner\prototype\server\scripts\MazeRunner\ FireBall.cs", you will find three datablocks.
FireBallParticie. This datablock defines the particles that are used for the projectile's trail. FireBallEmitter. This datablock defines the pattern for the trail. FireBallProjectile. This datablock defines the projectile itself and uses the above two datablocks as well as the three we discussed in Lesson #19 (Section 11.3.11) (FireBallExplosionParticle, FireBallExplosionEmitter, and FireBallExplosion), which are used for the explosion.

444

Special Effects

Chapter II

FireBaliParticle
Again, we have chosen to implement our particle datablock by using inheritance, but this time many parameters have been modified.
datablock ParticleData(FireBallParticle : baseSmokePDO ) { dragCoeffiecient = 0.0; gravityCoefficient 0.0; inheritedVelFactor = 0.0; lifetimeMS = 350; lifetimeVarianceMS spinRandornMin spinRandomMax colors[O] colors[l] colors[2] sizes[O] sizes[l] sizes[2] times[O] times[l] times[2]
};

50;

-30.0; 30.0;

"1 0.7 0.7 1.0"; "1 0.7 0.7 1.0": "1 0.7 0.7 0"; 0.5; 0.7; 1. 0; 0.0: 0.3: 1. 0;

The particles this produces will not be affected by drag or by gravity, nor will they inherit any velocity from the emitter. This means that they will just hang in the air where they are produced. They have a pretty long lifetime, between 300 and 400 milliseconds. As they hang in the air, they will spin back and forth between minus 30 and 30 degrees. LastJy, the smoke will start as medium sized off-white puffs and end as large gauzy white puffs.

FireBaliEmitter
The emitter datablock is fairly short because it doesn't have a lot to do for smoke trails.
datablock ParticleErnitterData(FireBallEmitter) ejectionPeriodMS 20; periodVarianceMS 5; ejectionVelocity 0.25; velocityVariance 0.10; thetaMin ~ 0.0:

445

---------

_. - - - - - - - - - -

Part III

Game Elements

thetaMax= 180.0; particles = FireBallParticle;


};

This emitter will produce a new particle every 15 to 25 milliseconds, meaning that the trail may be a little spotty (the projectile is moving at 20 world units per second if you will recall from Lesson #18 (Section 10.3.7)). The particles themselves have very little velocity when ejected, and they are all ejected between straight up and straight down (we could make this range smaller to create a more narrow trail). Lastly, the emitter uses the particle datablock we just discussed. FireBaliProjectile This datablock brings all of the work in the prior lesson and this one together to create the fireball.
datablock ProjectileData(FireBallProjectile) projectileShapeName = "-/data/MazeRunner/Shapes/Projectiles/projectile.dts explosion = FireBallExplosion; particleEmitter = FireBallEmitter; armingDelay = 0; lifetime = 5000; fadeDelay = 4800; isBallistic = false;
};

U ;

This particle uses a mesh that is provided with the GPGT Lesson Kit. It is nothing more than a very small elongated pyramid with a simple texture applied (Figure 11.1). It uses the explosion datablock and the (smoke trail) emitter defined above. There is no arming delay, so the Figure 11. 1. projectile will explode as soon as it Fireball projectile. strikes an object. The projectile will live for 5 seconds and begin to fade at 4.8 seconds. At the end of its lifetime, it will automatically be deleted if it has not already impacted upon something. It is nonballisitic and will travei in a straight line along the path on which it is fired. .
446

Special Effects

Chapter II

shootFireBall()
We deferred our discussion of the fireball-shooting method until this chapter so we would have the proper context. The main thing to understand is that, when we create a projectile and put it into the world, it starts with an instantaneous velocity and direction (as specified at creation time).
function StaticShape: :shootFireBall( %marker, %projectile , %pointingVector , %velocity) %bullet = new Projectile(} dataBlock = %projectile;

initialVelocity = vectorScale( vectorNormalize(%pointinqVector) , %veloci ty ); initialPosition = %marker.getworldBoxCenter()i


sourceObject = -1; sourceS lot = -1; theMarker = %marker;
};

%marker.bullet = %bullet; MissionCleanup.add(%bullet) ;

The most important things to see in the above code are the following. 1. The initial velocity is a combination of a direction and a magnitude. 2. The projectile can have any initialPosition. and we are choosing the centroid of the fireball block. This is important, because it demonstrates that collision detection only occurs for penetrations of a collision mesh, not for objects or rays leaving the mesh, as is the case with this projectile.

11.5 Sounds
TCE supports both 2D and 3D sounds. Standard TCE uses OpenAL for sound support, but resources have been written on how to use other libraries like FMOD. Sound is an area in TCE that, at first, may seem difficult, but in the end turns out to be simple and well organized. All TCE sound is supported via three mechanisms. Audio descriptions (ADs) Audio profiles (APs) Console functions 447

Parr III

Game Elements

Additionally, there are two defunct features (which could be made to work with some love). AudioSampleEnvironment AudioEnvironment

11.5. 1 Sound Dimension


For simplicity, sounds are often described as being either 2D or 3D. Now, both 2D and 3D sounds are 3D in the sense that, when they are played, the user's gaming setup will attenuate and otherwise modify them. The actual distinction being made here is how the sounds will be calculated and treated prior to being sent to the speaker(s).
2DSounds

These are sounds that have no apparent source. Their gain is not attenuated by position or orientation. Some sounds with this dimension are: menu and interface feedback sounds, intra music, background music, and global environmental sounds (wind. thunder, rain, etc.).
3D Sounds

These are sounds with a specific source. Therefore, their gain is attenuated by position or orientation as related to the listener. Furthermore, if advanced features are enabled. 3D sounds can be attenuated and modified by the environment, occlusion, etc. A small sampling of sounds with this dimension are: player footfalls, vehicle noises, weapon noises, and local environmental sounds (waterfalls, rivers, surf, birds in a stand of trees, etc.)

11.5.2 AudioDescription and AudioProfile


Throughout the scripts, you will find datablock fields and other bits of code that take either an AudioProtile and/or an AudioDescription. The purpose of each of these is to encapsulate sound-specific data so it doesn't have to be explicitly stated later. In other words, by using the AP/ AD (AudioProfile/ AudioDescription) mechanism, we simplify our life just like when we use datablocks.

448

Special Effects

Chapter II

AudioDescription
The job of the AudioDescription datablock is to define how a sound plays. It answers the following questions. Is the sound 2D (it doesn't attenuate), or is it 3D? If the sound is 3D, what kind of sound cones does it have? (See Section 8.7 .. "Audio Emitters. ") Does the sound loop? If it loops, how many times does it loop and at what intervals does it repeat? What is the maximum gain for this sound? What channel does it play on?

AudioProfile
The job of the AudioProfile datablock is to define what sound is played. It answers the following questions. What sound (file) is used for this sound? Should this sound be preloaded? Preloading is useful for sounds that would take a long time to load from disk or otherwise might cause a discernible listening gap if not already in memory. What AudioDescription does this sound use?

11.5.3 Sound Channels


All TGE demos and kits come with certain sound channels dedicated to certain tasks. It is best and easiest to not change the ones that exist, but instead to add a new channel if needed. The TGE channels are as follows.
SDefaultAudioType. Channel O. SGuiAudioType. Channell (dedicated to Gut sounds). SSimAudioType. Channel 2.

11.5.4 Using Sound


Because the AuclioEmitter mission object uses all the same concepts, we will not be reviewing the parameters for either ADs or APs in depth. Instead, a summary is provided in the appendix, and if this is insufficient, a review of Section 8.7, "Audio Emitters," should clarify things. For now we'll restrict ourselves to discussing standalone usage. TGE provides a complete set of OpenAL functions for playing and manipulating our sounds.
449

Parr III

Game Elements

11.5.5 new versus datablock for Profiles/Descriptions


Sometimes, when looking at the examples. you will see audio profiles and descriptions created using the new keyword, and other times using the datablock keyword. This may seem arbitrary at first, but it is not. An AudioProfile or an AudioOescription object created with the new keyword is nonnetworkable. In other words, these objects cannot be used to play sounds on remote clients. An AudioProfile or an AudioOescription object created with the datablock keyword is networkable. In other words, the server can play sounds using these on remote clients. Our focus in this guide is on the single-player usage of Torque, but because the new vs. da tablock distinction is important to understand early, we will take the time now to look at some examples. In fact, why don't we use a lesson to clear up any confusion on the distinction between new versus datablock.

11.5.6 Maze Runner Lesson #21 flO Percent StepJ-Game Sounds


In this lesson, we will examine the different methods available to create AudioDescription and AudioProfile objects. This work will subsequently be used in Section 14.7, "Finishing the Prototype," to add sound to our game interfaces and game world. For our game, we will need AudioDescriplions and AudioProfiles to play the following sounds. Splash screen music. We'd like to add some music to our splash screen when it is shown. This is a nonnetworked nonlooping 2D sound. Button-over and button press sounds for main menu. We want our buttons to provide feedback when the mouse hovers over them and when we click on them. These are both nonnetworked nonlooping 20 sounds. In-game music. We'd like some background music while playing our game, preferably an ambient loop of some sort. This is a nonnetworked looping 20 sound. Fireball firing and explosion sound. It doesn't make much sense for our fireball blocks to shoot a fireball silently, and the explosion when the fireball collides with something should not be silent, either. These are both networked nonlooping 3D sounds.

The Audio Descriptions .


450

In order to create audio profiles, we need to create audio descriptions first. Why? Because, the AudioProfile object uses the AudioOescription object.

Special Effects

Ch-"lprer II

In our list (above), we have three non networked nonJooping 2D sounds, one nonnetworked looping 2D sound, and two networked nonlooping 3D sounds. In total, this equates to a requirement for three different audio descriptions.

Nonnetworked Nonlooping 20 Audio Description


new AudioDescription( MazeRunnerNonLooping2DADObj ) ( volume 1.0; isLooping false; is3D false; type $GuiAudio~ype;
};

Using the new keyword, we have created an instance of AudioDescription descriptively named MazeRunnerNonLooping2DADObj, An audio profile using this description has the following attributes. Is non networked. It is a normal object, not a datablock. Plays at full volume for the channel the sound is using. Is nonlooping. Is not 3D. Is assigned to the $GUIAudioType channel and will thus be attenuated by changes to that channel.

Nonnetworked Looping 2D Audio Description


new AudioDescription( MazeRunnerLooping2DADObj ) ( volume 1.0; isLooping true; loopCount -1; is30 false; type $GuiAudioType; I;

Using the new keyword, we have created an instance of AudioDescription descriptively named MazeRunnerLooping2DAOObj. An audio profile using this description has the following attributes. Is nonnetworked. It is a normal object, not a datablock. Plays at full volume for the channel the sound is using. Is looping. Loops infinitely (we assigned -1 to loopCount, but we could have left it unspecified, as well, since the default value is -1). Is not 3D. Is assigned to the SGUIAudioType channel and will thus be attenuated by changes to that channel.

451

Part Ifl

Game Elements

Networked Non/ooping 3D Audio Description datablock AudioDescription( MazeRunnerNonLooping3DADDB ) { volume = 1.0; isLooping = false; is3D = true; ReferenceDistance = 2.0; MaxDistance = 20.0; type = $SimAudioType;
};

Using the da tablock keyword, we have created an instance of AudioDescription descriptively named MazeRunnerNonLooping3DADDB. An audio profile using this description has the following attributes. Is networked. It is a datablock. Plays at full volume for the channel the sound is using. Is nonlooping. Is 3D. Plays at max volume between 0 and 2 world units and attenuates to nearly zero at a distance of 20 world units from the source position of the 3D sound. Is assigned to the SSimAudioType channel and will thus be attenuated by changes to that channel.

The Audio Profiles


Now that we have our three audio descriptions, we can create our audio profiles. In this case, we need one each for the sounds, but since several of these sounds, are similar execept for the sound file played, we will only examine one from each category.

".

The Non/ooping GUI Sounds (SplaSh Screen and Buttons) new AudioProfile(MazeRunnerGGSplashScreen) ( filename "-/data/GPGTBase/sound/gui/GGstartup.ogg"; description = MazeRunnerNonLooping2DADObj;
);

Using the new keyword, we have created an instance of AudioProfile named MazeRunnerGGSplashScreen. This audio profile will be used when the GarageGames splash screen is shown and has the following attributes.
It plays the GarageGames startup sound from the demo kit. (This sound file was renamed to GGStartup.ogg from startup.ogg and included with GPGT base data for your use).
452

SpeciarEffects
It uses our nonlooping 2D AudioDescription object MazeRunnerNonLooping2DADObj.

Chapter) I

The Looping

Gur Sound (In-Game Music)

new AudioProfile(MazeRunnerLevelLoop) ( filename "-/data/GPGTBase/sound/gui/levelLoop.ogg"; description = MazeRunnerLooping2DADObj; I;

Using the new keyword. we have created an instance of AudioProfile descriptively named MazeRunnerLevelLoop. This audio profile will be used for in-game music and has the following attributes.
It plays a short ambient loop provided on the accompanying disk.
It uses our looping 2D AuclioDescription object MazeRunnerLooping2DADObj.

The Networked Sounds (Fireball Firing and Explosion)


datablock AudioProfile(MazeRunnerFireballExplosionSound) ( filename = "-/data/GPGTBase/sound/GenericExplosionSound.ogg"; description = MazeRunnerNonLooping3DADDB;
);

Using the datablock keyword, we have created an instance of AudioProfile descriptively named MazeRunnerFireballExplosionSound. This audio profile will be used for the sound effect attached to a fireball explosion and has the following attributes.
It plays a generic explosion sound that is included on the accompanying disk for your use. This sound is derived from the file "Crossbow_explosion.ogg" found in the TGE Demo.
It uses our nonlooping 3D AudioDescription datablock MazeRunnerNonLooping3DADDB.

Using The Audio Profiles


All of the above audio descriptions and audio profiles are provided on the accompanying disk. We will be using them later when we follow the instructions in Section 14.7, "Finishing the Prototype." However, the question of use should at least be addressed. How does one use these new sounds? The sounds we created are used in three ways. 1. Attached to a GUI control. The button-over and button-press sounds above will be used by a CUI button control. As you will see in Chapter 12, this attachment is achieved using GUI profiles.
453

Part III

Game Elements

2. Attached to a special effect. Our explosion sound is used by the explosion object. As we saw in Section 11.3, "Explosions," we can assign an AudioProfile datablock to the ExplosionOata soundProfile field. When an explosion is created with this datablock, it will automatically play the sound specified by our AudioProfile datablock.
datablock ExplosionData( FireballExplosion ) {

I I ...
soundProfile

= MazeRunnerFireballExplosionSound;

II
};

3. Played manually. Lastly, we can play sounds manually. We simply call alxPlay () and pass it the name or ID of a nonnetworked 20 sound AudioProfile.
II Play the GG Splash Screen Sound alxPlay( MazeRunnerGGSplashScreen );

11.6 Special Effects Summary


This short chapter was dedicated to discussing a set of classes that have no true home but by their nature define or enable a variety of special effects. These classes included the following.

Debris. The detritus left over by an explosion. Decals. Textures applied to surfaces to give the impression of bullet holes, scorch marks, footprints, etc. Explosions. A special effect dedicated to pyrotechnic displays and interactions with the camera that provide a convincing effect. Projectiles. A shape not only used to represent the output of weapons, but that can be used for a variety of other effects. Sounds. AudioProfiles and AudioOescriptions, used for networked vs. nonnetworked sound.
In each discussion, we summarized the features provided by the individual

class, how to use the class alone if possible, and how the class interacts with other TGE special effects or other classes.

454

'--

Chapter 12 Standard TGE GUI Controls


12. 1 Standard GUls
In this chapter, we will take a look at what is required to make use of several standard TGE GUI controls. We will not discuss the usage of every GUI control provided in TGE but will instead restrict ourselves to discussing the commonly used ones. Specifically, we will discuss the following controls. Windows, containers, and panes GuiControl GuiFrameSetCtrl GuiScrollCtrl GuiStackControl GuiPaneControl GuiTabBookCtrl GuiTabPageCtrl GuiWindowCtrl Backgrounds and borders GuiBitmapBorderCtrl GuiBitmapCtrl GuiChunkedBitmapCtrl GuiFadeInBitmapCtrl Text GuiMessageVectorCtrl GuiMLTextCtrl Buttons GuiButtonBaseCtrl GuiBitmapButtonCtrl GuiButtonCtrl GuiCheckBoxCtrl GuiRadioCtrl Menus GuiMenuBar GuiPopupMenuCtrl Sliders and Scales GuiFilterCtrl GuiSliderCtrl GuiTextEditSliderCtrl Miscellaneous GuiCursor GuiDirectoryFileListCtrl GuiDirectoryTreeCtrl GuiInputCtrl GuiMouseEventCtrl GuiTreeViewCtrl

GuiMLTextEditCtrl GuiTextCtrl GuiTextEditCtrl GuiTextListCtrl

However, before we leap into the examples, let's take some time to familiarize ourselves with some GUI basics.

455

Part III

Game Elements

12.1.1 Interfaces versus GUls


For the sake of clarity, I will be using four terms while discussing GUIs. The first term is interface. When I use the term interface, I mean an entire game interface, such as a main menu, a help dialog, etc. An interface is composed of one or more CUI elements. The other three terms I will use are CUI, control, and CUI element(s). I will use each of these interchangeably to keep the discussion from being too dry. Each of these terms refer to any single CUI class which mayor may not contain other controls. For example, a GuiBitmap, a GuiButtonCtrl, and a GuiScrollCtrl are all GUI elements, whereas an interface might be composed of all three of these, plus additional GUI elements.

12. 1.2 The Canvas


Since days of old, when working with user interfaces, it has been common to refer to the base interface's layer as the canvas. All GUI controls are stacked (placed in) the canvas. Torque supports a single canvas named, intuitively, Canvas. The canvas can display two generalized categories of interfaces: 1. Dialogs. 2. Everythjng else. In most respects, a dialog is not different from other controls, but it is treated differently. We will discuss the why and the how of this shortly.

Current Canvas Content


All nondialog interfaces are only displayed if they are the current content of the canvas. Furthermore, the canvas only has one content at a time. In order to set an interface as the contents of the canvas, we write a statement like the following.
Canvas.setContent( myCoollnterface );

In this example, we are making an interface named myCoolInterface the new (and thus the current) content of the canvas. This unloads the current content and replaces it with myCoolInterface.

Dialogues and Layers


Most of the time, exchanging the current canvas content is what we want. However, occasionally, we would like to retain the current canvas content while we temporarily display another interface over the current one. What we're talking about is a dialog. In order to display a dialog, we do the following.
456

Standard TGE GUI Controls

Chapter 12

Canvas.pushDialog( myCoolDialog / 1 );

In this example, we are pushing an interface named myCoolDialog onto Layer 1 of the canvas. The current content of the canvas is retained, as well as any interfaces already pushed onto any canvas layers. This method allows us to have as many interfaces open as we need. Note that, if no layer is provided as the second argument to pushDialog (), the dialog is pushed onto the default layer, Layer O. Later, we can pop a dialog in three ways.
Canvas.popDialog( ); II or Canvas.popDialog( myCoolDialog ); II or Canvas.popDialog( 1 );

The first popDialog () will pop the last interface that was pushed, which in this case would be myCoolDialog. The second popDialog () will do a lookup on myCoolDialog and pop it if it is found. The third and last popDialog () will pop all interfaces in Layer 1. I repeat, all interfaces in Layer 1. This is a nice way to pop multiple stacked dialogs at the same time.

Replacing the current content of the canvas does not affect dialogs. Dialogs are content that float over the canvas's current contents.

Canvas Extent vs. Screen Size


The canvas is in effect a boundless entity that extends beyond the visible screen. The "0 0" coordinate of the canvas is merely a reflection of the "starting" position of the screen (upper-left corner), and the extent is a reflection of the width and height of the screen. It is completely legal to position GUI elements outside the visible bounds of the screen. In fact, this is true of all controls, not just for the canvas. All controls will clip the parts of their children that are outside the control's own visible bounds.

12. 1.3 The Structure of a .gui File


Each interface that we make the content of the canvas and each interface that we push onto the canvas is a separate entity. For instance, we may have any of the following interfaces: splash screen, main menu, credits, settings dialog(s), help dialog(s), play GUI, etc. Each of these interfaces exists individually as a hierarchy of GUI controls, stored in a separate .gui file. The .gui files can be created by hand, by script, or with the GUI editor. The Torque standard is to have one interface definition per .gui file, and the general organization of such a file is as follows.
457

------_.

--

Part III

Game Elements

1. An optional block of code. 2. The definition of the interface between two comment lines.

11--- OBJECT WRITE BEGIN --11--- OBJECT WRITE END --3. A second optional block of code. A GUI file with just the sections delimited would look like this:
II Optional code block #1
//--- OBJECT WRITE BEGIN ---

II Interface definition
//--- OBJECT WRITE END II Optional code block #2

The lines in bold are optional.


Optional Code Block #1

This optional block of code can be added by hand after generating and saving an interface file, using the GUI editor. Normal bits of code that go here are: GUI profile(s) used in subsequent GUI definitions, onAdd () callback definitions for subsequent GUI elements, and miscellaneous code and global variables.
Interface Definition

This required block is generated by the CUI editor or by hand. If generated by the GUI editor, it will be delimited by two (optional) comment lines and look something like the follOWing.
//--- OBJECT WRITE BEGIN new GuiChunkedBitmapCtrl( parentGUI ) { horizSizing = "width"; vertSizing = "height"; position = "0 0"; extent = "640 480"; new GuiControl( childGUI } { I I ... new GuiTextCtrl( grandChildGUI) II I; (

"

458

- - - -.... _ - -

Standard TGE GUI Controls

Chapter 12

II
};

II
};

//--- OBJECT WRITE END ---

The comment lines (highlighted) allow the CUI editor to find the interface definition and preserve the optional codeblocks surrounding it in the case that we later reload our CUI with the editor and edit it. Yes, both code blocks will be preseroed. If we look at the above example skeleton, we will see that there is one parentGUI (named this way for the sake of the example) which can then have child CUIs, grandchild CUIs, etc., inside it. We'll talk more about the design of the CUI definition shortly, but let's first address the second code block.

Optional Code Block #2


We can optionally-hand edit the file and add a second block of code after the interface definition. Normal bits of code that go here are:
onWake () , onSleep () , and on Remove () callback definitions as well as any other callbacks that might be associated with the prior CUI elements, and miscellaneous code.

OK, we're doing well. We know a little bit about the canvas, and we understand the structure of a .gui file. Now, let's talk about the general structure of an interface.

General Design of Interfaces


When building an interface, I suggest using the following steps.
1. Select a control to be the base container for this interface. Cood choices are CuiControl, CuiBitmapCtrl, or CuiChunkedBitmapCtrl (among others). 2. Position the base CUI at "0 0" and make the extent equal to that of the canvas.
3. Use a horizSizing of "width" and a vertSizing of "height". This

last step is very important because we want our base CUI to cover the entire visible screen. The extent is not so important as the horizSizing and the vertSizing. (The CUI editor will automatically do this and the prior step for you when you create a new interface: File -7 New CUI). 4. Now, add all other CUI elements you wish to have into your selected base CUI.
459

Parr III

Game Elements

new GuiChunkedBitmapCtrl(MyCoolInterface) I I ... horizSizing = "width"; vertSizing = "height"; position = "0 Q"; extent = "640 480"; I I ... new GuiWindowCtrl() II
) ;

II
new GuiWindowCtrl() II
}; };

WelL we've gone on for a bit now, and I haven't told you what makes a nondialog different from a dialog. The short answer is nothing. Yes, that's right. In theory, there is no difference between a nondialog and a dialog except the way we choose to display them. In practice, however, there is usually one more important difference-how they capture inputs. To understand this, we need to explore how GUIs capture inputs in general. In order to understand how inputs are captured by GUI elements, we need to explore the following concepts: layers, first responders, focus, and modality.
How a GUI Captures Inputs

Layers
Unfortunately, the term layers has been and is used regarding dialogs. We are not currently discussing dialog layers. Instead, we are discussing the more general concept of layering. The canvas can be considered to be the bottom layer of the control stack. Each visible control is stacked onto this canvas, making a "layer." Those controls on the bottom are rendered first, and those on the top are rendered last. Thus, at the end of any rendering cycle, the topmost GUI controls will have rendered over all other elements below them, properly occluding and masking them. Now, recall our discussion from Section 9.5 regarding I/O processing order. Input events are passed from the operating system to the Torque platform code layer, which then passes the inputs to:

460

Standard TGE GUI Controls

Chapter 12

1. the ClobalActionMap, then to 2. the Canvas, then to 3. any active (nonglobal) ActionMaps. Imagine that the the mouse input events (the ones not captured by the ClobalActionMap) are like marbles falling onto our interface. Each marble will fall from the location of the cursor and hit the first ,CUI it encounters. This is the first CUI that will be given an opportunity to capture and to use the mouse event. If a control does nothing with the event, it can either allow the marble to "fall through" until a CUI lower in the stack finally uses the event, or the event can immediately be sent to the ActionMap stack. For a modeless control (we will define modality below), the event continues to fall through. For a modal control, the uncaptured event is passed directly to the ActionMap stack. Notice that I did not s'ay modal interface. Cenerally, you can consider an interface to be modal if any CUI in the interface is modal, but in practice the best way to do this is to make the base layer of the interface modal and to allow all the higher layers to behave normally (Le., be modeless). Keyboard events are a little more tricky. Because there is no parallel to the "mouse pointer location" idea, we need to discuss a new concept.
First Responders

Because a keystroke comes from no specific physical location, there needs to be a mechanism that tells TCE which CUI to send the keystroke to. This concept is called first responder. Some controls will automatically become first responder, but sometimes a control needs a little help (or discouragement). For example, in the case where there are two controls that are on the same layer and both want to be the first responder, the question arises, "Which of these will be first responder?" The first responder will be:
1. the control that was first responder on the last processing pass, or

2. the control that is made first responder by the method makeFirstResponder () , or

If you are using version 1.4 or later of the engine, this discussion does not apply. The concept of first responder has been deprecated in lieu of a more standard focus-based system. So, if you are not using version 1.3 or prior. skip ahead to "Focus."

3. the control that is made first responder as the result of a mouse-click or TAB transition. Note that some controls will take back the first-responder role even when another control has been clicked.
Focus

A CUI can have what is called focus. This term implies that the control is visible and active. However, the main thing to know about focus is that the

461

Part III

Game Elements

control that has focus and is first responder will be the one to receive keyboard inputs. Mouse movements and clicks can change the current focus, so how do we force a CUI to retain focus regardless of the mouse position/action?
Modality

If you are using version J.4 or later of the engine, this discussion does not apply. The concept of modality has been deprecated in lieu of a more standard focus-based system. So, if you are not using version 1.3 or prior, skip this.

The fourth and filial concept we need to wrangle with is modality. Modality is usually discussed in the same breath as dialogs, but it is a term that can be applied to any control. Namely, a control can be modeless, or it can be modal. Furthermore, a modeless CUI does not attempt to hold onto the focus. It will freely give up the focus to whatever other CUI wishes to take it. The modal CUI is less friendly, however, and once it has the focus, it does not relinquish it until its purpose is served and it chooses to release focus. All controls are modeless by default, although some do actively seek to attain first-responder status (CuiTextEditCtrl, for one), which is not the same as being modal. We can retain focus either by: 1. making our CUI modal (not very friendly and not suggested unless truly necessary) , 2. covering all other CUIs such that they do not have the possibility of getting focus (we can easily do this by placing the CUI control that we want to have the focus in a CUI control that covers the entire canvas), or 3. forcing first-responder status by using the makeFirstResponder () method call. Please note that sometimes you need to make a control be first responder, and sometimes you need to force a control to not be first responder. Wow! We have come a long way. We now have at least a passing understanding of some CUI concepts. Still, we have a way to go before discussing individual CUI elements. Now, let's talk about some more advanced topics.

12.2 GUI Profiles


Similar to the concept of datablocks for shapes, we have CUI control profiles (CuiControIProfile) for CUIs. These are unique objects that are instantiated on the client and used repeatedly in the creation, initialization, and use of CUIs. They save us having to constantly redefine common attributes on a CUI-byCUI basis. Like datablocks, they provide a single location from which to draw common attributes, but this space is not static (like a datablock).

462

Standard TGE GUI Controls

Chapter 12

The syntax for a CuiControlProfile is as follows.


new GuiControlProfile ( GuiProfileName [ : parentProfile I ) { field 0 value; field N value; value; I

[dynamic field_N
};

Like datablocks, each CuiControlProfile is expected to be unique; thus, creating a second profile with the same name as a prior one wilJ in effect override it. However, to be safe, always delete a profile if you are going to redefine it. Also, do not delete a profile that is currently in use, or you will crash the engine. Like datablocks, we can inherit (copy) from a previously defined profile if we so choose. Not all fields that can be defined in a CuiControlProfile are used by every CUI control, nor are they all used in the same way. We are, of course, free to add our own dynamic fields to any profile at any time.

12.2. 1 Visual Attributes of GUI Control Profiles


As you would expect, the majority of the fields in CUI control profiles are for enabling and/or modifying visual aspects of a CUI. It should be said once more that not all of these values are treated equally between CUIs, and experimentation will be necessary for controls not documented here. However, after reading the remainder of this chapter, you should have a reasonable idea of what to expect when you use these fields.
Bitmap

There are several controls that use a bitmap. Thus, it makes sense that the bitmap should be specified here. This simplifies CUI creation and easily allows us to have controls in different places all using the same graphics file. Unlike bitmaps used elsewhere in Torque, CUI bitmaps may have any reasonable dimension and need not be sized as a power of two.
new GuiControlProfile ( usesABitmapProfile ) { II No need to specify suffix bitmap "./some_path/somebitmapname";
};

463

Parr li'l

Game Elements

Borders
All controls can have a border. The border parameters in a GuiControlProfile are as follows.
border. This integer value specifies the control-specific border type, of

which there are ~p to five possibilities: O-disabled, and 1, 2, 3, 4-control-specific implementation.


borderColor. A three-value integer vector containing the RGB colors for

a normal border.
borderColorHL. A three-value integer vector containing the RGB colors

for an "is highlighted" border.. borderColorNA. A three-value integer vector containing the RGB colors for a "not active" border. borderThickness. This integer field determines the thickness of a border in pixels.
new GuiControlProfile ( aintGotNoBorderProfile ) { II Never rely on defaults, turn it off yourself! border = false;

I;
new GuiControlProfile ( pencilThinBorderProfile ) ( border = truei borderColor = "0 0 0"; borderColorHL - "0 0 O"i borderColorNA = "0 0 0"; borderThicknes = 1; I; new GuiControlProfile rainbowBorderProfile) border = true; borderColor = "255 0 0"; borderColorHL - "0 255 O"i borderColorNA = "0 0 255"; borderThicknes = 2i {

I;

Cursors
What is a cursor, you ask? What we're talking.about here is that little blinky thing that shows up in text boxes and the like. We can colorize it with the cursorColor field.
464

Standard TGE GUI Controls

Chapter 12

new GuiControlProfile ( angryRedBlinkyThingProfile ) { cursorColor = "153 0 0";


);

Background/Fill Colors and Opacity


If we're not using a bitmap as our background, we will need to decide what

color it should be. Thus, TGE has provided the following fields.
fillColor. This contains the four-element integer vector containing the RGBA values for a control's background. fillColorHL. This contains the four-element integer vector containing the RGBA values for a control's background when it is highlighted. fillColorNA. This contains the four-element integer vector containing the RGBA values for a control's background when it is inactive.

Notice that the background color vectors have four elements, not three. This means you can define an alpha channel and make an element translucent or even transparent.
new GuiControlProfile aTranslucentPurplishWindowProfile ) { opaque = false; II Enable translucency/transparency fillColor = "153 102 255 128"; II 50% translucent fillColorHL "153 102 255 200"; II 22% translucent fillColorNA = "153 102 255 64"; II 75% translucent (almost transparent)
};

Fonts

Images can do a lot for transmitting ideas, but we will often have to break down and actually write something. That is, we'll have to use words to make ourselves clear. Because nobody likes to be boring, it makes sense to have some way to make our text a little more interesting than the default Arial font. TGE supplies myriad fields to enable text coloring. Please be aware that some of these field names are aliased, so the last definition is the definition that will be used for both (Table 12.1). For convenience, I have included the color codes in Table 12.1. Why? Well, if you recall from earlier when we discussed the console in TorqueScript, I mentioned that you can use escape sequences to color text. This colorization applies to the console and many of the text controls. So we've learned to colorize our text, but can we select a typeface and point size, too? You bet! We can select our font typeface with the fontType field and determine the point size of the font with the fontSize field.
465

Part III

Game Elements

Table 12.1.

field

.....
fontColor fontColorHL fontColorNA

c:aIorCOde
\cO

Pu......
Three-element integer vector defining default text color. Three-element integer vector defining highlighted text color. Three-element integer vector defining inactive text color. Three-element integer vector defining selected text color. Three-element integer vector defining hyperlink text color. Three-element integer vector defining selected hyperlink text color.

.:;~

Fields for text coloring.

colors[Oj colors[lj

\cl \c2

colors[2]

colors[3] If you are working with a version of TGE prior to 1.4, the UFT extension is GFT, instead. Also, versions prior to 1.4 do not support Unicode. In either case, you may install custom fonts in your game by doing the following. I. Be sure the font is installed on your system. 2. Create a GuiControlProfile specifying the font you want to use at the type size you want to use it. 3. Create a control using this profile. 4. Run your game. At this point, if the font shows up, you are done. Now,just be sure not to delete the UFT/GFT file, and you can use this font on any system, even if the user doesn't have it installed. TGE will use colors[4]

fontColorSEL fontColorLink

\c3

\c4 \cS

colors[S]

fontColorLinkHL

colors[6] colors[7]

-----

\c6 \c7

Three-element integer vector defining user-defined text color. Three-element integer vector defining user-defined text color. Three-element integer vector defining user-defined text color. Three-element integer vector defining user-defined text color.

colors[B]

\cB

colors[9]

\c9

fontType rAn Aside) I know that when I first picked up TGE, it was not at all clear what my choices were for fonts. I poked around for a bit and found some files with a GFT suffix (see" \common\ui\cache\"). The strange thing was that, when I ran TGE on different platforms, I found different files in this directory. Huh? Well, a little more research and reading showed that the GFT files are a side effect of a successful font build; i.e, as I specified new fonts, if the build was successful, I would find a new GFT file with a matching name. Because I know it is nice to have a reference, I have supplied a list of commanly installed fonts. Select a font from the following list of 47 common fonts and try it.
N

the gene<ated one.

466

Standard TGE GUI Controls

Chapter J 2

Aria] Aria! Black Arial Bold Arial Bold Italic Arial Italic Comic Sans MS Comic Sans MS Bold Courier Courier New Courier New Bold Courier New Bold Italic Courier New Italic Franklin Gothic Medium Gautami Georgia Georgia Bold

Georgia Bold Italic Georgia Italic Impact Impact Italic Lucida Console Lucida Sans Unicode Microsoft Sans Serif Modern Palatino Linotype Palatino Linotype Bold Palatino Linotype Bold Itatic Palatino Linotype Italic Roman Script Small Fonts

Tahoma Tahoma Bold Times New Roman Times New Roman Bold Times New Roman Bold Italic Times New Roman Italic Trebuchet MS 1Tebuchet MS Bold 1tebuchet MS Bold Italic 1tebuchet MS Italic Thnga Verdana Verdana Bold Verdana Bold Italic Verdana Italic

For example, let's try one of my favorites, Tahoma Bold, at 10 points.


new GuiControlProfile ( sweetTahomaBoldProfile ) { fontType "Tahoma Bold"; fontSize ,., 10;

If this works, the first time we try to use this profile, a new UFT file named "Tahoma BoId_lO.uft", will appear in our font cache directory. If the font failed to get constructed, TGE will try to use Arial instead. You know, Tahoma Bold isn't reaJly all that legible on the screen at only 10 points. In fact, it may not be legible at all on a Macintosh, which brings up the concept of target platforms and their variances.

Platform Variances
The folks who designed TGE had it all together the day they designed the textformatting features. Someone realized that different platforms have different standard screens with different aspect ratios and different "expected" fonts. Thus, a way was needed to target profiles to platforms. That targeting is provided with the $platform variable. This global variable is set by the engine if it can determine the current platform type. It can take the following values.
macos. It's a Macintosh, or at least it's running OSX or OS9. windows. Some version of Windows.

467

--------_.

-------_.

._----------------- -

--- - - - - -

Part III

Game Elements

X86UNIX.

Unix.

Linux. Linux. OpenBSD.OpenB5D. Unknown. This means that TGE could not identify the 05. Honestly, I'm sure that the Splatform variable wasn't created with only fonts in mind. In fact, the only time we really care about this for fonts is when we're dealing with the Macintosh. Those guys just have to be different; or perhaps it's the PC guys who are different? Whatever the case, fonts on the Mac are quite different from those on PCs, due to several factors, which include strange aspect ratios and, more importantly, expectations. 05X (and 059 before it) uses a different font set from those found under Windows. 50, how do we make our fonts Mac and PC friendly? Like this. new GuiControlProfile ( makeAMACGuyHappyProfile ) ( fontType = ($platform $= "macos") ? "Courier New" "Lucida Console"; fontSize = ($platform $= "macos") ? 14 : 12; I;
Font Not Found?

In case you missed it above, if TGE cannot build your font, it will subsitute Arial at the point size you selected. Failing that, the engine will fail out, complaining about Arial fonts needing to be on the system. 5heesh! Really though, I've never seen it happen yet.
Unicode

Versions 1.4 and later support Unicode fonts, as well. Unicode is a method of encoding keyboard keys to corresponding numeric values. ASCII is the oldfashioned way of doing this, but with the world rapidly growing smaller and with a number of non-Latin-based alphabets being used on keyboards today, a new encoding was and is required. Thus Unicode was born. In short, if you want to penetrate a foreign market, one of the things you must be able to do is match that market's keyboard scheme. TGE is ready.

Text Formatting
We're doing pretty well so far. We've done a lot to get our GUIs looking nice and our fonts looking interesting. However, what happens if we try to use some text and it doesn't align nicely in the control. Using spaces to justifyI space our text isn't a very appealing solution. Does TGE help us out? Yes, it does. There are two fields that deal with how text is formatted.
468

Standard TGE GUI Controls

Chapter 12

The first is the justify field, which can take the following values: left-left justified, right-right justified, and center-centered. Then, for those cases where it isn't the justification we care about so much as the fact that the text rides too close to the edge, we can adjust our offset with the textOffset field, which takes a two-element vector defining the x-v offset of the upper-left corner of the first text character in pixels. new GuiControlProfile justify = center;
};

centerMyTextProfile ) {

new GuiControlProfile ( slightOffsetTextProfile ) { II Offset 4 pixels from left and 6 pixels from top textOffset = "4 6 H ;
};

We've talked about how GUI control profiles contribute to the look of a GUI; now let's talk about how they affect behavior.

Autosizing There are a few controls that may need to resize either their heights or widths to fit their parent control. Among these are the GUITextCtrl, GuiTextListCtrl, and GuiMLTextEditCtrl. To declare this functionality, TGE provides two Boolean fields.
autoSizeHeight. Allows the control to resize its height to accommodate

multi-line/row contents. autoSizeWidth. Allows the control to resize its height to accommodate multi-character/column contents.

Key and Mouse Attributes There are a few key and mouse attributes that I should at least touch upon.
mouseOverSelected. If this is set to true, the control will be selected

when the mouse hovers over it. returnTab. If this is specified, the control will generate a tab event when it is in focus and the ENTER key is pressed. canKeyFocus. If true, this control can be given keyboard focus.

Just setting these values to true does not guarantee the behavior. These fields only work if the control can behave in this way. For example. setting these on a label rGuiTextCtrlj would do nothing. but setting them on a text edit control (GuiTextEditCtrlj would work.

469

Part III

Game Elements

Modality Again, if you are using version 1.4 or later, this concept is deprecated. The following only applies to versions 1.3 and prior. We discussed modality above. Here is where we learn how to enable it. Simply set the Boolean field modal to true, and your control should be modal. My suggestion is that most controls have this set to false unless it really makes sense to force the user to deal with a CUI explicitly and first. Input Restrictions Besides the often onerous restriction of a modal CUI, what other restrictions are there? Well, just one. We can restrict text-entry fields to allow only numeric input by setting the Boolean field numbersOnly to true. Audio Attributes Because it would be a real bummer to have to define the sound for each and every button, TCE supplies two fields to do so in a CUI control profile instead.
soundButtonOVer. Play the sound represented by this AudioProfile when the mouse moves over this button. soundButtonDown. Play the sound represented by this AudioProfile when the button is pressed.

12.3 GuiControl-the Root GUI Class


CuiControl is the root class to all CUI controls and thus provides many fields and console methods. When it is used at all, it is normally used as a container for other controls, as it has very few rendering features and does nothing with inputs.

12.3. 1 Profiles
Nearly all controls require a profile. Furthermore, every time a control wakes up, it looks for its profile. If for some reason no profile is specified, the control will do its best to find one, using the following rules.
1. Use the profile specified by the user unless it is equal to the null string, ''''. 2. Try to find a profile whose name is the first part of the control class name + the word profile (e.g., CuiButtonCtrl would look for CuiButtonProfile). 3. Use CuiDefaultProfile (a profile with this name must always be created, and always before other profiles are created).

Interestingly, profiles can be changed at any time. Also, the contents of a profile (i.e., the fields) can be modified from script.

Standard TGE GUI Controls

Chapter 12

In order to set the original profile, assign a value to the profile field. This value can be the null string. If it is, the engine will search for a profile and replace the null string with the name of the first matching profile found. To change a profile after a control is created, call the setProfile () console method. The control's profile field can generally be updated using direct assignment, but caution and plenty of verification are in order if you intend to use this method.

II Creation ... new GuiControl ( myTestControl ) { profile = someProfile;


};

II Changing by console method myTestControl.setProfile( someOtherProfile ); II Changing by assignment ( not suggested myTestControl.profile = someOtherProfile;

12.3.2 Extents and Position


All controls have two extents, extent and minExtent. The former is a twoelement integer vector defining the control's initial width and height. The latter (not actually used in all controls) is also a two-element integer vector that specifies the minimum width and height dimensions the control can assume. If we did not have minExtent, a control could be scaled down to a point where it was too small to use or view, either because the parent was resized, or because we resized the control ourselves using the mouse. minExtent prevents either of these cases. position, also a two-element integer vector, is the initial x and y coordinate of the upper-left corner of the control.

new GuiControl( myTestControl2 ) { position = "10 20"; II start at < 10 , 20 > extent= "100 200"; II Start 100 pixels wide, 200 pixels high minExtent = "80 80"; II Do not allow to shrink below 80x80 pixels
};

12.3.3 Position and Sizing


Because the canvas may have various dimensions (as a result of user-selected resolution changes and/or GUI editor resizing), several ways are supplied to modify/maintain the sizing of a control. There are two fields, ho r i z Si zing and vertSizing. The settings and behavior of both of these fields are

471

Part III

Game Elements

thoroughly covered in Section 3.14, "The GVI Editor." Please refer there for how these two fields interact and control the sizing of a control. To retrieve the current position of the control, we can use the getPosi tion () method, which returns a two-element integer vector containing the current position of the control's upper-left corner. Then, to modify the position and/or the size of this control, we can use the resize () method.
II Move this control down 10, left 10, and resize it to II "100 100"
$position = $test.getPosition(); $newX = getWord( $position , 0) + 10; $newY = getWord( $position , 1) + 10; $test.resize( $newX , $newY , 100 , 100 );

12.3.4 Initial Visibility


If we so choose, we can cause a control to start off invisible (not rendered). Just set the Boolean field visible to false. By default, it is set to true.

12.3.5 Accelerators
Often, it is nice to be able to activate a control via some combination of key presses. To facilitate this, TGE GUIs support accelerators. By setting the accelera tor field to some combination of modifier(s) + key, we can enable access to buttons and many other controls from the keyboard.
new GuiButtonCtrl( myAcceleratedTestButton ) { II Ctrl+Alt+X activates this button accelerator = "Ctrl Alt X";
};

Modifiers
Modifiers can be CTRL, SHIFf, or ALT.

Keys
Keys include the following: F1, F2, F3, F4, FS, F6, F7, F8, F9, FlO, Fll, F12, A, B, C, D, E, F, G, H, I, J, K, L, M, N, 0, P, Q, R, S, T, V, V, W, X, Y, Z.

12.3.6 Commands and $thisControl


Many controls will wish to execute a command when activated, and a smaller set may need an alternate (secondary) command for other events. These com472

Standard TGE GUI Controls

Chapter 12

mands can be small scripts or just calls to functions. They are declared as follows.
new GuiButtonCtrl( myTestButton ) ( command = "do Something () ; " ; altCommand = "doSomethingElse();";
};

Be aware that any control that executes a command will first set the global variable $thisControl to the ID of the calling control. For example, when a button is clicked, it will do the following.
1. Set $thisControl to the ID of the button.

2. Execute the script specified by command. Only the following controls use al tCommand. GuiSliderCtrl GuiTextCtrl GuiTextEditCtrl GuilfeeviewCtrl

12.3.7 Variables
Interestingly, each control can have a variable associated with it. How it uses this variable is up to the control, but normally the variable contains the current value of this control.
$test = new GuiButtonCtrl( testButton ) { variable = "testButtonValue";
};

II Access this variable like this: echo("testButtonValue == " , $testButtonValue );

12.3.8 Becoming First Responder


There will be times when we want a control to capture keyboard inputs. To have a control start catching the input, we must call the makeFirstResponder () method with a Boolean value of true. To make it stop, simply call this method with a value of false. Also, don't forget that this concept only applies to version of TGE prior to 1.4.
II Capture keyboard inputs testButton.makeFirstResponder( true );

473

Part III

Game Elements

12.3.9 Current and Subsequent Visibility


We know how to tell the control whether it should start off visible, but what if we want to change this? Use the setVisible () method and pass it either true or false based on our needs.
testButton.setVisible( false ); II Hide the button

We can also check for visibility with the isVisible () method.


echo {"This button is", (testButton. isVisible () " not "), "visible");
?

12.3.10 Awake and Active?


A control can be awake or asleep, active or inactive. These modes can be interpreted as follows.
(isAwake () true} . Self or parent is current content of canvas or false) . Self or parent is not current content of canvas

canvas layer.
(isAwake ()

or canvas layer.
(isActive () (isActive ()

== ==

true). Currently enabted. false). Currently disabled.

In addition to checking the status of active, we can set it with the


setActive {} method. testButton.setActive( false ); II Disable this button

12.4 GUI Console Methods, Callbacks, andScoping


Gurs and GuiControlProfiles, like all TGE objects, support console methods and callbacks.

12.4.1 Console Methods for GuiControl and Children


You may not find much use for this, but just in case, I want you to know that you can in fact define console methods on GuiControleProfile objects. However, you can only define console methods for GuiControlProfile objects in either the namespace GuiConlrolProfile:

474

Standard TGE GUI Controls

Chapter 12

$profileA

new GUIControlProfile( testProfileA )

\ \ ...
};

function GUIControlProfile::testit( %this ) ( echo("GUIControlProfile::testit(",%this, ")");

$profileA.testit() ;

or in the SimObject namespace (GuiControIProfile's parent is SimObject):


function simObject: :testit2 ( %this ) ( echo ("simObject: :testit(2",%this,")");

$profileA.testit2();

The class, className, and superClass keywords are not recognized and cannot be used to extend the namespace of GuiControlProfile objects.

12.4.2 Console Methods for GuiControlProfile and Children


More useful to us than console methods for GuiControlProfile are console methods for GuiControl and all its children. GUIs have a lot of callbacks and you'll find it very useful to be able to scope console methods to specific object instances. Fortunately, the normal scoping rules and methods apply to CUI controls. In other words, we can scope methods to CuiControl and its parent classes, and we can scope to the object's name.
new GuiBitmapButtonCtrl(LessonsButton)

II
);

function LessonsButton::test(%this) ( echo( "LessonsButton: :test(" @ %this @ ")" );

Thus, typing
LessonsButton.test();

475

Part III

Game Elements

will print the following in the console.


LessonsButton: :test(LessonsButton)

If you need a review of the seoping rules, go back to Chapter 4, "Introduction to TorqueScript" If you want to know what the callbacks are for different GUI controls. take a peek at Appendix A.4, "GUI Controls Quick Reference." I'll be giving some code samples below to handle certain useful GUT callbacks.

12.5 GUI Skinning


Several controls offer the additional ability to "skin." In effect, we can create a graphic image or array of images that will then be used to cover the controL This offers us a simple means of giving our game GUls their own flavor. However, this power does come at a price. Specifically, we need to understand how this skinning graphic is laid out and how we create it. Don't worry; it's not that hard.

12.5.1 Bitmap Arrays


Almost every skin we will use is a single graphic file that has been laid out in some sort of an array (skin elements organized in rows and COlumns). The purpose of the rows and columns is determined on a per-control basis, but all of these arrays follow the same rules. General Rules These are the general rules to be followed. The format of the graphic file must be nonlossy and support an alpha channel (in theory you can do some controls with a 24-bit BMP (no alphachannel) or a JPG (lossy), but it is likely this won't work well). PNG is the preferred format. The graphic file does not need to be sized as a power of two. Any (reasonable) ratio will do. All elements must be separated by one pixel at their nearest point. The first pixel row of the graphic contains no elements, just a single color. This color becomes the "array-divider color." The array-divider color is used by Torque to identify rows and columns. All arrays of subelements are arranged left-to-right and top-to-bottom; i.e, if there is some element numbering implied, the upper-left element is zero and the lower-right element is N - 1, where N is the total number of elements.
476

'.

-----_._-

. -

. _ - - - - - - ._- --_._--

Standard TGE GUI Controls

Chapter 12

Column Rules
These are the rules to be followed for columns. The first column of elements must align with the left edge of the array. Elements in a column (excluding the first column) do not need to left-align with those elements in the rows above .or below them. Row Rules These are the rules to be followed for rows. The first row of elements starts one pixel from the top of the graphic. All elements in a row must top-align with all other elements in that row. All elements in a row must be one pixel apart at their nearest point in the row; Le., shapes may be irregular, but the extents of elements must be one pixel apart. The rightmost element in a row does not need to align to the edge of the graphic; Le., there can be empty space on the right of a row. The bottommost row does not need to align with the bottom of the graphic. Again, there can be buffer space. Now, it may seem like this is a lot to remember, but it really isn't that bad, and you'll have examples to follow. Figure 12.1 shows a simple example and a more complicated one. Most of the difficulty in creating a skin comes from following the prior rules and in knowing the layout requirements for the current control. I will give a layout for each skinnable control below so you know what you're looking at when you make your own, but here I'd like to stress some points that will make your life easier. Always use PNG files if you can. These support alpha channels, are reasonably sized, and you don't have to wony about artifacts. Make the subelements separately, then assemble the array as layers. It will be much harder to make your elements if you try to make them all in one graphic layer. Make the subelements at the maximum resolution you expect them to be displayed, unless they require a specific sizing (the bitmaps for GuiBitmapBorderCtrl and GuiWindowCtrl are sensitive to scaling). Use pure red (255 a 0) as your array-divider color if you can, or use another color that is not present in the element. You can use a tool to analyze the color mix of your subelements. Then, just pick a color that is not in the controls already. This is not a strict rule, but it makes designing skins easier if you follow it.
477
Figure 12.1.

Examples of bitmap arrays.


a. Check box array Iso

simple ... ).

b. Scroll array (Ewww ... J.

Part III

Game Elements

Do not use transparency as your array-divider color unless you absolutely have to. No bitmap arrays require this. Never use translucency (alpha lower than 1.0 and higher than 0.0) as your array-divider color, period. I mean it. More? Sure, but I'm sure that many people reading this are more artistically inclined than I am, so I'll stop here.

12.5.3 Enabling Skinning


Many controls require the presence of a bitmap in their profile. If you use a profile that does not have a bitmap for a control that requires one, the engine will not render the control. Be sure to include bitmaps in control profiles that require them. You were warned.

In order to enable skinning for controls, we use two fields in the CuiControlProfile.
hasSi tmapArray (deprecated in version 1.4 +). Boolean value enabling

skinning.
bitmap. Path to the bitmap skin to use to theme this control. new GuiControlProfile ( usesSitmapArray ) { hasSitmapArray = true; bitmap= "path to bitmap array file";
};

Now, finally, let's talk about the specific CUI controls.

12.6 Container Controls


This first category of controls contains the standard container-type controls. Don't forget, though, that all controls can act as containers to other controls.

12.6.1 GuiFrameSetCtrl
Table 12.2.

'. Setting up rows and columns.

This control is used to automatically or manually frame any number of child controls, in regular row-column format. The first time you try to use it, it may seem a little odd, but once you understand the rules by which it operates, YOU'll be using it for all kinds of tasks.

Setting Up Rows and Columns


position

"100100" "300300" "0 100 200" "0 100 200"

extent
co 1 umn s rows

To use this control, simply place it and give it an initial extent. Then, to divide the control into rows and columns, simply specify the starting position of each column in the columns field and each row in the rows field. For example, we could make a 3 x 3 matrix of cells where each cell is 100 by 100 pixels, by using the field settings shown in Table 12.2. Now, we need to add some children.

l__

478

- - - - - - - - --

----

Standard TGE GUI Controls

Chapter J 2

Inserting Controls
In this example, we'll just use nine buttons. To add these buttons, simply select your new GuiFrameSetCtrl and add nine GuiButtonCtrl controls as children. If you pay attention, you will see that the controls are added left-to-right and top-to-bottom; that is, the buttons are (automatically) added in the following order: button a -7 < Column a , Row button 1 -7 < Column 1 , Row

a>

>

button 2 -7 < Column 2 , Row a > button 3 -7 < Column a , Row 1 >

button 8 -7 < Column 2 , Row 2 > So, what happens if we remove a control?

Removing Controls
If we remove a child control, all of the children will shift as required to fill the empty slot. Furthermore, newly added controls will go at the end of the list. Just keep this in mind if you are making and destroying these controls dynamically.

How Borders Work


This control will allow you to specify dragable borders between the rows and column. In order to do this, specify these fields shown in Table 12.3.
FIeld
borderWidth borderColor borderEnable borderMoveable
Width of borders in pixels. Color and opacity of borders. Enable border color rendering. can be "alwaysOn", "alwaysOff", or "dynamic". Enable border dragging. can be "alwaysOn", "alwaysOff", or "dynamic".

Table 12.3.

Using borders.

Please note that, if you disable border color rendering, dragging the border will also be disabled.

Fudge?
There is an odd field named fudgeFactor. When set to a positiv.e number, this value is subtracted from every border, making them each that many pixels shorter on each end. This does not affect the ability to grab a border.

479

Part III

Game Elements

Autobalancing So, what if we would rather maintain balanced cells? We can enable the autobalancing feature by setting autobalance to true. Now, the control will automatically attempt to make all of the cells the same size the next time it wakes up. Scripting the GuiFrameSetCtrl
It is possible to manipulate this control from script. Table 12.4 shows all the

things we are allowed to do to this control from within a script.


Table 12.4.
Manipulating the GuiFrameSetCtrl.

M8III8d
addColumn () removeColumn() addRow () removeRow ( ) getColumnCount()

.'
.~

Add a new column to the control. All contents will shift. Remove a column from the right side of the control. Contents will shift but will not be deleted. Add a new row to the control. All contents will shift. Remove a row from the bottom of the control. Contents will shift but will not be deleted. Return the current number of columns in the frame. Return the current number of rows in the frame.
) )

In order to create empty blocks in a GuiFrameSet, simp Iy add a GuiControl as an element for each c ell you wish to be bla nk.

~/

getRowCount () getRowOffset( row

Return the beginning pixel offset for row. Return the beginning pixel offset for column. Set the beginning pixel offset for row. Set the beginning pixel offset for column.

getColumnOffset( column setRowOffset( row , offset )

setColumnOffset( column , offset )

12.6.2 GuiScrollCtrl

480

This control is used to contain a resizeable control. These resizeable controls are made children of the GuiScrollCtrl, which then allows the user to use scroll bars to move to a specific location within the child control. GuiScrollCtrl can be programmed to supply a vertical and/or a horizontal scroll bar. These scroll bars will be enabled (based on field settings) always, never, or when the child content expands beyond the vertical or horizontal bounds of the view area. This control also provides a configurable margin and control over the thumb affordance (the little slidey thing on the scroll bars). Lastly, from script we can force the control to scroll to the top or bottom of the child.

Standard TGE GUI Controls

Chapter 12

Configuring GuiScrollCtrl
As noted above, this control provides a few configuration options.

Scroll Bars
We can control when or if either the vertical and/or horizontal scroll bars will be rendered by using the hScrollBar and vScrollBar fields. These fields can be given the following values.
alwaysOn. Scrol1bar always renders. alwaysOff. Scrollbar never renders.
dynamic.

Scrollbar renders based on size of child.

Additionally, if we've chosen to render the scron bars, we can select either scaling or nonscaling thumbs. The thumb is the little box on the scroll bar that allows us to scroll by dragging. Normally, this thumb scales relative to how "full" the scroll dimension (vertical or horizontal) is. However, for really big children or really small scrolls, this behavior can cause the thumb to scale to a very tiny size, making it difficult to grab with the mouse. Thus, we can force the thumbs to maintain a fixed size, using the constantThumbHeight field.
new GuiScrollCtrl () { hScrollBar = "dynamic"; vScrollBar = "alwaysOn"; constantThumbHeight = false;
};

II Render horizontal scrollbar as needed II Always render vertical scrollbar II Scale thumb dynamically

Margins
We can make minor adjustments to the margins of a scroll area; that is, we can set a fixed margin that will cause the child to fit within a box defined by the childMargin field. This field takes a two-element integer vector. The tirst value in the vector is the left-right margin, and the second value is the topbottom margin. Both margins are in pixels. The GuiScrollCtrl already provides a margin for its children, but this allows us to further expand that margin, to account for various cases where the content may be occluded by a parent of the GuiScrollCtrl.
new GuiScrollCtrl() childMargin = "10 10";
};

Ignoring First Responder


We can control whether this control will be allowed to become first responder. The setting of first responder state is still controlled by the GuiControl method
4Bf

Part III

Game Elements

makeFirstResponder (), but we can force this GUI to ignore this request by setting the willFirstRespond field to false. Also, don't forget that this concept only applies to versions of TGE prior to 1.4. new GuiScrollCtrl () { II Do not become firstResponder ... ever willFirstRespond = false;
};

Scripting GuiScrollCtrl
GuiScrollCtrl provides two console methods that allow us to scroll the contents from script.
scroll ToBottom () . Scroll all the way to the bottom of the child. scroll ToTop () . Scroll all the way to the top of the child.

No scripting control is provided for horizontal scrolling.

GuiScrollCtrl Skin
This control has what is probably the most complicated (looking) skin. The bitmap array is organized as shown in Table 12.5.

12.6.3 GuiStackControl
This very simple container is used to hold any other control in a fixed-width stack. To use this control, simply place it and then start adding other controls to it as children. These controls will stack up on each other. You may control the direction of this stacking by setting the stackFromBottom field to true or false. If it is true, the controls will stack bottom to top: otherwise, they will stack from top to bottom (the default). The control can be resized horizontally but not vertically. If it isn't clear, the purpose of this control is to allow us to dynamically place other controls and to be guaranteed that they will all take on the same width and that they will stack perfectly against their mates. See the images in Figure 12.2 for clarification. Please note that, if you do not want the controls to be right next to each other, you may add some space by setting the padding field to a positive value.

12.6.4 GuiPaneControl
Here is another simple but useful container control. This control is designed to proVide us with a simple dropdown area that can contain any other control (s). The user can simply hide/show the pane by clicking on the caption bar at the
482

1_-

Standard TGE GUI Controls

Chapter 12

Table 12.5.
Bitmap array for GuiScrollCtrl skin.

sample Arr8y
I ....
Up-Scroll Normal Down-Scroll Normal Top of Vertical Thumb Normal Middle of Vertical Thumb Normal Bottom of Vertical Thumb Normal Vertical Bar Normal Right-Scroll Normal Left-Scroll Normal Left of Horizontal Thumb Normal Middle of Horizontal Thumb Normal Right of Horizontal Thumb Normal Horizontal Bar Normal Lower-Right Affordance Normal Up-Scroll Depressed Down-Scroll Depressed Top of Vertical Thumb Depressed Middle of Vertical Thumb Depressed Bottom of Vertical Thumb . Depressed Vertical Bar Depressed Right-Scroll Depressed Left-Scroll Depressed Left of Horizontal Thumb Depressed Middle of Horizontal Thumb Depressed Right of Horizontal Thumb Depressed Horizontal Bar Depressed Lower-Right Affordance Depressed Up-Scroll Inactive Down-Scroll Inactive Top of Vertical Thumb Inactive Middle of Vertical Thumb Inactive Bottom of Vertical Thumb Inactive Vertical Bar Inactive Right-Scroll Inactive Left-Scroll Inactive Left of Horizontal Thumb Inactive Middle of Horizontal Thumb Inactive Right of Horizontal Thumb Inactive Horizontal Bar Inactive Lower-Right Affordance Inactive

Figure 12.2.
Using GuiStackControl.

stackFromBottom

false

stackFromBottom == true

Delete Button 1 (adjusts after sleep/wake)

483

Part III

Game Elements

Figure 12.3.

Console script error pane.

Script Error Pane-Open

top. An example of this control that you should be familiar with is the console script error pane (see Figure 12.3). Please note that normally this control will be populated with a single scroll control, which will then contain a self-expanding control like GuiMLTextCtrl or GuiTextListCtrl.

GuiPaneControl Skin
This control uses a very simple skin which is ordered as shown in Table 12.6.
Table 12.6.

CGlumn2

Bitmap array of GuiPaneControl skin.


Pane Close Button Caption Bar Begin Caption Bar Caption Bar End + Pane Toggle Button

This control will decide how tall to make the caption bar based on the height of the first row in the skin bitmap. So, if you need the caption bar to be taller or shorter, adjust the skin.

Please note that you may make all of these buttons and bars the same if you like. In the end, clicking anywhere on the bar will open or close it. The variances in the bar graphic merely supply a recognizable affordance.

Caption Text
The caption bar may display a sbort (255 or fewer characters) text string. Furthermore, this text may be rendered in front of or behind the caption bar. To specify the text, set the caption field to the text you want. To specify the render order, set the barBehindText to true or false. Setting it to true will render the text in front of the bar; setting it to false will render the bar in front of the text (Figure 12.4).

484

Standard TGE GUJ Controls

Chapter 12

Disabling Collapses Although the control is really meant to be opened and closed, we may tell the control that it cannot be collapsed. Simply set the collapsable field to false. Please note that, if the control is collapsed when we set this field, it will not open when the caption is clicked. So, in effect, setting this field to false locks the pane. Scripting the Control There isn't much we can script on this control, but it is possible to toggle the pane open and closed using the method setCollapsed ( collapse ), which will collapse if collapse is true, otherwise it will be open. Be aware that, if the pane collapsing is disabled, this method will do nothing.

Figure 12.4.

Caption bar.

12.6.5 GuiTabBookCtrl and GuiTabPageCtrl


A tab book is something that Illany of us have come to take for granted. It is a control that contains an unspecified number of tabbed panes. By clicking on any of the tabs, the pane that is associated with the tab is brought to the front and made visible. It's like an index file, except it is completely 20 (Figure 12.5).

Figure 12.5

~:-':.~'

J!!!!!!!!.-I

Tabbed panes.

Configuring GuiTabBookCtrl To use this control, we must first place it and size it to our liking. Once that is done, we need to decide where the tabs should be as well as how large they should be. Right now, tabs can be on top or on the bottom of the tab pages and can be any size we wish. In the future, the engine may also support tabs on the right and left. To specify the tab positions and sizes, set the fields as shown in Table 12.7.
Table 12.7
tabPosition tabHeight tabWidth

May be "Top"

or "Bottom".

Setting tab positions and sizes.

Height of tabs in pixels. Width of tabs in pixels.'

485

Part III

Game Elements

Wow, that was pretty simple! Now, let's add some tab pages.

Adding Tab Pages


The first thing you must know about adding content to a tab book is that the GuiTabBookCtrl may only contain GuiTabPageCtrl controls. If you try to place any other kind of control in a tab book, the new control will either drop into the first tab page the engine finds (owned by this book), or the control will drop onto the book's parent. To add pages (and therefore tabs), simply select the book we just created as the instant group and start adding GuiTabPageCtrl controls until you have enough pages. We can add text to a page's tab by setting the text field of the GuiTabPageCtrl we just added. After we add our pages, we will need to put content in the pages.

Editing Tab Pages


Editing pages is a breeze. Simply click on the tab for the page you wish to modify and start dropping controls into the page. We can tab through pages while in the editor, making it exceptionally easy to modify our tab book.

Dynamic Page Creation/Destruction


We can add and remove pages from our tab books from script by calling the two methods in Table 12.8.
Table 12.8.

Adding and removing pages.

addpage ( [ pageName ] )

Add a new page to the GuiTabBookCtrl and return the ID of the GuiTabPageOrl that was added. You may optionally specify the text for the page's tab by passing a value in pageName. Remove the page at index (left to right) position.

remove Page ( index

More Scripting
Aside from dynamically adding and removing pages, we can'also specify two callbacks for a GuiTabBookCtrl (Table 12.9).
Table 12.9.

Two callbacks for GuiTabBookCtrl.

onClearSelected() onTabSelected( tabText

Called when right mouse is clicked in a page and the mouse is over a valid control (besides the page itself). This callback is called when a tab is clicked and prior to the page associated with that tab being (re-)displayed. It is passed the text in the page's tab.

486

Standard TGE GUI Controls

Chapter 12

12.6.6 GuiWindowCtrl
This control provides the familiar window metaphor. This is a completely skinnable control. By default, TCE comes with the graphics required to skin this as a standard Windows- or OSX-style window (Figure 12.6). These windows provide standard window behaviors through the following fields.
canClose. Boolean value enabling close icon and ability to close window. canMaximi ze. Boolean value enabling maximize icon and ability to maxiFigure 12.6. Window skins. a. Windows theme.

mize window.
canMinimi ze. Boolean value enabling minimize icon and ability to minimize window. canMove. Boolean value enabling dragging. If true, the window can be dragged by the upper bar. closeCommand. This script is executed when the window is closed. minSize. A two-integer vector describing the minimum size this window can take when drag-resized. This does not affect minimized size, which is always just the drag-bar and buttons. resizeHeight. Boolean value enabling height drag-resizing, Le., the window can be height-resized by dragging a corner or edge. resizeWidth. Boolean value enabling width drag-resizing, Le., the window can be width-resized by dragging a corner or edge.

b. OSX theme.

~.:=lesxTheme

new GuiWindowCtrl () { / / ... resizeWidth = "1"; resizeHeight = "1"; canMove = "1"; canClose = "1"; canMinimize = "1"; canMaximize = "1"; minSize = "50 50";
};

The sample CuiWindowCtri definition above creates an unnamed window that can be resized in both height and width, and can be moved, closed, minimized, and maximized. It also has a minimum drag size of 50 x 50 pixels. GuiWindowCtrl Skin As noted above, this is a fully skinnable control. This skinning is controlled by two CuiControlProfile fields and a bitmap.
hasBi tmapArray. Boolean value enabling skinning. bitmap. Path to the bitmap skin.

487

Part III

Game Elements

Table 12.10.

Bitmap array for GuiWindowCtrl skin.

Maximize Button Normal Revert Button Normal Minimize Button Normal Title Bar Left Edge Title Bar Left Edge Inactive Left Edge

Maximize Button Depressed Revert Button Depressed Minimize Button Depressed Title Bar Right Edge Title Bar Right Edge Inactive Right Edge

Maximize Button Inactive Revert Button Inactive Minimize Button Inactive Title Bar Middle Title Bar Middle Inactive Lower Left Corner Bottom Edge Lower Right Corner

The bitmap array is organized as shown in Table 12.10. Making Your Own GuiWindowCtrl Skin Sometimes it isn't enough just to see a finished example, so let's make a simple window skin together and then improve on it a bit. As a rule, I like to start simple then work my way up. Thus, we will make a simple GuiWindowCtrl bitmap array together.
Setting up. 1. Open your graphics program of choice. 2. Create a blank 300 x 300 image with a red (255 0 0) background. 3. Enable a viewable grid and adjust it to 1 x 1 pixels. 4. Zoom in on the upper-left corner of your image until the grid is at one-pixel scale. Button blanks. 1. Select a foreground color of (64 64 64).

488

Standard TGE GUI Controls

Chapter' 2

2. Create a new transparent layer and rename it "Button Blanks." 3. In this new layer, using a rectangular selection tool starting at X:O Y: 1, select a 17 x 14 pixel area. 4. Flood fill the selection. S. Copy the selection and paste it to this layer as a new selection, placing it at X: 10 Y:1. 6. Paste another selection in this row, maintaining the one-pixel distance. 7. Make three more rows, and you should have an image like the one in Figure 12.7. Upper bars normal. 1. Create a new transparent layer and name it "Top Bar Normal." 2. Select a new fill color of (l28 128 128). 3. Create two 5 x 23 rectangles and one 38 x 23 rectangle, again maintaining one pixel between this new row and those above, as well as one pixel between each element in the row (Figure 12.8). Upper bars inactive. 1. Create a new transparent layer. 2. Copy the bars we just made (Figure 12.8) and paste them into our new layer. Edges and bottom. 1. Create a last layer and name it "Edges + Bottom." 2. Create the following parts: 3 x 9,3 x 9,3 x 3, 7 x 3, and a last 3 x 3. 3. Our final image should look like Figure 12.9. That is it! We now have a very simple GuiWindowCtrl bitmap array. Go ahead and save it, duplicate it, and then save the duplicate as a PNG. Use this PNG in a test window, and it should look like Figure 12.10.
Figure 12.10
Our new GuiWindowCtrl skin in use.

Figure 12.7

Figure 12.8

Figure 12.9

489

--

_._~-----------

Part III

Game Elements

12.7 Backgrounds and Borders


The controls in this category are normally used as backgrounds to other can troIs, but they are quite versatile and can be used for a number of more advanced effects. I'll give you a hint. Think in terms of layers and what you could do by enabling and disabling these layers. Combine this with the concept of masking and, well ...

12.7. f GuiBitmapCtrl
This control is used to display any reasonably sized image. In TGE versions prior to 1.3, this control could only accept a bitmap with a maximum size of 256)( 256 pixels. For larger images, the GuiChunkedBitmapCtrl was used. This limitation is no longer in place.

The Bitmap
The initial bitmap is specified as a field in the control.
new GuiBitmapCtrl( myTestBitmap ) ( bitmap = "./somelmage"i

Subsequently, this can be changed using the setBi tmap () method and a complete path to a new image.
myTestBitmap.setBitmap( expandFilename("./somelmage2.png")
)i

In the above example, it is implied that there is an image file with the name "someImage2.png" in the same directory as the script.

Wrapping and Offset


When creating a GuiBitmapCtrl, we can specify the wrap field as either true or false. If wrap is set to false and the image is larger than the GuiBitmapCtrl extent, the image will be scaled down. Vice versa. if the extent is larger than the image, it will be scaled up. However, if wrap is true. no scaling will occur. The image may be clipped or repeated based on size versus extent. In addition to wrapping, we can offset an image using the setValue () method.
function TestBi tmap2: : scrollMe ( %this ) ( if(! %this.isScrolling ) returni' %this.curX += 2i

490

L -..

Standard TGE GUI Controls

Chapter 12

%this.curY += 2:
i f ( %this. curX

>= 256)
0: 0;

%this.curX %this.curY

%this.setValue( %this.curX I %this.curY ); %this.schedule( 32 I scrollMe ):

The code above comes right from the GPGT Lesson Kit. It is used to scroll the sample image. Note that positive values cause the image to be offset up and left, whereas negative values cause it to be offset down and right. All values are in pixels.

12.7.2 GuiChunkedBitmapCtrl
This control is the big brother to GuiBitmapCtrl and serves basically the same purpose. It was used in days of old to render images larger than 256 x 256. It did this by cutting up the image and storing it appropriately on the video card/memory. Today's hardware has made this control pretty much obsolete, but there are a few variances in its behavior, so we'll discuss it briefly.

The Bitmap
As with GuiBitmapCtrl, the initial bitmap is specified using the bi tmap field. Also like GuiBitmapCtrl, this control does support changing the bitmap after creation using the setBi tmap () method.

Tiling
Whereas GuiBitmapCtrl had a wrapping functionality, GuiChunkedBitmapCtrl has tiling. Tiling is controlled by the Boolean tile field and behaves pretty much the same as wrapping, but not quite as reliably.

The useVariable
A significant difference between GuiBitmapCtrl and GuiChunkedBitmapCtrl is the useVa r iable field. If this field is set to true, we can specify a variable name in the bitmap string instead of a path. Then, when this control is rendered, it will look at the contents of the named variable for the path to its image file. This field is only checked when the onWake () callback is called. So, you may only change a bitmap using this field between wakes.
491

-_ .._-_._----_._-------_.

Part III

Game Elements

In the following example, we've chosen to use a named variable to specify our path instead of doing so directly.
new GuiChunkedBitmapCtrl() / / ... useVariable = true; variable = "MyBitInap"; bitmap= "";
};

Of course, for this to work, we must have defined $MyBi tmap.


$MyBitInap = expandFileName( ".\some\path\to\some\image" );

12.7.3 GuiBitmapBorderCtrl
This skinnable control is used to adorn other controls with a frame (or border) .
GuiBitmapBorderCtrl Skin

The bitmap array for this control is organized as shown in in Figure 12.11.
Figure 12.11.
Bitmap array for GuiBitmapBorderCtrl skin.
Upper-left border Left border Upper-right border Right Border Top border Lower-left border Lower border Lower-right border

Making Your Own GUIBitmapBorderCtrl Skin

Learning to make skins for the border control is a step-by-step process. In this section, I will provide you with the basic steps needed to make a plain border. Once you have mastered this process, you should feel free to create more advanced borders using the same rules.
Setting up.

1. Open your graphics program of choice.


492

t--

Standard TGE GUI Controls

Chapter 12

2. Create a blank 300 x 300 image with a red (255 0 0) background. 3. Enable a viewable grid and adjust it to 1 x 1 pixels. 4. Zoom in on the upper-left corner of your image until the grid is at one-pixel scale. Top of bitmap border. 1. Create the three components of the upper bar: upper-left, lower-right, and middle. In this example, they are purple, blue, and cyan, respectively (Figure 12.12). Sides and bottom. 1. Create the two sides and the bottom components: left, right, lower-left, bottom, and lower-right. In this example, they are green, yellow, pink, pale-yellow, and red-brown, respectively (Figure 12.12). End result. 1. Our end result would look something like the image in Figure 12.13.

Figure 12.12

Figure 12.13

12.7.4 GuiFadelnBitmapCtrl
This control is used to display an image by fading it in, waiting, and then fading it out over specified times. There is no good way to make this cycle repeat. In fact, the only way to cause the fading cycle to start over is to put the control to sleep and then wake it up again.

Setting Up the Fade


Setting up the GuiFadeInBitmapCtrl is relatively simple. There are three fields that specify the fade timing.
new GuiFadeInBitmapCtrl( myFadeInBitmap ) ( fadeInTime = 1000; II Fade in over one second waitTime= 2000; II Stay visible (without fading) for two seconds fadeOutTime = 500; II Fade out over a half-second
};

The mechanics of this control are quite simple. Upon waking, the control will start to fade in a bitmap over a period of time fadeInTime. Once the fade in is complete, the image will stay faded in for wai tTime. Finally, the image will fade out for fadeOutTime. When the whole process is complete, the engine will set the fieid done to true. Please remember that the engine never sets this to false. So, if you are
493

Part III

Game Elements

relying on this value, be sure to clear it when you put the control to sleep and be double sure that it is saved as false or set to false in the onAdd () method. Interestingly, fadelnTime and waitTime can both be set to zero, but setting fadeOut Time to zero will cause the bitmap to display forever at full alpha, Le., it won't fade out.

Sensing Clicks
This control is often used to display a splash image when starting a game or as an interlude between missions, etc. Users will frequently want to bypass these screens (once they've seen them enough times to stop being impressed with the artwork). As a game player, you probably will recall that the most frequently used way to bypass these screens is either an ESC key press, a SPACEBAR key press, or a left mouse click. TGE has supplied the ability to sense a left mouse click via the click () callback. If the user presses the left mouse button while the cursor is over this control, the click () method will fire (if specified). If you want the other mentioned inputs to be sensed, you'll need to use a GuiInputCtrl (see Section 12.12.3).
function myFadelnBitmap::click( %this ) ( echo("myFadelnBitmap::click() => User clicked the left mouse button.");
};

If you wish to capture key presses on a GUIFadelnBitmapCtrl GUI or any other GUI that does not normally catch them, simply add a 1 x J button positioned at <0 0>, set the accelerator field of the button to the key you want to catch, and use the command field to execute the task you need. Repeat this for every key you need to catch. This is a lot better than using an ActionMap for these special cases.

To stop this control from being displayed, simply remove it from the canvas.

12.8 Text Controls


This section discusses the various controls whose purpose it is to store, display, or take as input, text values.

...

12.8.1 GuiMessageVectorCtrl
This control is normally used to build a chat HUD, but it can be used for a number of other purposes as well. In order to use this control, a MessageVector object must also be used (see "The MessageVector" below). Since the actual data to be displayed is stored in the MessageVector and not this control, we can remove and add GuiMessageVectorCtrl controls at will and not corrupt message data. This control is capable of displaying colorized text.
I

I
I

494

Standard TGE GUI Controls

Chapter 12

Child Only
This control is not allowed to exist standalone. It must instead be made a child of a control that allows for expansion of the child. Thus, if you wish to use it, you must make this control the child of a GuiScrollCtrl. If you do not do this, you'll crash the engine when you try to attach the GuiMessageVectorCtrl to a nonempty MessageVector or when you attempt to add text to a MessageVector that is already attached to it. A suitable definition of a GuiMessageVectorCtrl would look something like the following.
new GuiScrollCtrl()

I I ...
new GuiMessageVectorCtrl(testMessageVectorCtrl)

II
};
};

The MessageVector
So, what is this business about MessageVectors? Well, as noted above, the GuiMessageVectorCtrl only has one job. That job is to display the contents of a MessageVector. The MessageVector is a standalone class that can contain a variable amount of text. MessageVectors don't have any special fields, and thus creating one is as simple as the following.
$myMsgVector
=

new MessageVector();

Once the MessageVector has been created, text can be added to the front or to the back of what is basically a text queue, as follows.
$myMsgVector.pushFrontLine( "some string" ); II Put text at front of queue $myMsgVector.pushBackLine( "some string" ); II Put text at back of queue

We can also insert text in the middle of the queue using the insertLine () method.
$myMsgVector.insertLine( 5 , "some text" );

Later, we can

p~ek

at a line of text in the MessageVector using the

getLineText () method. echo( "Line 10 =>" $myMsgVector.getLineText( 10 ) );

495

---

--- ----_.

Game Elements

At any time, we can remove lines using the popFrontLine () . popBackLine () , or deleteLine () methods.
$myMsgVector.popFrontLine(); SmyMsgVector.popBackLine() ; SmyMsgVector.d~leteLine( 5 );

Interestingly, we can also save the contents of a MessageVector to a file.


SmyMsgVector.dump( "-/chat.log" / "My Chat Log" );

The above example would create a file named "chat.log" in the current mod directory, make the first line of this file equal to "My Cha t Log", and then dump the contents of $myMsgvector to the file. The file is automatically closed at the end of the dump.

MessageVector and Tags


MessageVectors support one more interesting feature-tags. When we add lines of text to a MessageVector, we are allowed to supply a unique integer value (greater than 0) as a tag. Later, we can use these tags to do searches. For a complete treatment of MessageVector syntax, please see Appendix A.4, "GUI Controls Quick Reference."

Attaching a MessageVector
OK, so far we've talked a lot about the place we store OUI text (the MessageVector), but not the control that displays the text (the GuiMessageVectorCtrl). To display the text from a MessageVector, we simply attach it to any currently active GuiMessageVectorCtrl.
testMessageVectorCtrl.attach( SmyMsgVector );

In the above example, we are attaching our previously defined MessageVector $myMsgVector to the GuiMessageVectorCtrl we declared at the start of this discussion (testMessageVectorCtrl). That's it! If we've done everything correctly, the text will now be displayed in our chat HUD (or whatever it is being used as). At a later time, we can disconnect the MessageVector usJng the detach () method.
testMessageVectorCtrl.detach();

496

l-.- _

Standard TGE GUI Controls

Chapter 12

Note that multiple detaches are allowed, but if a CuiMessageVectorCtrl is not attached to a MessageVector, it will print a warning when the detach () method is called. Also note that a single MessageVector can be attached to multiple CuiMessageVectorCtrl controls.

12.8.2 GuiMLTextCtrl
This control is a markup-language-supporting text control (ML = markup language). In addition to printing multi-line text, this control will accept TCE Markup Language (TorqueML) formatted text, allowing us to make changes to the font, font weight, color, etc. A complete listing of the TorqueML tokens and the syntax for using them is supplied in Appendix A.4, "CUI Controls Quick Reference." This control also supports onURL () and onResize () callbacks. To use one of these controls, do the following. 1. Open the CUI that will contain our new CuiMLTextCtrl using the CUI editor. 2. Select appropriate control as "add-parent" for the CuiMLTextCtrl (can be embedded in any control). 3. Add CuiMLTextCtrl. 4. Position, size. and configure CuiMLTextCtrl. S. Add your text. 6. Reflow the CuiMLTextCtrl.

Configuring GuiMLTextCtrl
The CuiMLTextCtrl only has a few new fields.
allowColorChars. This enables colors defined in the selected profile to take effect. I suggest setting this to false and using TorqueML instead.
deniedSound. This is a reference to an audio profile that should be played when an attempt is made to place more text than maxChars in this

control.
lineSpacing. An integer value specifying the number of pixels between

lines.
maxChars. This integer value can be used to place a cap on the number

of characters this control will display. All characters are counted, including formatting characters. Set this to -1 for no limit. text. This string is the initial contents of the control. This is most useful for making ML labels. Note that the CUI editor will clip this at 255 characters, so it is usually best to use the set Text () method instead of static assignment.
497

Part III

Game Elements

Scripting GuiMLTextCtrl

This control can be scripted in the following ways.


Adding. Setting. and Clearing Text

There are two means of adding text to this control.


II Add text without reformatting %control.addText( "Add this text", false );
%control.setText( "Make the text equal to this" );

The first method adds text to the end of the control without optionally reformatting the displayed text. The second method sets all text in the control to the passed content. This method can also be used to clear the control. Simply pass a null string, "".
Formatting and Reflowing

As noted, when adding text we have the option of causing the control to reformat. This basically causes the control to reevaluate the contents and to be sure that everything is displayed correctly. However, forcing a reformat every time we add a line of text might be wasteful if we are adding many lines at once. Thus, we can wait until we are done and then reflow the control at the end.
%control.forceReflow();

This will reformat the control just once. It is worth mentioning that, if you add text and don't reformat or reflow, the text will not be displayed.
Scrolling and Tags
If we have embedded our GuiMLTextCtrl in a GuiScrollCtrl, we can force the contents to scroll to the top.
%control.scrollToTop();

We can also force the contents to scroll to an embedded tag.


%control.scrollToTag( 10 ); II Scroll to tag 10 #10

Tags are special (nonprinting) TorqueML content that can be embedded in our text. This is useful for making context-sensitive help pages.
498

Standard TGE GUI Controls

Chapter 12

TorqueML The TGE Markup Language, TorqueML, is pretty extensive and can do many of the things that HTML can do. A complete listing of the TorqueML tags is provided in Appendix A.4, "GUI Controls Quick Reference," but I'll cover a few concepts and highlights here to help speed you on your way.

Syntax Closure
Unlike modern HTML, TorqueML does not require closure for most of its formatting characters. In other words, once an effect is applied, it stays in effect. The exceptions to this are clipping and hyperlinks, both of which have a closure tag.

Attribute Stacking
Instead of the standard closure mechanisms, TorqueML supplies the concept of an attribute stack. Thus, we can push the current formatting attributes to the attribute stack, apply some changes, print some text, and then pop our old formatting attributes back off the stack.
<font:arial:10> This text is in Arial-10. <spush> <font:arial:14> This text is in Arial-14. <spop> This text is in Arial-10.

Tables and Tabs


TorqueML doesn't really support tables, but it does support a formatting feature that allows us to easily columnize our text in a table-like format. For example, to make a two-column table, do the following.
<t;ab:60> Torque Rocks<br> GPGT Makes Learning Easier<br> long<br> This line will be too

This statement has told TGE to make the first column 60 pixels wide. Subsequently, the first TAB in every line will cause the text following the TAB to move over to pixel 61. Thus, our sample would print out something like the following.
Torque Rocks GPGT Makes Learning Easier This line will be too long

499

- ---------------

Pa~t

III

Game Elements

As can be seen, the formatting is somewhat lacking. It would be better to make the first column of the third line clip instead.

Clipping
Fortunately for us, TorqueML supplies a method of clipping text to a specific pixel width.
<tab:60> Torque Rocks <br> GPGT Makes Learning Easier <br> <clip:58>This line will be too</clip> long <br>

The first column on the third line has been instructed to clip its contents to 58 pixels. Now, when displayed, we get something like the following.
Torque GE'GT This line Rocks Makes Learning Easier long

This may seem a bit strange, but consider the case where you are printing data via a scripted formatting system. In cases like this, you have no good way to know in advance if the data will be too wide for your column, thus, you must clip it to maintain formatting.

Tags
We touched on tags above, but for completeness, we'll discuss them here. Tags are nonprinting TorqueML elements that are used to mark a line for later searching and locating. To add a tag, simply specify it as follows.
<tag:lOO>

Later, we can scroll to the line marked with this tag using the scroll ToTag () method. It's that simple. The only rule to remember is that tags must be unique integer values greater than zero.

12.8.3 GuiMLTextEditCtrl
This control is a TorqueML-formatted text entry. Nearly all of its functionality derives from its parent GuiMLTextCtrl. Its purpose is to provide a "nicely formatted" text entry field. The simplest way to use this control is to pre-specify the font, margins, etc. in the text field. Subs~quent text typed into the control will now follow these formatting rules.
500

Standard TGE GUI Controls

Chapter 12

new GuiMLTextEditCtrl ( TorqueMLFormattedTextEntry ) { text = "<font:Tahoma Bold:22 >";


};

Text that is typed into our example control (above) will now be formatted as Tahoma Bold at 22 points. Escaping This control provides a special field named escapeCommand where we can specify a command to execute when the ESC key is pressed while this control is in focus.
new GuiMLTextEditCtrl ( TorqueMLFormattedTextEntry ) { escapeCommand = "doit();"; II Run doit() when ESC is pressed
};

12.8.4 GuiTextCtrl
This is a label, plain and simple. It displays a fixed (256 characters or fewer) amount of text on one line. It can be updated dynamically from script if needed, but beyond that it isn't very flexible.
new GuiTextCtrl( ourLabel ) { maxLength = 12; text= "Torque Rocks!";
};

When displayed, the above example will print "Torque Rocks", without the exclamation point because we have limited the text length to 12 characters. Changing Labels Subsequently, we can update the contents of the control using the set Text () method.
ourLabel.setText("Torque is Cool");

Again, our text will be clipped because the text we have specified is too long.

altCommand
Interestingly, this control provides an al tCommand action. That is, if the field al tCommand is specified, the function specified there will be called when this
501

Part III

Game Elements

control is active and the ENTER key is pressed. This only applies to the children of GuiTextCtrl, which we will talk about next.

12.8.5 GuiTextEditCtrl
This is a simple single-l!ne text entry control. It is a child of GuiTextCtrl and is thus limited to a'maximum of 256 characters and can be limited with the same mechanisms provided by its parent. This control can also recall prior entries (a history) and allows them to be recalled via the up and down arrows on the keyboard.

escapeCormnand and al tCormnand


Remember that GuiTextCtrl allowed us to specify an al tComrnand? Well, as the child of that control, GuiTextEditCtrl will evaluate the script specified in al tComrnand when the ENTER key is pressed. Additionally, we can specify a script for the ESC key in the suitably named field escapeCommand.

Passwords
If we are using this text entry as a password field, we can tell TGE to print asterisks instead of characters as the user types by setting the password field to true.

Numeric Only
Recall that we can set the profile field numbersOnly to true. Doing so causes this control to only accept numeric inputs.

I'm Full!
Because this control has a limit on the amount of data it can accept, we need a way to provide feedback to the user when they attempt to exceed that limit. This can be done by specifying an audio profile for the deniedSound field. Then, when the size limit is reached and an attempt is made to add more characters, the qeniedSound will play.

Your History?
This control has the nice feature of retaining a history of prior values. They can be recalled using the up and down arrow keys. We, as the designers, can specify a limit on the number of history lines by setting the field historySi ze to any integer value of zero or greater, zero being no history.
502

Standard TGE GUI Controls

Chapter 12

Tab Completion In addition to the commands fired by ENTER and ESC, we can specify that the TAB key will fire a callback. To do this, set the field tabComplete to true and provide a callback definition.
function Gui TextEdi tCtrl: : onTabComplete ( %this ) ( II Do something

Validation As if all the scripts that get called were not enough, we can specify one more. If we specify a script or function name in the val ida te field, it will be called every time this control loses focus. Moving the Cursor Lastly, it will occasionally be useful to either know or set the position of the cursor in the control. Thus, two functions are provided for this purpose.
%control.getCursorPos() %control.setCursorPos( 20 ); II Move cursor after character 20

12.8.6 GuiTextListCtrl
This control is a multiline list of selectable entries. Alone, it can be used to display data, but in concert with other controls (buttons), it can be used as a selection control. Furthermore, this can be made the child of a GuiScrollCtri to allow for long lists. Configuring GuiTextListCtrl This child of GuiTextCtri adds three new fields.
clipColumnText. If we are implementing columns, setting this field to true tells TGE to clip the contents of a column if it is too wide. columns. Again, if we are implementing columns, we must specify a list of

integer widths for each column using this multi-entry integer vector. f i tParentWidth. When this field is set to true, the GuiTextListCtrl will expand to fit the width of the parent and no further. This means that, if the control is embedded in a GuiScrollCtrl, its horizontal bar will not be activated. In short, if our text is wider than the scroll area, it will be clipped. If we don't want our rows to be clipped. we need to set this field to false.
503

- --~---------------

Part III

Game Elements

Now the GuiTextListCtri will expand to the size of the widest line of text, possibly activating a parent scroll control's horizontal scroll bar. Rows and Columns
If we so choose, we can cause text to be formatted into columns. Columns are separated by the TAB character. So, if we have specified a value "50 100 150" in the columns field, we could add some text to our control and expect that the first column should start at pixel offset 50, the second (TAB-separated) column should start at pixel 100, and the third column should start at pixel 150. Don't be confused by the slight variance between this behavior and that of GuiMLTextCtrl. For GuiMLTextCtr!, the first value specifies the start location of the second column. For this contro!, it specifies the location of the first column.

Scripting GuiTextListCtrl This control can be scripted in the following ways. Adding Rows Text can be added to the GuiTextListCtri using the addRow () method.
%control.addRow( 0 , "Some text H
,

1 );

This example specified that we want the string "Some text" to be added at row 1 and given an 10 of O. The row argument is optional, and if not specified, new text is added to the end of the list. However, we always need to specify an 10, but these lOs do not need to be unique and can be zero if you don't intend to use them for any purpose. Changing and Removing Rows
It is possible to change the text in an entry at a later time, but to do so, we

must have specified a unique 10 for the row. Then we can do the following.
%control. setRowByID ( 0 , "Some new text H
);

Here, we have changed the text in the row with 10 0 to "Some new text". If multiple rows have the same 10, the first row with this 10 will be the one changed. We can also remove a row if we choose. In the case below, we will remove a numbered row (2). Please remember that row numbering starts at O.
504
%control.removeRow( 2 ); II Removes row 2

Standard TGE GUI Controls

Chaprer 12

If we have specified a unique ID for a row, we can use that ID to find and remove the row.
%control.removeRowByIO( 3 );

Again. if multiple rows have the same ID, the first row with this ID is the one affected. Clearing the List We can clear a list at any time as follows.
%control.clear();

Getting Row Attributes As most of the time we are accessing rows for selection purposes, there are myriad methods for getting row attributes. We can get the first row number with a specified ID.
%control.getRowNumByIO( 2 ); II Return number of row with 10 2

We can get the ID of a specific row.


%control.getRowIO( 4 ); II Get 10 of row 4

We can get the text in a row.


%control.getRowText( 15 ); II Get text in row 15

We can get the text in the first row with a specific TO.
~control.getRowTextBy10(

12 ); II Get text of first row with 10 12

Finally, we can get the TO of the currently selected row.


%control.getSelected10();

If no row is selected, the above call will return -1.

Row Count We can count how many rows there are with the rowCount () method.
echo("This GU1TextListCtrl has" SPC %control.rowCount() SPC "rows.");

50S

Part III

Game Elements

Navigating
Thus far, we've worried about the contents of rows, but how do we navigate our list? First, we can search for a row with a specific text value, like this.
%control.findTextIndex( "this text" );

The above code will return the first row encountered that has the exact string "this text". If no match is found, the method returns -1. As the user is expected to select an entry from this list, we might also be expected to be able to find it. If a row is selected, we can retrieve its ID using the getSelectedID () method. Sometimes, though, we would like to force an entry to be selected. We can do this in two ways, either by ID or directly by row number.
%control.setSelectedByID( 43 ); II Select first row with ID 43 %control.setSelectedRow( 14 ); II Select row 14

In either of these cases, if the ID or the row does not exist, no row will be selected.

Scrolling
Sometimes when we are selecting a default row, that row may not be guaranteed to be in the visible set of rows (Le., it is off screen in the scroll list). We can force this line to show itself as follows.
%control.scrollVisible( 10 ); II Make sure row 10 is visible

There is no guarantee on the exact location of the line on our screen, but it will be visible.

/De-JActivating Rows It will on occasion be necessary to (de-)activate a row, sayan option is (not) meaningful or available in the current context. Thus, we can toggle whether a row is active.
%control.setRowActive( 10 , false ); II Deactivate row 10

We can check to see if a row is active, too.


if ( %control.isRowActive( 10 ) ) echo("Row 10 is active!");

506

"-----

Standard TGE GUI Controls

Chapter 12

else { echo("Row 10 is not active!");

We would, of course, expect the above code to print: Row 10 is


active!

Sorting
Last to mention but not least in importance is the fact that we can sort our lists. This comes in handy for those of use who are too lazy to be sure entries are in the right order or in cases where it is out of our hands. We can sort alphabetically on a specific column or numerically (again, by column).
%control.sort( 2
I

true); II Increasing sort on column 2


I

%control.sortByNumerical( 0

false); II Decreasing numeric sort on column 0

12.9 Buttons
This section describes the controls used for buttons.

12.9.1 GuiButtonBaseCtrl
This is the base class to all other buttons and tons. Its only job is to provide common fields ButtonCtrl, GuiButtonCtrl, GuiCheckBoxCtrl, This control supports three styles of buttonType field). should not be used to make butand methods for the GuiBitmapand GuiRadioCtrl controls. buttons (selected through the

e Push buttons (buttonType == PushButton). This is your standard button. It depresses when clicked and goes back to its normal state when the mouse is moved or the mouse button is released. eToggle buttons (buttonType = ToggleButton). This is like apush button except that it retains the current state when the mouse button is released. e Radio buttons (buttonType == RadioButton). This is like a toggle button, but this button is also grouped with other buttons. Within the group, only one button may be "on," while all others are "off." Selecting a new button as the "on" button changes all other buttons in the group to "off." All buttons are allowed to have some text in them. This text is set in the button's text field. Not all button types will display the text; GuiBitmapButton specifically does not, although, in the case of a GuiBitmapButton, if the graphic is not available, a default button will be displayed instead and it will display the text. This is a nice debug/design feature.

507

Part III

Game Elements

Grouping Radio Buttons So, we can group radio buttons, but how do we do it? First, all radio buttons that are going to be grouped need to be at the same level; that is, they should have the same parent. Second, to group them, set every grouped radio button's groupNum field to the same nonnegative value. It is perfectly acceptable for different groups with different parents to have the same groupNum. However, only radio buttons with the same parent and the same groupNum will communicate with each other and act like a radio-button group. All other radio buttons will be treated separately. Note that, by default, all radio buttons in a group start off unselected, so you may wish to preselect a button when the interface first wakes up. See below for how this can be done. Getting and Setting Button Data Now that we have our buttons, we need some ways to get and set their values.
It may sometimes be desirable to be able to check the text value of a button

or to change it. For these purposes, there are two methods.


$buttonText
=

%button.getText();

%button.setText( "New button text" );

As mentioned in "Grouping Radio Buttons" above, we may at some time wish to select a button from script. To do this, simply use the performClick () method.
%button.performClick(); II Send click event to this button

Button Scripts Lastly, how do we program the button to do something when clicked? Recall that all children of GuiControl provide a field named command. In this case, command should be a small script or a function call of some sort. This command will be called when the user clicks the button and releases the mouse button, not before.

12.9.2 GuiBitmapButtonCtrl
This control is a skinnable button. Unlike other skinned controls, this control takes a maximum of four normal (nonarray) graphics. Graphics files for this control use the following naming convention.
prefix tag.suffix

508

prefix. Any name for the image file.

Standard TGE GUI Controls

Chapter 12

_tag. One each of the following (based on button state).

n. Normal. Highlighted . _d. Depressed.


_h.

i. Inactive.
suffix. png. jpg, bmp, etc.

. For example. we could provide the four images in Figure 12.14.


Figure 12. '4.
Using GuiBitmapButtonCtrl.

gglogo_n.png (normal)

gglogo_h.png (highligted)

gglogo_d.png (depressed)

gglogoJpng (inactive)

To use these images, we set the bi tmap field to "path + prefix". In other words, we specify the relative or absolute path and the prefix of the filename. The control is smart enough to load all four images based on this information. Specifically, bi tmap would be set to ".jgglogo". If an image file is not provided for one or more of the states highlighted, depressed, or invalid, the normal image will be substituted. Sensibly, the normal image is always required.

A nice shortcut for setting up these buttons is to set the extent to "0 0" in the CUI inspector and then to press Apply. This will cause the CUI to expand to the size of the image file. Nice, eh? Interestingly. the four different images need not be the same size; however, results may vary based on what choices you make here. We can change the bitmap at a later time using the setBi tmap () method.
('\

myButton.setBitmap("full path + prefix");

12.9.3 GuiButtonCtrl
This is a standard button. It defaults to a buttonType of PushButton. All functionality 'comes from its parent, CuiBaseButtonCtrl.

12.9.4 GuiCheckBoxCtrl
This skinnable control displays the perennial checkbox. By default, this control toggles between on and off.

509

Part "'

Game Elements

Table 12.11.

Table 12.12.

Sample image of checkboxes.


'7

Sample image of radio buttons.


.c:~

,.
~

~.

..... '" ..'>


,
-'0

.....; . t1
,~

D
~

'0

Unchecked Normal Checked Normal Unchecked Inactive Checked Inactive

0
1

Unchecked Normal Checked Normal Unchecked Inactive Checked Inactive

1
2

2
3

Skinning
Define a profile with the following settings.
new GuiControlProfile ( aProfileName ) ( / / ... hasBitmapArray = true; bitmap= "path to bitmap array graphic";
);

Provide an image file with the structure in Table 12.11.

12.9.5 GuiRadioCtrl
This is a skinnable radio-button control. It is used when a group of buttons must have only one button set at anyone time.

Skinning
Define a profile with the following settings.

"'

new GuiControlProfile ( aProfileName ) ( / / ... hasBitmapArray = true; bitmap= "path to bitmap array graphic";
);

Provide an image file with the structure in Table 12.12. In order for the radio control to behave properly, the buttons all need to have the same parent and groupNum. In the following example, either "Radio 0" or "Radio 1" can be selected, but not both.

510

Standard TGE GUI Controls

Chapter 12

new guiCon trol () ( new GuiRadioCtrl () ( profile = "GuiRadioProfile";

I I ..
text = "Radio OU; groupNum = "1"; buttonType = "RadioButton";'
);

new GuiRadioCtrl () { profile - "GuiRadioProfile"; I I .. text = "Radio 1"; groupNum = "IU; buttonType - "RadioButton";
}; };

12.10 Menus
This section describes the controls used for menus.

12.10. 1 GuiMenuBar
This semi-skinnable control displays the familiar menu-bar metaphor. By semi-skinnable, I mean that graphic icons can be embedded in menu items, but the bar and the dropdowns themselves are not skinned.

Creating a GuiMenuBar
GuiMenuBar does not provide any new fields. Also, a GuiMenuBar is normally placed at the top of its parent, but in theory it can be placed in any position. A simple definition would look something like the foHowing.
new GuiMenuBar ( myMenuBar ) { position- "0 0"; horizSizing = "width vertSizing = "bottom";
U ;

II
};

Menu Item Icon Arrays Define a profile with the following settings:
new GuiControlProfile ( aProfileName ) { / / ...

511

Part III

Game Elements

hasBitmapArray = true; bitmap= "path to bitmap array graphic";


};

Provide an image file with the structure in Table 12.13.


Table 12.13.

Sample image of menu icons.

..m.... ..,

CoIu.... 2 (1nIIc:tIve)
0
~ ~

-~~
1.

db db

~ ~

Checked Mark Optional Icon 0

Not-Checked Mark Optional Icon 0

Inactive Checked Mark Optional Icon 0

Optional Icon N

Optional Icon N

Optional Icon N

In effect, a CuiMenuBar can have any number of icon rows, but the first (0) row is normally reserved for the "checked" icons. You can of course use any icon for "checking" that you wish, and you can use those icons elsewhere, too. GuiMenuBar Guidelines/Rules The following guidelines/rules apply when building menus. Place and size the initial menu bar using the CUI editor. 2. Open the .gui file (or use a separate .cs) and write code to populate the menu. 3. Text values for menus and menu items should not start with a digit. 4. Menu items may optionally have accelerators. 5. Menus and menu items may be enabled and disabled from script. 6. Menu items may have separator lines (-) between them. 7. Text for menus and menu items can be dynamically changed from scripts. 8. Menu items can be hidden. 9. Menu items can have checkbox behavior and radio behaviors, including the display of a currently checked image in the menu. Menus and menu items can be identified or referred to either by their text 10. or ID. 11. Hierarchical (cascading) menus are not supported. 12. Menus do not support accelerators (only menu items support this).
512

Standard TGE GUI Controls

Chapter 12

Menus and Menu Items


GuiMenuBar supports only one level of menu; Le., it does not support cascading menus, just dropdowns. The parent items in the main bar are referred to as menus, whereas the dropdowns are referred to as menu items. In order to use the GuiMenuBar, it must have menu items to select. To add menu items, we need menus. So, let's Jearn how to add menus first.

Adding, Removing, and Clearing Menus


The normal order of operations for adding menus to the GuiMenuBar is as follows.

II 1 - Clear all menus from menu bar myMenuBar.clearMenus(}; II 2 - Add a new menu myMenuBar.addMenu( "TestO" , 0 }; II ..
repeat step 2

II

Add menu 'Test' as menu 10 0

Later, we can clear the menu again if we wish, destroying all contents, or we can remove just one menu.
myMenuBar.removeMenu( "TestO" );

II

Can use name or IO of menu

Adding, Removing, and Clearing Menu Items


Now that we have menus in place. we can add our menu items.

II Add new menu items to "Test 0" menu myMenuBar.addMenuItem( "TestO" , "SubMenuO", 0 ); myMenuBar.addMenuItem( 0 , "SubMenul", 1 };
I I ..

II ..
~

repeat for other menus

In the above example, we have added two menu items to menu "Testa". When adding these menu items, we can refer to menu "Testa" by name or by its numeric ID (0, in this case). Be aware that each menu item has a per-menu unique ID, not a completely unique ID; that is, menu items in different menus may have the same IDs, but menu items in the same menu may not. As with menus, we can both clear menu items (this removes all menu items from a single menu), or we can remove a specific menu item.
myMenuBar.clearMenuItems( "TestO" ); II Can use nam~ or 10 of menu myMenuBar.removeMenuItem( "TestO" , "SubMenuO" };

II

Can use names or IDs

513

Parr III

Game Elements

Adding Bitmaps and Dividers

In addition to adding normal text to our menu items, we can add dividers.

II This adds a divider as item 2 "-" myMenuBar.addMenultem( "TestOn


We can also add hitmaps.

2 I;

II Use row 4 bitmaps myMenuBar.setMenultemBitmap( "TestOn, "SubMenu1 n , 4 I;


The above statement says to display one of the bitmaps found in row 4 of the bitmap array specified in this GuiMenuBar's profile. Rows start at 0 and have three columns-normal, selected, and inactive. Accordingly, menu-item states determine which bitmap in the row is used. Bitmaps can be changed or removed at any time. To remove a bitmap, simply pass an index of -1 as the row number to the above method as follows.

II Remove bitmaps myMenuBar.setMenUltemBitmap( "TestOn, "SubMenu1 n , -1 ) i


Accelerators and Check Groups

Like buttons, menu items can be accelerated. Also, if we want to add a radiobutton list to a menu, we can. The adclMenuItem (I method comes with two optional arguments. Thus, to add an accelerated menu item, we would do the following.
Add an accelerated menu item that will activate if CTRL + H are pressed myMenuBar.addMenultem( "TestOn, "HelpH, 3, "CTRL Hn Ii

II II

If we wanted to make items part of a radio group, we could do this:

II Make a three choice myMenuBar.addMenultem( myMenuBar.addMenultem( myMenuBar.addMenultem(

radio group "Testl n "Option on, 0, "u , n "Testl "Option 1 n , 1, "" , n "Test1 "Option 2 n , 2,

a o a

I; I; I;

514

In the above example. we have three nonaccelerated items that are all part of the same check group, which is zero. Check groups must be unique within any menu but may be reused between different menus. It is acceptable to use a check group of -1. This means that the checked item will behave like a checkbox instead of a radio control.

,
'--

Standard TGE GUI Controls

Chapter 12

Hiding Menus and Menu Items GuiMenuBar is designed with context sensitivity in mind. Thus, we may want to hide menus or to deactivate them based on our current context. A menu can be (de)activated as follows.
myMenuBar.setMenuEnable( "TestO", false );

II

deactivate

This will make the menu unselectable. Also, the menu text will now display in the profile-specified inactive color (fontColorNA). If it is not enough to enable/disable the menu, we can also (un)hide it.
myMenuBar.setMenuVisible( "TestO", false );

II

hide this menu

Similar features are provided for menu items.

II deactivate myMenuBar.setMenultemEnable( "TestO", "SubMenu1" , false ); II hide SubMenu1 myMenuBar.setMenultemVisible( "TestO", "SubMenu1" , false );
Remember that, when a menu item is inactive, the inactive version of the bitmap will be displayed if a bitmap is used for this item. MOdifying Menu and Menu-Item Text Also in line with context sensitivity is the idea of changing menu and menuitem text. This can be done as follows.
myMenuBar.setMenuText( "TestO", "TestMenuO" ); myMenuBar.setMenultemText( "TestMenuO", "SubMenuO", "TestSubMenuO" );

Script Check Selection We can force a checkable item to be (un)checked from script by using the setMenuItemChecked () method.

II check item 1 myMenuBar.setMenultemChecked( "Test1", "Choice 1", true );


onMenuSelect ()
When a menu is selected, the engine will first attempt to execute the callback onMenuSelect (). Then, it will open the dropdown menu containing the
515

Part 11/

Game Elements

menu's menu items. This ordering allows us to modify the menu's contents prior to its display. The onMenuSelect () callback is documented in Appendix A.4, "GUI Controls Quick Reference."

onMenulternSelect()
Lastly, we need a callback to tell us when an menu item has been selected. It is the onMenult'emSelect () callback that does this for us. This callback is called after the menu item is selected and the mouse button is released. It, too, is documented in Appendix A.4, "GUI Controls Quick Reference."

12.10.2 GuiPopupMenuCtrl
This is a traditional pop-up menu. When a left mouse click is applied to this control, a list will pop up. This list will either be above or below the control depending on its placement, how many entries are in the list, and the nearness of the bottom of the screen (not parent). In the case that the list is taller than the height of the screen or maxPopupHeight, it will scroll automatically. Additionally, each text entry can be themed with a coloring scheme.

Creating a GuiPopupMenuCtrl
The GuiPopupMenuCtrl has only one new field. Its purpose is to control the height of the pop-up menu. With it, we tell the control the maximum number of entries it may display.
new GuiMenuBar( myPopupMenu ) { maxPopupHeight = 4; II Show only 4 entries at a time

II
};

Scheming
No, this is not how we turn the pop-up menu into some kind of evil control with nefarious purposes. Instead, think in terms of font formatting. The GuiPopupMenuCtrl gives us the ability to create font-formatting schemes. We can scheme individual entries. With a scheme, we can specify the font colors for the standard states-enabled, selected, inactive.
myPopupMenu.addScheme( 1 , "0 0 0", "255 0 0", "64 64 64" );

Here, we have create a numbered scheme (1), with an enabled color of black, a selected color of red, and a disabled/inactive color of dark gray. Scheme a is reserved for the values provided in the profile.
516

Standard TGE GUI Controls

Chapter 12

Skinning This control is partially skinnable and uses the same skin as the GuiScrollCtrl. Adding Entries Adding entries to the pop-up menu is simplicity itself. Below, we add two entries to our pop-up menu. The first uses the default scheme, and the second uses the scheme we created above.
myPopupMenu.add( "Entry 1", 0 , 0 ); myPopupMenu.add( "Entry 2", 1 , 1 );

II II

Entry Text, 10, Scheme Entry Text, 10, Scheme

Modifying Entries and Current Button Text When a menu entry is selected, the text in that entry replaces whatever text was previously displayed on the pull-down menu button. Subsequently, we can modify this value using the setText () method.
myPopupMenu.setText( "New Text" );

II

Display "New Text" on button.

Additionally, once we've selected an entry, we can change the text of that entry.
myPopupMenu.replaceText( "Yo" );

II

Change selected entry text to "Yo"

Navigating We will on occasion wish to navigate our pull-down menu from script. TGE provides the ability to find an entry by text:
myPopupMenu.findText( "Yo" );

II

Return entry number of first "Yo"

text by ID:

II %entryText now contains "Entry 2" %entryText = myPopupMenu.getTextBylD( 1 );


ID of currently selected:

II Return ID of current selection (-1 for none) myPopupMenu.getSelected();


and text of currently selected:

II Return text of current selection ("" for none) myPopupMenu.getText();


517

Part III

Game Elements

Lastly, we can set the current selection from script.


myPopupMenu.setSelection( 1 ); II Select entry with 1D 1

Starting Over
Although this control is not as configurable as a CuiMenuBar, it can be cleared using the clear () method. This will remove all entries and all schemes, allowing us to start from scratch.

Sorting
This control can be sorted alphabetically using the sort () method.

Callbacks
The order of calls may be a little tricky if you don't understand it. There are three entry points to the callback stream for this control.
1. If the user opens the menu and clicks on an entry, the order of events is as

follows. Menu closes on click (not button release). If valid selection, onSelect () is called, else onCancel () is called. If command field was specified, specified script is executed. 2. If a script chooses the selection via the setSelection () method: if valid selection, onSelec t () is called, else onCancel () is called followed by corrunand script if it was specified. 3. If a script forces the onAction () callback via the forceOnAction () method: if corrunand field was specified, specified script is executed.
myPopupMenu.forceOnAction();

12. 11 Sliders and Scales


This section describes the controls used for sliders and scales.

12. 11. 1 GuiFilterCtrl


This odd control allows us to specify a multi-knotted spline-like CUI that can be used to create a vector of floating-point values (one per knot), where each value is between 0.0 and 1.0. The control can be used both as an input device and as a feedback device (we can set the position of each knot from script).
518

'---_.

. ...

----_...-.. _-----

---

Standard TGE GUI Controls

Chapter 12

Creating a GuiFilterCtrl
When creating this control, we need to specify an initial number of knots (two at a minimum). We can also specify the initial values for these knots.
new GuiFilterCtrl( myFilter ) { control Points = 3; filter= "0.0 0.5 1.0"; II Initial positions left-to-right II
};

It is perfectly legal to change the number of control points a filter has at a later date by simple assignment.
myFilter.controlPoints 4;

Using for Input


This control is normally used for input. The user can click on a point and drag it up or down. At any time, we can retrieve the current positions of the knots from script as follows.
myFilter.getValue();

The knot values are returned in a vector of space-separated floating-point values, where the first entry is knot a (left), and the last is entry is knot N - 1 (right), where N is the total number of knots.

Using for Output


This control can be updated from script and used as a feedback mechanism. The update is accomplished by passing a new vector of knot values to the control.
myFilter.setValue( " 0.25 0.33 0.66 1.0" );

If you intend to use a filter as an output-only control, you should fully cover the "face" of the filter with another control to block mouse inputs.

Identity Crisis!
OK, the control doesn't experience mental breakdowns, but we may want to "straighten" it out on occasion. By calling the identi ty () method, we can force the control to align its knots on a 45-degree line from 0.0 on the left to 1.0 on the right.
519

--------

Part III

Game Elements

12.11.2 GuiSliderCtrl
This is a numeric slider control. It allows a value between a lower and upper range to be selected using a sliding interface.
Creating a GuiSliderCtrl

When creating this' control, we need to specify an initial number of ticks, initial ranges, and the initial value.
new GuiSliderCtrl( mySlider ) { II 5 inner ticks and two outer ticks ticks = 5;

7 total ticks

II Range:
range
=

[-1.0,1.0] "-1.0 1.0";

inclusive

II Start at 0.0 value = 0.0; II


};

Like the filter control, we can adjust these values later in script by simply assigning them:
mySlider.ticks mySlider.range mySlider.value 3; "0.0 1.0"; 0.25;

Getting Data

We can peek directly at the value, or we can call the method getValue () . The method is provided to enable consistent coding.

...

if( mySlider.value == mySlider.getValue() echo("This is always true");

) {

altCommand
This control executes the script specified with the command field when the slider is released, and if we specify a script in al tCommand, that script is executed every sim tick while this control is "active" and selected.
520

Standard TGE GUI Controls

Chapter J 2

12. 11.3 GuiTextEditSliderCtrl


This is another floating-point slider contro!, but it uses up-down buttons instead of a left-right slider. This control is a bit more flexible in terms of its output, as it uses a standard-C printf-style formatting string. Creating a GuiTextEditSliderCtrl When creating this control, we need to specify an initial format, initial ranges, and the step increment:
new GuiTextEditSliderCtrl( mySlider ) { format= "%5.5f"; II Standard-C sprintf formatting is used range = "-5.0 20.0" II Range: [-5.0, 20.0] inclusive increment = 0.25; II In-/De-crement in steps of 0.25 II
};

This may be sounding repetitive by now, but these values can be changed by assignment at any time.

12.12 Miscellaneous Controls


This section describes various other controls you might wish to use.

12. 12. 1 GuiCursor


TGE allows us to define our own cursors, using a simple image file and some information defining the location of the cursor's hot spot. In order to use a custom cursor, tell the canvas to activate it using the Canvas. setCursor () method.
new GuiCursor(HOWCrosshair) hotSpot = "30 30"; . bitmapName = "./cursorlmages/HOWCrosshair";
};

12.12.2 GuiDirectoryTreeCtrl and GuiDirectoryFileListCtrl


Torque comes with two controls that are designed to be used in tandem but that can be used separately. I will be describing them together, but once we are done discussing them, you should not find it too challenging to separate them.
521

Part III

Game Elements

The first of these controls is GuiDirectoryTreeCtrl. It is used to display the folder structure of a specified directory and subdirectory in our game's "mod path" (directories that our game can see). The second control is GuiDirectoryFileListCtrl. It is used to display a list of files. At first, this might seem redundant to the GuiTextListCtrl. However, the GuiDirectoryFileListCtri is able to auto-populate once we specify a directory to look in, making it nicer to work with for this case.

Creating These Controls


Both of these controls must be created as children of their own GuiScrollCtrl. If we do not do this, the controls will not expand correctly and will generally look bad. Beyond that, there isn't much involved with setting up the default version of each control. If you are customizing your controls, you are allowed to modify the skin texture for the GuiDirectorylteeCtrl. So, let's talk about that next.

Skinning GuiDirectoryTreeCtrl Define a profile with the following settings.


new GuiControlProfile ( aProfileName ) { / / ... hasBitmapArray = true; bitmap= "path to bitmap array graphic";
};

Provide an image file with the structure in Table 12.14. I have shown the image map twice, both uncut (left) and cut (right). If you are modifying this to match your own art or theme, be very careful to maintain the pixel ratios of the original bitmap array. Provide an open-folder and a closed-folder icon. These icons must be located as follows:

Open-folder image ~ must be named "jcommonjuijfolder.png". Closed-folder image

must be named "jcornmonjuijfoidecciosed.png".

Provide a leaf-node icon image

Iiti:l named" jcommonjuijdefault.png".

Scripting GuiDirectoryTreeCtrl This control is a child of the GuilteeViewCtrl (see Section 12.12.15), so it inherits all of that control's functionality. Additionally, it adds two new methods and a callback (Table 12.15).

522

Standard TGE GUI Controls

Chapter' 2

::::""; .'-~=,

:'"-6.";;; .~~:' 1"...._'

. if:.

'~r)-

MeII1Ing Branch back.

Table 12.14.
Skin texture for directory tree.

r
-

Branch to file/folder.

!
-

I t

-E
El-

~
B-

Branch to folder.

Root branch close button (single-branch).

(t)-

e. +(t)-

C?-

Root branch close button (multi-branch).

Final folder close button.

Middle branch close button.

Root branch open button (single-branch).

t
+
10-

t>-

Root branch open button (multi-branch).

Gr

Bottom branch open button.

_....

t
I
D

Middle branch open button.

Down branch connector.

No branches button (empty tree).

523

Part III

Game Elements

Table 12.15.

-.

~:'
"

-"

: ... :

'r

"

r." ....
~
_

~- .. (~:':~"<o~.'~~.,

-~-

.~

.:....,.

':

,.;...

':...

_'::

....

_'._

+_

"t..

. ...

~,,~:~:~~:;:~<\~~t..k:.1

Methods and callbacks for GuiDirectoryTreeCtrl.

setSelectedPath( path getSelectedPath( )

set the path (which will be traversable) to pa tho Return the path that is actually selected (if any).

onSelectPath( path)

This is called when the users clicks on a directory in the tree and passes the full path to that directory.

Please be aware that, although the methods in Table 12.15 sound similar, one is being used to initialize the tree and the other is returning a selection (if any), which is not exactly the same.
Scripting GuiDirectoryFileListCtrl

This control is also a child of the GuiTextListCtrl, so it inherits all of that control's functionality. Additionally, it adds two new methods (Table 12.16).
Table 12.16.

Method for GuiDirectoryFiJeListCtrl.

setpath ( path [ , filter ] )

Display all files in the specified path optionally matching the specified filter. Returns the currently selected file name, if any.

getSelectedFile( )

It is very important to remember that this control inherits the click behavior

of its parent and thus will execute any script that has been specified in its command field when the user clicks on a valid line in the control.
Filtering

Filtering uses the same string-matching rules that we discussed earlier when we learned about Torque's string-manipulation functions in Chapter 10, "Gameplay Scripting." The important thing to remember is that filenames have the path stripped off before the comparison happens, so we can use the filter to exclude flat file names only. For example, to display all GUI files, our filter would be ,,* .gui".

12.12.3 GuilnputCtrl
This control is used to capture all input events. Input events in this case are such things as mouse clicks and/or keystrokes. For every input event, a single callback is fired.
524

Standard TGE GUI Controls

Chapter 12

Getting It All
Understand that, if you use this control any place in the current interface, it will capture all inputs, period. This control can be a rather nasty one, but it serves its purpose, and we can remove it when we don't need it any longer.

Creating
To create one of these, we could use this snippet:
new GuiInputCtrl(gsTestInputCtrl) { profile = "GuiInputCtrlProfile";
);

All Your Base Are Belong to Us


OK, I don't really mean all your base(s); I mean all your inputs. Once created, this control sinks all device inputs. For every input, the following callback is called.
onInputEvent( %this, %deviceString, %actionString, %makeOrBreak )

This callback takes the following arguments.


%deviceString. A string specifying the device name: keyboard, mouseD, etc. %actionString. A string specifying the action: a, b, tab, buttonD, etc. %makeOrBreak. Only applies to release of special device buttons and modifier keys, false for all others.

Because it would be sheer madness to try to cover all the inputs and what they mean, a GuiInputCtrl sampler has been provided to allow you to see what the inputs are. You will find it in the GUI Sampler part of the kit. To see it, run the GPGT Lesson Kit and select GUIs Sampler -7 GuilnputCtrl.

12.12.4 GuiMouseEventCtrl
This control is used to capture a large variety of mouse inputs. The designers of TGE decided to limit each control to only capture and react to inputs that were normally pertinent to that control. However, they knew that special cases would arise in which the user might want to capture a large variety of inputs. Thus, the GuiMouseEventCtrl was born. It captures all of the following events. Left Mouse Button Press Right Mouse Button Press Mouse Move Left Mouse Release Right Mouse Release Mouse Enter Left Mouse Drag Right Molise Drag Mouse Exit
525

Part III

Game Elements

Additionally, it handles the following modifiers. Left Shift Left Control Left Alt Right Shift Right Control Right Alt Either Shift Either Control Either Alt

Please note that a drag is mouse motion with a button pressed, entering means to enter the bounds of the control, and exiting means to leave the bounds of the control. Mouse moving is like dragging but without the button pressed. Configuring There isn't much involved in setting up one of these controls. Simply place it as a child of any other control and be sure it covers the hot area where you want events to be recorded. You can even make a GuiMouseEventCtrl a child of another GuiMouseEventCtrl if you need to. GuiMouseEventCtrl Callbacks To acquire information from this control, write a set of general callbacks scoped to GuiMouseEventCtrl or specific ones scoped to the name of your control with the following form.
function myMouseEventCtrl: : EVENT_NAME ( %theControl , %modifiers , %point , %clicks ) { / / ...

This callback will respond to an event EVENT NAME and will receive any modifiers (SHIFf, CTRL, ALT keys that are pressed at time of the event), the location of the mouse relative to the < 0, 0> in the canvas (not the GuiMouseEventCtrl control), and the number of clicks that were recorded within the last half-second (a-no clicks, I-single click, 2-double click, etc.).

EVENT NAME
The possibilities for EVENT_NAME are shown in Table 12.15.
526

--------

----~-

----

Standard TGE GUI Controls

Chapter 12

::

.:~,~~::::;-.~'~~v ~~ ;~:-~~~~~0;!.~~.)~ ,~:~r~~ft~:


onMouseDown onMouseUp onRightMouseDown onRightMouseUp onMouseMove onMouseDrag onRightMouseDragged onMouseEnter onMouseLeave

'

;~.......

:.
'

to.. I~ ... _ _

.
I '.

~. ~~ :.~i~j/~~

_ ,'"

Table 12.15
GuiMouseEventCtrl callbacks.

Left mouse button pressed. Left mouse button released. Right mouse button pressed. Right mouse button released. Mouse moved while no button is pressed. Mouse moved while left mouse button is pressed. Mouse moved while right mouse button is pressed. Mouse entered control region. Mouse exited control region.

%modifiers

The %modifiers argument is a bitmask that can be logically compared against the values in these global variables:
$EventModifier: :LSHIFT $EventModifier::SHIFT $EventModifier: : LCTRL $EventModifier::LALT $EventModifier: :RSHIFT $EventModifier: : RCTRL $EventModifier::RALT $EventModifier: :CTRL $EventModifier: :ALT

Note that the unadorned version of SHIFf, CTRL, and ALTwili compare true if any key of this variety is pressed. The code to check for a specific modifier or set of combined modifiers looks like the following.
if( %modifier & ( $EventModifier: :LSHIFT I $EventModifier: :ALT ) ( $EventModifier: :LSHIFT I $EventModifier: :ALT ) ) ( echo( "The LEFT shift key is pressed and one of the ALT keys is pressed." ); } .
==

%point

The first time you use this control, you may be disappointed to find that the click point that is passed into the callback is always relative to < 0,0 > in the canvas. Don't worry, though. If you need to calculate the position of the click relative to the upper-left corner of the control that captured it, simply use a script like the following.
function myMouseEventCtrl: :onMouseDown( %theControl f %modifiers %point f %clicks )
f

527

Part III

Game Elements

%tmpControl = %theControl.getGroup(); %Offset = %point; while( isObject( %tmpControl )


{

%Offset = vectorSub( %Offset , %tmpControl.position ); %tmpControl = %tmpControl.getGroup();


}

//

...

Figure 12.15.

This code iterates upward through each parent until we get to the root control, and along each iteration it subtracts the position of that control relative to its parent from the original click point. By the end of this loop, the variable %Offset contains the position of the click relative to the < 0,0> coordinates of the control myMouseEventCtrl.

Leh-aligned tree views. a. Simple tree.

%clicks
The last argument is the click count. When We click in this control, the control will increment an internal click counter to 1. Then, it will add any subsequent clicks to this counter for the next half-second. After that time, the counter goes back to 0, then 1, and accumulates for another period, ad infinitum. The purpose of this mechanism is to allow us to differentiate clicking styles-i.e., single click, double click, triple click, etc.

,oxa

Adi_"UI,Otell" Slf?lO,,'ll'

1(X)4: CU,nt01tw., Sh:.. G~..., m:Je:: QgiO,eloI"


10Je.
~Im.row,

O~IC)..h""o\o1p,

'11""'0'.11'

12.12.5 GuiTreeViewCtrl
This control is used to display a left-aligned tree. We are accustomed to seeing these used for displaying directories and data where there is some hierarchy and/or inheritance associated with the data. Although this control can be used to make simple and elaborate trees (Figure 12.15), I will only be discussing how to make simple trees. Why? Because the elaborate tree mechanism was added to enable the creation of a more detailed Inspector tool. Therefore, the only icons available are those used by the Inspector. I'll list the syntax for the elaborate tree, but you will have to dig into the engine if you want to try to use it for your projects. By that time, you would likely be expanding the icon list anyway, so further discussions between us on this topic would be a wash.

1007. TCf'6101l1!'

5,mO'.",

1ClJ1: CIl.n'C.Illlll.....O..... p !l1""O

1641: O.t.ElJ.akOr6olol,' SIIttGrell,


10<:1 ....lJIIi.IP'.p.rty..., Mn....'P"'

b. Elaborate tree (inspector-specific) .

Creating a GuiTreeViewCtrl
To create a tree view, simply create a GuiScrollCtrl and then add an instance of GuiTreeViewCtrl as a child. The tree view relies on the scroll control to handle resizing and, as you might imagine, scrolling.

528

'---

Standard TGE GUI Controls

Chapter 12

Configuring GuiTreeViewCtrl

Depending on how we want to use this tree, there is either very little to do or a great deal to do. We will start off talking about the basics and then move on to the harder stuff. To set up the tree, we must specify a few fields. Those fields have the functions and effects shown in Table 12.16.

.
: _ .. ' t

"
.. : ,

"

,
-_\..n:v".

.~.~

.. .

".

. ', . :'

'-

tabSize textOffset

This is the pixel size used to indent subtree items.


<\'. ,
~ ~--

.. ...

.
"

Table 12.16.

Fields for setting up a tree view.

This is the pixel offset between the end of the tree image and the text describing a level in the tree. If this value is true, a row may be selected anywhere parallel to the item; otherwise, the user will be required to click directly on the text or icon to select it. Not adjustable, but specifies the height of a line and is based on the tallest item in the line. If set to true, the tree is reset every time it goes to sleep. You may ignore this field. Allows the user to highlight multiple entries in the tree.

fullRowSelect

itemHeight

destroyTreeOnSleep mouseDragging multipleSelections

Skins and Icons for GuiTreeViewCtrl

This control uses the same bitmap array, folder open/closed icons, and leaf node icon as are used for the GuiDirectory'freeCtrl (Section 12.12.2). Please refer to the skinning directions for that control to learn more about the basic skin and icons used here. Elaborate Icons This control may also display a limited set of predefined icons. Please note again that the purpose of this feature is to support the new Inspector, which has nice icons depicting various object types. If you want to start digging and modifying this feature to use in your own creations, one of the first things you will have to do is build a library of icons. A Gui'freeViewCtrl will try to build an icon library every time it wakes up by calling the callback onDefinelcons () . If you want to include icons in your tree, you should create the icons and store them in a fixed location. Then, use a callback to build the control's icons library as follows.
function GuiTreeeViewCtrl::onDefinelcons( %theControl ) { %icons = "path/icon file nameD" @ "." @

529

Part III

Game Elements

"path/icon_file_namel" @ "." @ "path/icon_file_name2" @ "." @ "path/icon file_name3" @ "." %theControl.buildIconTable( %icons );

Adding Items to a Tree

We may populate a tree in one of two ways. We can either manually add new items (lines of text) to a tree, or we can use the tree to open a SimSet. Let's discuss the manual method first. To manually add an item to an existing tree, we simply write some code like the following
%myTree.insertItem( 0 , "Item Text" );

This statement will insert a new item into the tree and attach it to root (entry 0). This item will display the string "Item Text" in the tree.
Parent Indexes

Figure 12.16.

When manually adding elements to a tree, each item added to the tree is assigned an index. Later, when we want to add a new item into the tree, we must remember this index and add our new element to the index. Please note that indexes are never reused for any individual tree. For example, the following script will produce a tree like Figure 12.16.

Newly inserted entries.


! ntry

II Creates index 1 attached to root (0) %myTree.insertItem( 0 , "entry 1" ); II Creates index 2 attached to 1 %myTree.insertItem( 1 , "entry 2" ); II Creates index 3 attached to root (0) %myTree.insertItem( 0 , "entry 3" ); II Creates index 4 attached to 3 %myTree.insertItem( 3 , "entry 4" ); II Creates index 5 attached to 2 %myTree.insertItem( 2 , "entry 5" );
You should note that the insertItem () method returns the index for the element it just inserted, so you don't have to count or anything heinous like that. Instead, just save the return values if you need them at all.

8-(5J;
L

I! nt IV

:2

entry 5

E! ntry

entry 4

530

Standard TGE GUI Controls

Chapter 12

Assigning a Value
It is possible to give each entry a value in addition to text. This value can later

be retrieved and can be any valid string. To create an item with a value, do the following.
%myTree.insertltem( 0 , "entry 10", "oops" );

If we called this on the tree we just created, the tree would have a new folder with the text "entry 10", and it would have a value of "oops" stored at that entry (not visible). (See Figure 12.17.) If we recall the above discussion of "Parent Indexes," it will also be clear that the ID of this item is 6, as in the sixth item we have added.

Figure 12.17.
Entry inserted at end of list.

Inserting Icons

a..o
I! ntry

entry 1 entry:2 entry 5

In order to use icons in your image (instead of the default folders). use the insert Item () method.
%myTree.insertltem( 0 , "some text", "Sun" I;

(51 entry J L entry 4


10

This code will produce a tree entry with the "Sun" icon. The full syntax of insertItem (I is as follows.
insertltem( parent id , text [ , value, iconString , normallmage , expandedlmage ] I;

Opening a SimSet
The second way to populate a tree is to have it open a SimSet. To do so, simply use the method described in Table 12.17.
, ;';j."-:--'
..,...'....
open ( setID [ , editable I )
~.1
-.' '". ",.

'.~

.'

-.,,'

Table 12.17.
Opening a SimSet.

This will populate a tree with the contents of a SimSet identified by setID. This set will be traversed, and all sets within will be traversed, until all branches have been followed to a leaf. Optionally, we enable or lock the Simset by passing true or false in the position of edi table. If the set is locked, we won't be able to use tree methods to modify it.

A sample open looks like the following.


II Open a SimSet and lock it (not modifiable) %myTree.open( %mySimSet , false I; 531

--- ------

Part ,'II

Game Elements

Clearing Trees
We can empty a tree from script as follows.
%myTree.clear();

This will empty the tree of items, but it will not modify any SimSet that may have been loaded. '

Counting Items
It is possible to count the number of items currently in a tree as follows.
echo ("myTree has ", %myTree.countltemsO , "items in it.");

For our current tree, the above code would produce the following.
myTree has 6 items in it.

Finding Items
Once we have a tree, we can search for items in the tree by name as follows.
'item
=

%myTree.findltemByName("entry 10"); II Returns 6

If this were cailed on the tree we created above, the variable %i tern would contain the value 6.

Querying Items Directly


Once we have an item ID, we can get information about the item's text and value as follows.
%text = %myTree.getltemText( %item ); II %item contains 6 %value = %myTree.getltemValue( %item ); II %item contains 6 echo( "Tree entry: ", %item , " has text label: " , %text , ", value: ", %value );

Assuming we are writing these sample snippets in order, the above code will produce the following.
Tree entry: 6 has text label: entry 10, value: oops

Editing Items

Jt is possible to manipulate the contents of an item after we add it like this, resulting in the new tree shown in Figure 12.18.
532

Standard TGE GUI Controls

Chapter 12

%myTree.editltem( litem , "entry 6" , "fixed" );

Figure 12.18.
Editing an entry (item).
'. I! ntry

Now. if we reran our prior query code, we would get the following.
Tree entry: 6 has text label: entry 6, value: fixed

Don't forget that, if we opened a SimSet in the locked state. this will not work-i.e. no changes will be allowed.
,DeJseleding items

EJ....o entry:2
L L
,entry 5 entry 3 entry 4

The user may select and deselect an item from the tree using a mouse, keyboard. or other device, but sometimes we will want to modify selection states from script. To select an item in our list we do the following.
ImyTree.addSelection( litem ); II "entry 6" now selected

entry e

Or we can set the selection/deselection status of an item as follows.


%myTree.selectltem( litem , false ); II "entry 6" is deselected

It should be noted that addSelection () does not return a value, but


select Item () will return true or false to indicate success or failure (bad item or unable to modify). On the flip side, we can deselect the previously selected item (as we just did with select Item ()). %myTree.clearSelection() ;

Or we can target a deselect.


II De-select "entry 6" (already'not selected) %myTree.removeSelection( litem );

Neither of these two methods returns a value.


Querying Selected Items

Once an item is selected, it is possible to query that item as follows.


litem
=

%myTree.getSelectedltem(); II Will be 0

533

~~--~~--

Part III

Game Elements

It should be noted that, in the case of multiple selections, this will only ever

return the ID of the last selected item, and if no items are selected, it will return O.

Expanding Items
Another manipulation that we might want to do from script is the expansion and collapse of the tree or a branch of the tree. This can be achieved as follows, producing the results shown in Figure 12.19.
%myTree.expandItem( 1 , false ); II Collapses branch 1 %myTree.expandItem( 3 , false ); II Collapses branch 3 %myTree.expandItem( 4 , true); II Expands 3 and then 4
Figure 12.19. Collapsing and expanding folders.

t t
... collapses ...

!ntry 1 !ntryJ ! ntry 6

~
_

!ntry1

'!ntryJ !ntry4

!ntry 6

. .. expands ...

Removing Items So, what about removing items? Easy. We can remove an item using its index.
%myTree.removeItem( 6 ); II Remove item that we labelled "entry 6"

Or we can remove the current selection.


%myTree.addSelection( 1 );

%rnyTree.deleteSelection() ;

In either of these cases, if we had instead opened a SimSet, and if we had locked it, no deletions would be allowed.

String Operations
There is a cool feature that is often used to create paths and other constructs:
%rnyString

%rnyTree.getTextRoot( 4 , "I" );

echo( %myString );

534

This code will produce the following output.

Standard TGE GUI Controls

Chapter 12

/entry 3/entry 4

Please note, the second argument in the above call to getTextRoot () is an optional delimiter and can be a string.

Tree Relationships
To round out the GuiTreeViewCtrl's set of methods is a short list of methods used for getting item IDs based on an item's position in the tree (Table 12.18).
,

......

~-:

..

;.,Method
)

DescrIption
Returns the ID of the first child of this i tern, or 0 if i tern has no children.
)

Table 12.18. Getting item IDs.

getChild ( item

get Parent ( item

Returns the ID of the parent of this i tern, or 0 if item is root. Returns the ID of next entry in same branch as item (below i tern), or 0 if no such entry' exists. Returns the ID of prior entry in same branch as item (above i tern), or 0 if no such entry exists.

getNextSibling( item)

getPrevSibling ( item

GuiTreeViewCtrl Callbacks
No control would be complete without adding a few callbacks. GuiTreeViewCtrl is no exception and adds the callbacks shown in Table 12.19.

m.~! ,'~'**

Called when...
)

Table 12.19. Callbacks for GuiTreeViewCtrl.

onAddSelection( 10 onOeleteSelection() on1nspect( id


}

.. a new item is added to a SimSet tree, .. an item is deleted. same as on Select () except only called on leaf nodes.
)

...

onRernoveSelection( item

." i tern is deselected. .. mouse is clicked over a SimSet object item. Passes in <x, y> position of click and object ID. Same as above for non-SimSet trees. ... an item is selected in the tree. id will contain the node's text for a normal list, the field name for SimSets when the selection is not an object, and the ID of an object if the selection is an object.

onRightMouseOown( x , y , id ) onRightMouseOown( x onSelect( id


)

y }

onUnselect( id

Reverse of onSelect () .

535

Pan III

Game Elements

12.13 Summary
In this chapter, we covered a massive load of TGE standard GUT topics. This chapter was structured to teach about GUIs in general and then to lead you through the various techniques for using the 35 most commonly needed and used controls (the canvas is a control, too), It is also structured to act as a reference. In addition, a complete appendix (Appendix A,4, "GUI Controls Quick Reference") is supplied that contains almost all of the information in this chapter (in a more succinct form) and completely documents all fields, methods, and callbacks (some of which are not mentioned at all in this chapter) . In the beginning, we discussed the fundamental concept of the canvas. We learned about the two categories of controls it contains: dialogs and everything else. We then learned the difference between the canvas's current content and the pushing and popping of dialogs which float above that content. We also learned that all interfaces are constructed by stacking controls on top of controls, and that stacked controls are the children of the parents they stack upon. Our next topic was input capture. We explored the concept of mouse inputs to GUI layers using the falling marble analogy. Then we examined the first-responder concept, which is used in older versions of the engine (prior to version 1.4) to help sort out input rules between controls on the same level in a layer. Next, we looked at focus and came to understand that focus can be attained by clicking in a control or by tabbing to it from another control. Lastly, we looked at modality (also not used after version 1.3), which is used to force layers to take ownership, or conversely to allow it to be taken away. Done with general topics (for now), we jumped into a discussion of the CUI profile. We came to understand that these are templates containing information about bitmaps, borders. fill details, fonts, text formatting, and input behavior, which are specified and then used by subsequent controls to define basic behavior and presentation, Finally, we got to our first placeable contro!' the root class to all controls, CuiControl. We spent time examining its use of profiles. Then we looked at how to specify and modify extents, position. and sizing. Next we learned that any GuiControl or child can be visible or not visible, After that, we talked about the use of accelerators to tie controls to keyboard and other events. Then we examined the serious topic of conunand and al tCommand, two fields that can contain scripts that will be executed at specific times based on the type of control they are specified' for. We also examined the $thiscontrol variable, which is set prior to any and all c"allbackjcommandjaltCommand calls. Lastly, we talked about this contro.! being awake, asleep, active, and inactive, as well as how this affects its and its children's behaviors.

536

l_ _

Standard TGE GUI Controls

Chap[cr 12

Before continuing in our discussion on individual controls. we swung back and talked about a general topic: skinning. We learned that many controls can be skinned. This led to a discussion of bitmap arrays, the rules for organizing them, and a walk-through creating one. For the remainder of the chapter, we blazed our way through control after control in the following categories. Container controls. Frames, scrolls, stacks, panes, tab books, and windows. Backgrounds and borders. Bitmap borders, bitmaps versus chunked bitmaps, and the fade-in bitmaps. Text controls. Message vectors, Torque Markup Language text dis players and edit areas, labels. single-line text edits, and the very useful text list control. Buttons. Skinned buttons, plain push buttons, and specialized skinned check boxes and radio buttons. Menus. Menu bars and pop-up menus. Sliders and scales. The specialized spline (filter) control, a horizontal slider, and a text slider. The grab bag (miscellaneous controls). Cursors, directory viewers, an input capturing control, a mouse capture control, and the generic tree viewer. If you have examined the samples that come in the GPGT Lesson Kit Gurs Sampler (start the GPCT Lesson Kit and click the "GUls Sampler" button to see these), you will be well on your way to making Lise of each of these controls to make your own interfaces. To help accelerate this learning, we will examine the creation of several interfaces as the topic of our next chapter.

537

i~

__

Chapter 13 Game Interfaces


13.1 Game Interfaces
As we established earlier, all games have some minimum set of interfaces. In this chapter, we will design two sets of interfaces that we can later use when we make games. The purpose of these interfaces is twofold. First, they are learning aids. We will learn how to make simple interfaces, combining several basic GUI controls. Second, they can be used over and over for demo games and prototypes. In the future, we can skip right to working on game content without needing to deal with the mundane items like menus, SPla~h screens, etc. The interfaces we will be designing in this chapter are as follows. A-II-0-f-th-e-----....

11=

Splash screens. Splash screens are those GUIs that get displayed when the game starts or during interludes. Games may have multiple splash screens, each providing some information such as game title (screen), . . . company Iogos, copyng h t m formatlOn, etc. For t h' samp Ie, t h ere WI'11 b e IS . Ias h screen. It WI'11 b e use d to d'ISP Iay a h ypot h ' I company Just one sp etlca logo. Menus. As with splash screens, games may have many menus. We'll keep our lives simple and provide a single (main) menu. . Credits. Because we don't want to forget to thank those who have helped 'I'k us to create our won d er fu I game, we 'II nee d a cre d'ItS screen. T h'IS IS 1 e
a splash screen except that it will list our credits information and is usually not displayed until the end of the game, or on demand from the main menu. We'll choose the latter.

interfaces we will discuss in this chapter are provided In a k' completed' and t t th wor Ing s a e Kit. You In e GPGT Lesson may view any of them at any time by running the GPGT Lesson Kit. clicking the "Interface S I" b tt amp er u on. and then clicking the button that has the name of the interface you wish to examine. Additionally, you may add new interfaces to this kit. Simply follow the direction supplied in Appendix B. "GPGT Lesson Kit Docs,"

At the end of this chapter, we will have made two versions of each of the above interfaces: one set in a "Toon" theme, which we will make together, and the second set in a "Tech" theme, which you should make to practice. After we have created these basic GUIs, we will work together and make some common HUD interfaces, including the following.

Counters. We will make some generic counters that can be used to track any numeric information in the game. Vertical feedback bars. We will make some generic vertical feedback bars that graphically display the values in the range 0.0 to 1.0. Strip compass. Although a good compass should be made in C++, we'll make one using just standard GUI controls and scripts to prove that you can in fact prototype just about anything in TorqueScript.

539

Parr III

Game Elements

At any'time, you may look at the finished product of all interfaces by running the CPGT Lesson Kit and clicking on "Game Interfaces" from the main menu. Please note that it is best to create the following examples in order because I will only give detailed explanations the first time we see something new. Subsequently, I may gloss over the same topic. Therefore, unless you have seen the prior explanations, some discussions may be confusing.

13.1.1 Before We Start


Let's discuss our design method. You can make new interfaces in two basic ways.

If you're comfortable with this method, please feel free to use it. If you don't know how to do this, please go back and review Section 3.14, "The GUI Editor." 2. I prefer to make my interfaces from a blank template. That is, I'll take an interface I already have. copy the .gui file to a new directory, cut out the fat, and then make sure the new file gets loaded by the client. Once I've done this, I can just pick my new interface out of the named list of current interfaces and edit it.
1. You can use the GUI editor and create a new interface.

In the following pages, we will be making these interfaces and HUDs using the second method. Unless otherwise specified, the starting .gui files will all contain the following code.
//--- OBJECT WRITE BEGIN --new GuiControl( useAUniqueNameHere ) { profile = "GuiDefaultProfile n ; horizSizing = "width vertSizing = "height"; position = "0 Off; extent = "800 600 u ; minExtent = "8 2 ff ; visible = "In;
ff ;

'.

);

//--- OBJECT WRITE END ---

For each CUI to be created, take the above code and do the following: 1. Create a directory somewhere under" \client\ui\". For example, in the GPGr Lesson Kit, the Splash (Toon) interface is located under the directory \client\ui\200_GameGUls\ggsSplashToon\". 2. Copy the above code into an appropriately named file. Splash (Toon) is in the file "ggsSplashToon.gui".
N
"N

540

.,'---_.

------"-,--.,

-"-'-' ... _ ..

----,--- - - - - - - - -

Game Interfaces

Chapter 13

3. Make sure the file is executed from the initClient () function (usually) located in " \client\init.cs".
N

Now, when we (re)start the CPCT Lesson Kit and start the CUI editor, we'll find our newly loaded interface in the existing interfaces list. If you don't find it, check the log for errors. I always mistype something; maybe you did, too.

13.2 Toon-Themed Interfaces


Our first set of CUIs will be designed using a sort of carefree cartoon theme. Yes, I know, the art in Figure 13.1 isn't that much like a cartoon, but please bear with me. The thing to concentrate on is consistency in our theme.
Figure 13.1. Cartoon-themed screens.

Splash Screen

Main Menu

Credits SCreen

13.2.1 Splash (Toon)


Our first interface is very simple. We can make a splash screen with just a few CUI controls. For this example, we just want our made-up company logo to be splashed for a few seconds, and then we want to automatically proceed to the main menu. The perfect GUI control for this kind of screen is a CuiFadeInBitmapCtrl. Let's look at how to create this interface.

The Splash Interface Make a graphics file in your favorite editor that looks something like Figure 13.2. The image should be the highest resolution you expect the user to play at, or perhaps one scale larger. Ours is a 1024 x 768 24-bit color image, saved as a lPEG file. Copy your file, or the one from the CPCT Lesson Kit, to the directory where your .gui file is. Start the CPCT Lesson Kit.

Figure 13.2. Splash screen.

541

Part III

Game Elements

Now, using the GUI editor, open the splash screen interface and add a GuiFadeinBitmapCtrl control with the following parameters. new GuiFadeInBitmapCtrl( ggsSplashToonFadeinBitmap ) { profile = "GuiDefaultProfile";
horizSizing "width"; vertSizing = "height";

position = "0 0"; extent = "800 600"; minExtent = "8 2"; visible = "1"; bitmap = "./splash";
wrap "0"; fadeinTime "1000"; waitTime = "2000"; fadeoutTime "1000";

done
};

"0";

You'll want to use your own name for the GUI, but it does need a name because we're going to write some code for it. The important things to note are the following. horizSizing and vertSizing use "width" and "height", respectively. This GUI will always resize itself to the extents of its parent. wrap is set to false. The control will fade in over one second, wait for two seconds, and fade out over one second.
The Splash Interface Code

Now, we have to make some code to go with this interface. Why? For a few reasons.
1. We need to set the done parameter to false every time this interface is

added, just to be safe. Otherwise, we could accidentally save the interface and it would later skip right to done. 2. Once the GUI is done fading out, it won't actually do anything else automatically. We need to check for the done state and move on to the main menu. 3. We want to allow the user to skip this screen by clicking the mouse, and this requires a little code. Setting done to false is easy, but writing code to patrol for done-although simple-is not a one-liner. In these GUIs, we'll be using the event-manager code that is provided with the GPGT Lesson Kit, and separately on the CD ("Base\ Scripts\EGSystems"). If you're not familiar with it, the event manager is a set of
542

Game Interfaces

Chapter 13

scriptObject classes that manage various kinds of events and sequences. Appendix A.6, "Scripted Systems Quick Reference," outlines how this code works. In case this is your first time seeing it, I'll explain what the code does below.
onAdd () and onRemove ( )

It will be the job of onAdd () to set done to false and to create our task manager. on Remove () will be responsible for destroying the task manager when the CUI is removed.
function ggsSplashToonFadeinBi tmap: : onAdd ( %this ) %this.done = false; %this.taskMgr = newTaskManager(); %this.taskMgr.setTarget(%this) ; %this.taskMgr.setDefaultTaskDelay(100) ; (

II add a repeating task to the task manager and start it running


%this.taskMgr.addTask( "checkIsDone();", -1 );

This onAdd () console method does the following.


It is scoped to our (named) CUI ggsSplashToonFadeinBitmap. It sets the done field to false. It creates a new task manager using the newTas kManager () helper function.

Because we want this task manager to call all functions it executes in the scope of this CUI ("ggsSplashToonFadeinBitmap"), we'll tell the task manager to target %thi s (the handle of our CUI). Also, this task manager will loop continuously, and we want it to use a default value of 100 milliseconds for the loop.

Lastly, it adds one task (checkIsDone () ;) and tells the task manager that this task is always rescheduled (i.e., it repeats forever) .
function ggsSplashToonFadeinBitmap: :onRemove( %this ) ( %this.taskMgr.stopSelfExecution(); %this.taskMgr.clearTasks(); %this.taskMgr.delete();

This onRemove () console method does the following.


It is scoped to our (named) CUI ggsSplashToonFadeinBitmap., It assumes the task manager is running and stops it.

543

Pare III

Game Elements

It deletes all outstanding tasks in the task manager. It tells the task manager to delete itself (the prior two steps were included to show that they exist, but be aware that deleting a task manager will stop execution of outstanding tasks, and clear the task list automatically).

onWake () and onSleep ()


Now that we're ready to go, the onWa ke () method will be responsible for starting the task manager polling, and onSleep () will be responsible for stopping it.
function ggssplashToonFadeinBitmap: :onWake( %this ) II Need to clear this as it only gets set to true II by the control %this.done = false; %this.taskMgr.selfExecuteTasks( true);

This onWake () console method does the following.

It is seoped to our (named) CUI ggsSplashToonFadeinBitmap.


It sets done to false again. This is a bit of overkill, but it is the safest way to deal with this. Now, the CUI is guaranteed to replay every time it wakes.

It tells the task manager to start polling (self-executing). The argument t !Cue is telling the task manager to ignore any times specified for tasks and to instead use the task manager's default value, which we earlier set to 100 milliseconds.
function ggsSplashToonFadeinBitmap: :onSleep( %this ) ( %this.taskMgr.stopSelfExecution() ;

This onSleep () console method does the following.


It is scoped to our (named) CUI ggsSplashToonFadeinBitmap.
It tells the task manager to stop.

click ()
If you recall, we want the user to be able to click the mouse at any time to skip this splash screen. The GuiFadeinBitmapCtrl control provides a callback named cl i c k () that is called when the control is awake and the mouse is clicked. We'll create an instance of this seoped to our CUI and make it do some work.

544

'----

Game Interfaces

Chapter 13

function ggsSplashToonFadeinBitmap: :click( %this ) ( %this.done = true;

This click () callback does the following.


It is scoped to our (named) GUI ggsSplashToonFadeinBitmap. It sets done to true (we let the checkIsDone () task do all the real work).

The checkIsDone () Task


OK, we're almost done. The last bit of code is the console method that is supposed to check for done. When done is true, the splash screen will load the main menu.
function ggsSplashToonFadeinBitmap: :checkIsDone( %this ) ( if( %this.done ) ( %this.taskMgr.stopSelfExecution() ; Canvas.setContent(ggsMainMenuTech);

This checkIsDone () console method does the following:


It is scoped to our (named) GUI ggsSplashToonFadeinBitmap. It checks for done equals true, and if it is true,

it stops the task manager, and it sets the canvas content to the main menu (which we haven't made yet).

13.2.2 Main Menu (Toon)


We now have one working interface. Now, let's make the main menu interface. Before you start, make sure you've got a template .gui file (like we described at the beginning of this chapter) in a directory where your main menu will be located and be sure that it is getting executed. For this interface, you should use a template like the following (use your own name for the GUI).
new GuiChunkedBitmapCtrl(ggsMainMenuToon) profile = "GuiDefaultProfile"; horizSizing = "width"; vertSizing = "height"; position = "0 0"; extent = "800 600";

545

Part III

Game Elements

minExtent = "8 2 u ; visible = "IU; bitmap = ". /back u ; / useVariable = "OU; tile = "OU;

This is the image we're about to make.

"rhe Main Menu Interface


Figure 13.3.

Graphic for main menu.

Make up a graphics file in your favorite editor that looks something like that in Figure 13.3. The image should be the same as our splash interface with a few differences. First, there is no label on this one. Second, it is a grayscale image. Third, I've made it a bit dark so it provides good contrast for our buttons (Figure 13.4). Copy your file (named "back.jpg") or the one from the GPGT Lesson Kit to the directory where your .gui file is. For this menu, we're going to have three buttons: Play, Credits, and Quit. For each of these buttons, let's make graphics. The graphics are going to be used by a bitmap button, so when we make them, we'll want to make four versions of each. The versions will be for the four states: normal, highlighted, depressed, and inactive. For example, our four Play button images can be seen in Figure 13.4.

Figure 13.4.

Four Play button images.

PLAY
play_n.png

) )

(
play-h.png

)
'lAY
play_i.png

(
play_d.png

546

The images are all 24-bit PNG files measuring 640 x 480. Notice that "playJpng" is the same as "play_n.png". We could in fact just create the normal, depressed, and highlighted versions of the button and not supply the inactive one since we don't need it. However, although the engine will automatically use the normal image for our missing inactive image if the need arises, it will print a warning message. I personally don't like warning messages, so I always supply a button for all four cases.We could just as easily use a 1 x 1 transparent PNG for the inactive button.

Game Interfaces

Chapter 13

Now, start the GPGT Lesson Kit. Using the GUI editor, open the main menu interface. At this point, your interface should look just like the first image we made ("back.jpg"). Add three GuiBitmapButtonCtri buttons to the GUI and arrange them so that the)i line up down the center. When you're done, the screen should look like Figure 13.5.

Figure 13.5.

Completed main menu.

The Main-Menu Interface Code


There is no separate code file for our main menu. All the code we need to write is embedded in the command field for each of the three buttons:
//Play Button => command = "quit() ;"; //Credits Button => command = "Canvas.setContent(ggsCreditsToon);"; //Quit Button => command = "quit();";

Currently, the Play button will quit (or in the case of the GPGT Lesson Kit version, it will go back to the "Game GUIs" menu). We'll change this to a play interface later when we finish our game. The Credits button will load our credits interface as the contents of the canvas, and the Quit button quits.

13.2.3 Credits (Toon)

The next interface we'll make is the credits interface. This interface is quite similar to our main menu. In fact, it will use the very same template (obviously, in a new directory and with a new name). We use the very same JPEG image as we used fo'r the main menu. The credits interface is going to load and display the contents of a text file in an attractive manner. We're using an external source for the text content, because this makes it easy to edit and correct mistakes.

The Credits Interface Copy your template and the graphics file to a new directory and be sure that "init.cs" is executing them.

547

I,

Part III

Game Elements

Start the GPGT Lesson Kit. Using the GUI editor, open the credits interface. It should look just like our main menu did when we started working on it (see Figure 13.3). We're going to use a GuiMLTextCtrl to display our credits. To do this, we'll need a GuiScrollCtrl as the parent. So, using the GUI editor, add a GuiScrollCtrl to our credits interface and then make a GuiMLTextCtrl the child. When you're done, you should have something like the following.
new GuiScrollCtrl () { profile "GuiScrollProfile"; horizSizing = "center"; vertSizing = "relative"; position = "150 50"; extent = "500 500"; minExtent = "8 2"; visible = "1"; willFirstRespond = "1"; hScrollBar "alwaysOff"; vScrollBar = "alwaysOff"; constantThumbHeight = "0"; childMargin = "4 4"; new GuiMLTextCtrl(ggsCreditsToonMLText) profile = "GuiDefaultProfile"; horizSizing = "width"; vertSizing "bottom"; position = "6 6"; extent = "500 500"; minExtent = "8 2"; visible = "1"; lineSpacing = "2"; allowColorChars = "0"; maxChars = "-1";

};
) ;

You'll want to use your own name for the GuiMLTextCtrl, but it does need a name because we're going to write some code for it. Note that the GuiScrollCtrl uses the following settings.
It uses the default profile. We'll want to change this soon.
horizSizing and vertSizing of "center" and "relative" respec-

tively. This means that the control will resize to take up all of the space of its parent from top to bottom and maintain an aspect ratio with its parent horizontally while staying centered.

548

The scroll bars are both turned off all the time.

Game Interfaces

Chapter 13

There is a small child margin. Note also that the GuiMLTextCtrl has the following settings.
It uses the default profile. We'll want to change this soon. It resizes to fit the width and height of its parent.

Before we move on to the credits GUI code, let's do one more thing. We want the user to be able to press ESC in, order to return to the main menu. The easiest way to do this is to create a button that uses the ESC key as an accelerator and then have it positioned off screen. This way, it won't render, but it will still respond to an ESC button press. The (abbreviated) code for this button would look like the following.
new GuiButtonCtrl() { / / ... position "-1 -1"; extent = "1 1"; conunand "Canvas.setContent(ggsMainMenuToon) ;"; accelerator = "escape";

};

Notice that the button is 1 x 1 pixel positioned at < -1, -1 > , thus putting it off screen. The command this button executes sets the main menu as the contents of the canvas. Simply make this button a child of the credits interface, and it will work.

The Credits Interface Code


OK, at this point we have the. GUI controls in place, but the GuiScrollCtrl and the GuiMLTextCtrl will be using default profiles. So, the current credits interface will look something like the image on the left in Figure 13.6, when we would rather it look more like the image on the right. To fix this problem, we'll need to write our own GuiControlProfile for the GuiScrollCtrl and for the GuiMLTextCtrl.
Figure 13.6.

Credits interface.

549

Parr III

Game Elements

Custom Profiles
By default, the scroll profile uses either a Windows or OSX theme (based on your platform). Both themes use a completely white and opaque background and have a white border. Also, they both use a graphic for the scroll bars, arrow buttons, and the scroll thumb. Our needs are a little different, though. First, we want our background to be transparent (our image is dark enough to act as a background to our ML text). Second, we don't want to display any graphics. We could make a completely new profile to meet our needs, but to make our lives easy, we'll make a profile that derives from the default GuiScrollProfile.
if(lisObject(gsToonCreditsScrollProfile)) new GuiControlProfile ( gsToonCreditsScrollProfile GuiScrollProfile) ( 0; border opaque false;
);

This profile disables the border and sets the control to translucent (not opaque). The hidden benefit of inheriting from GuiControlProfile is that we can use its graphics array (which is required by the control), but since we're not rendering it, we don't really care what it is. That is, we don't have to create and specify a new bitmap array. So, what about the GuiMLTextCtrl? We'll inherit again, but the only thing we need to change is the opacity of the control.
if(!isObject(gsToonCreditsMLTextProfile) ) new "GuiControlProfile (gsToonCreditsMLTextProfile) opaque = false;
};

Great! Now we have the two profiles, but we need to decide where to put them. We could put them at the top of the .gui file, but I prefer to put my profiles in a separate .cs file. Then, I load the .gui file from the .cs file. This way, I can have profiles and console methods in one place and have that be separate from my interface definition. Please be sure that you load the .gui file after the profile definitions, or else the control will fail to be defined (they have to be defined to be used).

Filling the GuiMLTextCtrl


At this point, the only thing we have left to do is write some code to put text in the GuiMLTextCtrl, and then to write that text. To fill the GuiMLTextCtrl, we'll
550

Game Interfaces

Chapter 13

need two pieces of code. First, we'll need some code to read a file and dump the contents to the control.
II 1. Clear all content.
II 2. Open the file gsMLTextContent.txt (abort if not found) II 3. Read the file and push the contents into this GuiMLTextCtrl
function gsToonCreditsMLTextProfile::reload( %this ) { %this.setValue("N); II Clear it %file = new FileObject(); %fileName = expandFileName( "./gsMLTextContent.txt N ); echo( "Attempt to open N , %fileName ); %fileIsOpen = %file.openForRead( %fileName ); echo( "Open for read N (%fileIsOpen? "succeeded N "failed N
i f ( %fileIsOpen )

{ while (! %file. isEOF ( ( %currentLine = %file.readLine(); echo(%currentLine); %this.addText( %currentLine, true);

%this.forceReflow(); %file. close () ; %file. delete () ;

This gsToonCreditsMLTextProfile: : reload () console method does the following.


rt is seoped to our (named) ML text GUI gsToonCreditsMLTextProfile.
It opens the file ", \gsMLTextContent.txt ". It reads the file line by line until the end of the file and then closes it. It dumps every read line to the GuiMLTextCtrl using the addText () con-

sole method. After reading the contents of the file, it forces a reflow on the GuiMLTextCtrl. Lastly, it closes the file and deletes the file object. In order to use our new file-reading code, we need to have the onWake ( ) method call it.
551

Part III

Game Elements

function gsToonCreditsMLTextProfile::onWake( %this ) %this.reload();

Finally, we make a new file named "'/gsMLTextContenUxt" and put the following text in it:
<just:center> <color:FFFFFF><spush><just:center> <br><br> <font:Comic Sans:40><color:bl002B><shadowcolor:00la69> <shadow:l:l>My Big Game<spop><br><br> <font:Palatino LinoType:36>Playground Productions <br><br> <tab: 300, 300> <just:left> <spush><font:Arial Bold:1B> Written by: Edward F. Maurina III <color:b09100><shadowcolor:dddddd><shadow:l:l> Hall Of Worlds, LLC<br><br> <spop> <just:left> <spush><font:Arial Bold:18> Brought to you by: GG Press (trn) <color:b09100><shadowcolor:cccccc><shadow:l:1> Garage Games<br><br> <spop> <br><br><font:Arial Bold:16> <spush> <lrnargin%:l>I just want to thank ... <br><br> <tab: 150, 300> My Wife Teresa ... <br><br> The Staff at Garage Games, and ... and ... sniff<br><br> You the custorner ... for making this possible ... :)<br><br> <br><br><br><br><br><br> <spush><just:center>(Escape for Main Menu) <spop> <spop>

552

We've gotten to the end of our Than sene!\. Now, you should do it all again, except use new graphics from the Tech series.

L---

-_.

__.- ..--- - - - -

Game Interfaces

Chapter 13

13.3 Tech-Themed Interfaces


If you're skipping ahead, please go back to the start of the Toon-themed series. If not, please take a look at the images in Figure 13.7. Now that you've seen

the differences, please follow the steps outlined for the Toon series, but use new art for a sort of Tech theme (or choose your own theme). Please note that the Tech-themed menu adds a new button. You may add this if you wish, or add only the original three, skipping the Options button.
FIgure 13.7. Tech-themed screens.

Splash SCreen

Main Menu

Credits SCreen

Please feel free to use the images provided with the GPGT Lesson Kit. If you're copying my art, I expect that the second time through should only take about an hour or less to achieve. If you're making your own art, then most of your time will likely be spent making the art. That is, once you've got a working set of interfaces, it is quite easy to re-theme them: add a few extra touches here and there, and then you're done! Now that you're feeling pretty good and you've become a bit of an expert at making GUIs, let's go make some HUDs.

13.4 Common HUDs


The last set of set of interfaces we will make in this book will be some common HUDs. The most common set of HUDs you'll find in video games are the following.

Counters. Almost all video games use some kind of numeric feedback to give score, ammo count, health status, etc (see Figure 13 .Sa). Thus, we'll take the time to make a simple set of customizable counters that can handle up to nine digits. Feedback bars. If a game doesn't use numeric feedback, it will almost surely use some kind of graph instead (see Figure 13.Sb). Often both are present. Thus, we'll make a vertical bar to supplement the horizontal bar that comes with TGE. Strip compass. OK, not all games have compasses, but I see requests for this kind of thing a lot, and the compasses that folks have submitted as resources are always popular. Problem is, all the compasses that folks have submitted are C++-based, and new users may not want to mess with the code. Thus, this strip compass is entirely TorqueScript based (see Figure B.Sc).

553

-----------___ _

__

__

Part '"

Game Elements

Figure 13.8.
Some common HUDS.

a. Counters.

b. Feedback bars.

c. Strip compass.

13.4.1 Counter HUDs


A counter HUD should be flexible enough that the digits can be placed in any position, and also allow different styles of digits. We'll make a simple set of counters that use bitmap images for the digits. Additionally, we'll write scripts that handle up to nine digits (or actually a max count). This can be expanded if you need, but nine is usually enough. These counters will come with and without frames.

Counter HUD Images


Before we start, we'll need to make some graphics files for our counters. You can make your own graphics, or use the ones from the GPGT Lesson Kit.
Digits. The GPGT Lesson Kit contains digital and comic-style digits in blue, green, and yellow. Additionally, gray digits are provided as templates so you can simply adjust the color and perhaps add other effects as you wish. All digits are 50 x 50 pixels.

..

Frames. Optionally, you can create frames for the digits. The GPGT Lesson Kit includes several frame variations (corroded (not shown) and noncorroded). .
554

Game Interfaces

Chapter 13

DIJ

Counter HUD GUI Controls


Depending on how you choose to implement your counter, you'll have either two or three sets of GUI controls involved. All counters will have a GuiControl as a container for the digits, an optional GuiBitmapCtrl for the frame, and up to nine GuiBitmapCtrl controls for the digits (I count this as one set). In order to build your HUD, follow these steps. 1. Add and position a GuiControl as a child of the interface that should contain this HUD. 2. Add the GuiBitmapCtrl controls that will be the digits as children of the GuiControl we just added. 3. Optionally, add the frame GuiBitmapControl as a child of the GuiControl. These HUD controls need to have certain names. 1. The GuiControl container can have any name, but it needs some name for scoping our console methods. 2. The GuiBitmapCtrl digits need unique names of the form aDigitO, aDigitl, etc. Notice that they all have the same prefix but different numeric values. 3. The (optional) GuiBitmapCtrl for the frame does not need to be named. Now that we've named the controls, we need to add some dynamic fields to the GuiControl container.
numDigi ts. This should be between 1 and 9 (or greater if you modify the

scripts to handle more digits). digitTileName. This should be the same as the prefix we used when naming the GuiBitmapCtrl controls used for the digits. In this example, the value would be "aDigit". digi tPa tho This field tells the scripts where to find the digit bitmap. This path can be relative or nonrelative and should be of the form ". \counters\ blueDigits\digi". Notice that there is no ending slash. OK, now we're ready to write the scripts!

555

Part /11

Game Elements

Count HUD Scripts


We need a minimum of three methods to use these counters: (1) a method to initialize the counter, (2) a getCounterValue () method, and (3) a setCounterValue () method.

initializeBitmaps()
The first method we need is the initializeBitmaps () method. This method is responsible for setting up the bitmaps. function GuiControl::initializeBitmaps( %this ) ( if( "" $= %this.digitPath ) return false; for ( %count = 0; %count < 10 ; %count++ ) ( %this.digitBitmap[%countl = expandFilename( %this.digitPath @ %count );

This method basically expands the digit Path we supplied into a list of ten images, one per possible digit (0 ..9).

setCounterValue()
The main method we need is the setCounterValue () method. This method is responsible for actually displaying a numeric value. function GuiControl::setCounterValue( %this , %newCount) { II Check to be sure that the required fields have been set:

II II numDigits - Number of digits in this counter II digitTileName - Prefix for tile names used in this counter (i.e. names of the controls) II digitPath - Path to tiles used in this counter II II
if ( "" $= %this.numDigits ) return false; if( $= %this.digitTileName ) return false; if( $= %this.digitPath ) return false;

II Store the currentCount %this.currentCount = %newCount; %newCountDigits = strlen( %newCount );


if ( %newCountDigits > %this.numDigits ) ( II Overflow fori %count = 0 ; %count < %this,numDigits ; %count++ ) %tmpDigit[%countj = 9;

556

Game Interfaces

Chapter 13

else ( II Pad with zeros so our 'newCount' string is exactly II %this.numDigits wide %tmpNewCount = ""; fort %count = %this.numDigits - %newCountDigits %count > 0 ; %count-- ) ( %tmpNewCount = %tmpNewCo~nt @ "0";

%tmpNewCount

%tmpNewCount @ %NewCount;

II Get digits in reverse order and store them fort %count = 0 ; %count < %this.numDigits ; %count++ ) (
%tmpDigit[%count]
=

getSubStr( %tmpNewCount , %this.numDigits - 1 - %count , 1 );

II Change the bitmaps for each digit in the display fort %count = 0 ; %count < %this.numDigits ; %count++ ) { (%this.digitTileName @ %count) . setBitmap (
%this.digitBitmap[%tmpDigit[%count]] return true; );

This method does the following. Checks that the required fields are present and ditches if they are not. Stores the new count value as the current count. Checks to see if the count that was passed is too large. If it is, all the digits are set to nine, and the counter ditches. This is an overflow case.
If the basic checks are passed, the routine iterates over each count value and extracts the digit. Then it uses the extracted digit to assign a bitmap digit to the correct tile. By default, unset tiles are set to O.

getCounterValue() Because it is good to create symmetric functionality for game objects, we'll create a getCounterVal ue () method, too.
function guiControl::getCounterVal'ue( %this ) return %this.currentCount;

557

Part III

Game Elements

This function simply returns the currentCount value we stored in


setCounterValue().

As was previously mentioned, these scripts can be expanded to handle as many digits as you like. Also, as an exercise, you might try adding nonnumeric handling code.

13.4.2 Vertical Feedback Bar HUDs


As mentioned above, an alternate to the digit counter is a feedback bar of some type. TGE comes with a horizontal bar, but I often wish to use a vertical (or other) style bar. TGE does come with a specialized vertical bar for the player's health/energy, but I prefer to use this scripted one. This HUD is used to represent a value between 0.0 and 1.0 in steps of 0.1. In this sample, the HUD is arranged as a vertical bar, but this code will handle having the indicator tiles in any configuration. However, helper code has been provided to make the design of a vertical bar easier. You'll have to write your own resize () method for other arrangements. Vertical Feedback Bar HUD Images The vertical feedback bar has one image: the frame for the HUD. Some sample frames are included in the GPGT Lesson Kit so you can use this right away. The templates for these frames are provided so you can modify them. The design of these vertical frames is important. If they are not designed properly, you will not be able to (successfully) use the provided resize () method. This method makes tile placement a cinch. So, let's discuss the design of this frame. The provided frames all have the same properties (see Figure 13.9). There are eleven cells, and the top cell is a graphic indicating the type of indicator (damage or energy). You can put any graphic you like here by editing the provided templates. The bottom cells are for separating our feedback indicators. The top indicator cell starts at < 0, 50 >, and each cell after that is set at a delta of < 0, 45 > pixels. The complete image is 50 pixels wide and 500 pixels tall. Overall, the frames included in the GPGT Lesson Kit have a kind of known symmetry and regularity; i.e., the cells that represent the indicators are not oddly shaped and do not exist at irregular spacings. We'll see why in a moment. Vertical Feedback Bar HUD GUI Controls As with the counters, the vertical feedback HUDs are composed of three sets of GUl controls. First, a GuiControl is used as the container for our HUD.

Figure 13.9.

Vertical feedback bar.

..

558

Game Interfaces

Chapter 13

Second, a GuiBitmapCtrl is used as the frame for the HUD. Third, a set of GuiControl controls are used as the indicators. To make this last set of controls act as controls, we make custom profiles for them. We'll address this in the scripts section below. For now, let's assemble our HUD. In order to build your HUD, follow these steps. 1. Add and position a GuiControl as a child of the interface that should contain this HUD. 2. Add the GuiControl controls that will be the indicators as children of the GuiControl we just added. Just make ten and place them at < 0, 0> for now. 3. Add the frame GuiBitmapControl as a child of the GuiControl container. 4. Resize the container and the frame to have the same dimensions and be sure that the frame is above (in front of) the GuiControl indicators. These HUD controls need to have certain names. 1. The GuiControl container can have any name, but it needs some name for scoping our console methods. 2. The GuiControl indicators need unique names of the form DamageBarOIndicatorO, DamageBarOlndicatorl, etc. Notice that they all have the same prefix but different numeric values. The "Indicator" portion of the name is required. 3. The GuiBitmapCtrl for the frame needs to have a name of the form DamageBarOIndicatorFrame. Notice that the prefix is the same as that for the indicators. Now that we've named the controls, we need to add a dynamic field to the GuiControl container: feedbackBarPrefix. This should be a string containing the prefix you used for the indicators and frame. In this example. this field would contain DamageBarO. OK, now we're ready to write the scripts!

Vertical Feedback Bar HUD Scripts


We have two kinds of scripting to do. First, we need to make some custom profiles for our GuiControl controls. Second, we have to make scripts update the bars. As an added bonus, we'll do an (optional) final script that will make positioning and creating these counters a cinch.
Custom Profiles

Our indicators are made from the GuiControl control. To make this work, we need a custom profile that will make these controls opaque with a predefined background color. Additionally, we may wish to make the container
559

_.'

- _.. _ - - -

Part 1/,

Game Elements

GuiControl opaque with a different background color. These profiles will look like the following.
II A container profile new GuiControlProfile (feedbackBarBackColorProfileO) opaque = true; fillColor = "20 20 20 255";
);

II An indicator profile new GuiControlProfile (feedbackBarIndicatorColorProfileO) opaque = true; fillColor = "255 255 255 255";
};

The first profile is a gray color and used for the container. The second profile is a fully white color and used for the indicators. Please understand that, if it pleases you to do so, each indicator tile can have its own profile, and each can be a different color. Once you've created custom profiles, be sure they get loaded before the .gui file and then use them as the profile for your controls.
Feedback Scripts

The guts of this HUD are the scripts that update the display. I'm providing two methods, and as an exercise, you should modify them a bit and add a third. The two provided methods are setFeedbackGUIValue (), which sets the indicators based on a passed value, and flashIndicatorBar (), which is used to cause the changed indicator cells to flash (optionally, of course).
function GuiControl::setFeedbackGUIValue( %this , %value II Check for the required indicator prefix field if("" $= %this.feedbackBarPrefix) return;

II Generate an 'index' from %value if ("" $= %value) %value = 0;


if(%value > 1.0) %value = 1; else if(%value < 0.0) %value 0; else %value %value; %this.curIndex
=

mFloor( 10 * %value );

560

Game Interfaces

Chapter J 3

for(%count = 1; %count <~ 10; %count ++) { %toggleCheck = (%this.feedbackBarPrefix @ "Indicator" @ %count) .isVisible(); (%this.feedbackBarPrefix @ "Indicator" @ %count) .setVisible( %this.curIndex >= %count ); if( %toggleCheck !~ (%this.feedbackBarPrefix @ "Indicator" @ %count) .isVisible() ) { if ( %this. flashTime > 0 ) { (%this.feedbackBarPrefix @ "Indicator" @ %count) .flashIndicatorBar( %this.flashTime );

%this.prevIndex ~ %this.curIndex; %this.currentValue = %value;

This method does the following. Checks that the required field is present, and ditches if it is not. Checks that the count is between 0.0 and 1.0 and, if not, adjusts it so that it is. Because this method uses values between 0 and 10, it multiplies and further massages the value so that it meets our requirements. The method loops over each tile and decides whether it should be visible or not visible. If a tile is changed from visible to not visible, or vice versa, the flashIndicatorBar () method is called. The method finishes by saving the current index and actual values. So, what about this flashing? It is a pretty common thing to have indicators flash when they change. So, as part of the fun, we're going to add this functionality.
function GuiControl: :flashIndicatorBar( %this I %flashTime ) ( %flashPeriod ~ %flashTime / 3: %isVisible = %this.isVisible(); %this.schedule( %flashPeriod * 1 , "setVisible", ! %isVisible ); %this. schedule ( %flashPeriod * 2 , "setVisible", %isVisible ) ;

This method does the following. Divides the flash time into three parts.
56!

Part III

Game Elements

Toggles the indicator visibility. Schedules the indicator visibility to toggle again in one flash period, and then once more in two flash periods, for a total of three toggles. Flashing is optional. I didn't mention it earlier, but if you want the indicator to flash when it changes, you'll need to add one more dynamic field to the frame: flashTime: This field should be the number of milliseconds you want the indicator to flash.
Design Helper Method

At this point, we've written all the required code. However, when designing this tutorial, I noticed that it was a real hassle to position the tiles. Thus, I've added another piece of code. This code is used to resize the frame and pre-position the indicator tiles. This version of the method only works for vertical bars.
function GuiControl::resizeVBAR( %this ) { II Check for the required indicator prefix field if("" $= %this.feedbackBarPrefix) return; if("" $= %this.originalframeDimensions) return; if("" $= %this.firstIndicatorY) return; if("" $= %this.IndicatorHeight) return;

II Resize and reposition the frame first %ContainerWidth = getWord( %this.getExtent() , 0 ); %ContainerHeight = getWord( %this.getExtent() , 1 ); (%this.feedbackBarPrefix @"Frame") .resize( o , 0 , %ContainerWidth , %ContainerHeight ); II Resize and reposition the indicators
%originalFrameWidth = getWord( %this.originalframeDimensions, 0 ); %originalFrameHeight = getWord( %this.originalframeDimensions , 1 ); %resizdFirstIndicatorY = ( %this.firstIndicatorY I %originalFrameHeight) * %ContainerHeight; %indicatorHeightDelta = ( %this.IndicatorHeight I %originalFrameHeight) * %ContainerHeight; %indicatorY = %resizdFirstIndicatorY; for(%count = 10; %count >= 1; %count --) (%this.feedbackBarPrefix @ "Indicator" @ %count). resize( 0 , %indicatorY , %ContainerWidth , %indicatorHeightDelt~ );

562

Game Interfaces

Chapter 13

%indicatorY

%indicatorY + %indicatorHeightDelta;

This method relies on the presence of some new dynamic fields:


originalFrameDimensions. This two-element integer vector contains

the width and height of the original -frame graphic. In the samples this is "50 500".
firstIndicatorY. This is an integer value that denotes top-to-bottom

y-offset of where the first indicator should be. In our sample, this is would be at pixel "50". IndicatorHeight. This is an integer value specifying the full-size height of each indicator cell. Knowing what these value are, the method does the following. Checks that the required fields are present, and ditches if they are not. Calculates the new size of the container. Grabs the original frame width and height. Calculates the new resized height for the first cell. Repositions and resizes the frame. Calculates the delta height for each indicator cell. Loops over the indicator cells and repositions and resizes each.

Why is this even necessary? Well, as I mentioned, when you move the frame around and resize the elements, it becomes a real hassle to keep everything properly aligned. This is compounded by the fact that, when we open and close the editor to edit and test the GUI, the GUI controls are often resized and repositioned.

13.4.3 Strip Compass HUD


Our last HUD is a compass displayed as a strip. I call this a strip compass, but you can call it whatever you like. Basically, the challenge here is tomake a 2D representation of what is fundamentally a 3D object, and to do so in script. This compass is designed to take a pointing vector and translate that to a compass direction. It is assumed that + y is north and + x is east (as is the case in the TGE world model).

Strip Compass HUD Images


The strip compass has two image files: (1) the frame and (2) the strip (Figure 13 .10). As you can see, the frame is fairly simple. It has an outer strip,
563

Part III

Game Elements

a center marker, and a screen. This screen is mostly translucent and the whole thing is designed to overlay the strip.
Figure 13.10.

A strip compass.

SE

NE

SE

sw

NW

NE

The strip may seem odd at first. It seems to be a lot larger than what it needs to be. In fact, because we want to make it possible to display any point on the compass by shifting our strip leftward, it needs to be wide enough and contain enough elements to show all compass points along its leftward travel. This means we need twelve points on the compass, versus the standard eight. Four are repeated. The provided strip is 1200 pixels wide and 50 pixels tall. The frame is 400 pixels wide and 50 pixels tall.

Strip Compass HUD GUI Controls The strip compass has three GUI controls that are used to represent it: (1) a GuiControl used as a container, (2) a GuiBitmapCtrl used as the strip, and (3) a GuiBitmapCtrl used as the frame. When we place our controls, they are placed as follows.
1. The container control is placed as a child of some GUI (i.e., whatever inter-

face contains the compass). The dimensions of this control are "400 50". This control should be named. 2. The strip is placed as a child of the container at "0 0". The dimensions of this control are "1200 50". This control must be named. 3. The frame is placed as a child of the container (covering the strip) at "0 0". The dimensions of this control are "400 50". For scripting purpose, the container control needs two dynamic fields.
\'\

stripName. This field contains the name of our strip control. stripWidth. This field contains the pixel width of the original graphics

file used for the strip. Please note that if you require the strip and frame to be of a different dimension, you may resize them prior to saving your work, but-and this is a big but-please be sure that you maintain the same ratio between the frame width and the strip width. In addition, be sure that the stripWidth dynamic field matches the width you've chosen for the control; i.e., if you choose to shrink the control by half, the dimensions and fields would be as follows.
564

Game Interfaces

Chapter 13

Container Strip
=

"20025'

"60025"

Frame = "200 25' container. StripWidth (dynamic field) = 600

Strip Compass HUD Scripts


If you don't have a good grasp of 3D graphics mathematics. you should probably stop at this point and go bone up by reading the appendix in AkenineMoller and Haines's Real-Time Rendering, Second Edition (A K Peters, Ltd., 2002) or whatever book(s) you use as reference. Once you are properly girded. carry on. lt is a well-known fact that we can convert a two-dimensional vector (Le., in a plane) into a 360-degree angular value theta. where theta is the rotation about a vector perpendicular to the plane and represents the rotation from some arbitrary location. In other words, we're going to use the dot-product and some things we know about the TGE world to calculate an angle.
function GuiControl::updateCompass( %this , %facingVector) { II Check for the required fields stripName and stripWidth. if ( n" $= %this.stripName ) return; if ( n" $= %this.stripWidth ) return; II Normalize the facing vector (just in case) %facingVector = vectorNormalize( %facingVector ); We can use the dot product and some tricks to figure out what part of how we should position our strip to properly indicate our facing direction. %leftFacing vectorDot( "1 0 0" , %facingVector ) < 0) ? true remember 0 1 0 is forward, and that we can get the angle between X and Y in radians using the DOT product: %forwardTheta = vectorDotC "0 1 0" , %facingVector ); Now, knowing our facing and theta, we can calculate our right-hand rotation about Z in degrees: if( %leftFacing ) ( %rotationDegrees = 360 mACos( %forwardTheta ) * 180.0 I 3.1415927);
}

II II II II II

false;

II II

else ( %rotationDegrees

mACos( %forwardTheta ) * 180.0 I 3.1415927;

565

--------~

Part III

Game Elements

If we calculate our rotation as a percentage, account the ratio 8/12, and scale based on our current extent vs. the p~e-scaled width of the image, we can calculate the exact position to place the strip at: %curPosY getWord( %this.stripName.getPosition() 1); %curExtX getWord( %this.stripName.getExtent() 0); %curExtY getWord( %this.stripName.getExtent() 1); %percentageRot = %rotationDegrees I 360.0; %extentRatio = %curExtX I %this.stripWidth;

II II II II II II II

We've created a strip that is three times as wide as the frame, giving it 12 compass points vs. the normal 8.

II recall this is a left-shift %newPosX = -1 * (8 I 12 * %percentageRot * %extentRatio * %this.stripWidth); %this.stripName.resize( %newPosX, %curPosY, %curExtX, %curExtY );
This method does the following. Checks that the required fields are present, and ditches if they are not. Normalizes the facing vector to make it nicer to work with. Checks to see if we are left- or right-facing. The dot-product only provides the angle between two vectors, up to 180 degrees. Thus, we need to determine which half of the circle this is to get the whole picture. Calculates the right-hand rotation about a vector "00 1", which is the TGE world up vector. Calculates the linear offset for the strip based on some known quantities, including the current angle of rotation, strip width, and the ratio of the normal points (8) to actual points (12) on the strip. Repositions and resizes the strip.

13.5 Summary
In this chapter, we examined several GUI controls working in tandem to produce new and useful results. We discussed the following standard interfaces.
Splash screens. This is a basic screen, of which most games have at least one and often several. They are used to display many different kinds of

566

Game Interfaces

Chapter 13

information, including the game title, company logos, and even interlevel art. Main menus. This interface really needs no introduction, but we did learn about how to implement it and then to hook other interfaces to it. Credits screens. This is another common interface, used to thank the folks who worked hard on your game, to provide additional information, etc. As part of the above effort, we produced two variations of each interface (Toon and Tech themes) for a grand total of six interfaces. Having completed the more serious discussion, we then launched into a discussion of HUDs and learned how to make the following three useful HUDs. Counters. The counter HUD is seen in almost every game in some form or another, making it worthwhile to explore one means of creating one. Vertical feedback bars. Another prevalent HUD is the feedback bar. Since Torque already supports a horizontal bar, we worked together to create a vertical version using only scripts and GUI controls. Strip compasses. Lastly, to show that C++ is nice but not necessary for creating HUDs with complicated behaviors, we made a strip compass. This HUD represents a 3D compass that rotates. We produced the same effect in 2D using a little bit of math knowledge and the powerful and useful TorqueScript language.

567

'--.

Mal<in the Game

Part IV

Chapter 14 Putting It All Together


14.1 Maze Runner: A Simple Single-Player Game
Maze Runner is a simple platform game brought into the 3D realm. It isn't based on a specific game, but it is inspired by games I have played. My purpose for this game was not to create a new blockbuster but rather to provide an easy-to-understand game idea upon which we could hang examples as we worked through the guide. A 60-second summary of this game would read something like the following.
In this game, you run around a maze and pick up coins. Your goal is to pick up all the coins while avoiding various obstacles. Mazes will vary in size and in scope. They may run along one level, or have multiple levels. Along the way, as you hunt for all of the coins, you will need to avoid disappearing bridges that may drop you to a lower level or into a fiery cauldron below. You will be blocked by fireballs and impassable chasms. To get around these obstacles, you will have to use your ingenuity and the occasional teleport station. Timing, awareness of your surroundings, agility, and a little luck are all required for winning. You will start with three lives and gain a new life for each level you complete. To continue the game, pick up all of the coins and move on to the next level. Get the highest score and win the admiration of your peers! Good luck.

14.2 Game Elements


Let's stop for a moment and define the term game element. This is a term that I am using to describe any and all of the pieces that are used to create a game. For example, all of the following listed items are game elements: The game view. This general term incorporates point of view, field of view, and other view-related concepts and describes the end view of our game. We discuss this in Chapter 7, "Cameplay Classes." Interfaces and HUDs. However much we might wish to ignore it, all games require some CUI work and will have a variety of interfaces (splash screens, main menus, play GUls, etc.) and some HUDs (counters, indicator bars, etc.). Players and opponents. Although we could certainly have a game with no directly identifiable players or opponents, 3D games generally do have at least one model representing the player and other models opposing this player in some fashion. Weapons. This seems pretty straightforward, but what I really mean here is weapons and weapon analogues. The analogue, in this case, is something that functions like a weapon but may rn5f necessarily do damage.

571

Part IV

Making the Game

The world. This is a rather large game element and is in fact composed of a multitude of subelements, including terrain. water, the sky, environmental objects (trees, rocks, grass, etc.), environmental effects (rain, wind, lightning, the sun(s) and planets, etc.), structures (buildings, fences, bridges, etc.), sounds, and so on. Power-ups and pickups. These are items that are often at the core of a game and are meant to be interacted with. Sample items in this category would be coins, gems, weapons, ammunition, health packs, etc. Special effects. Here we are talking about eye and ear candy. These do have a place in gameplay, but they are often not directly tied to interaction, which is where we should focus our attention first. Miscellaneous elements. This last category is a grab bag for elements that don't fit anywhere specifically. Some examples are inventory systems, collision detection and response, damage and energy, and general scripting tasks. Now, armed with an idea of what a game element is, let's list the game elements in our game.

14.2. 1 Maze Runner: Game Elements


The finished game has the following elements and attributes. Interfaces. Splash screen CUI, main menu CUl, credits CUI, and play CUI. Game view. The game can be played in 3rd POV only. Player. The initial player will be the Blue Cuy that comes with the FPS Starter Kit. We will later design our own player. This player will be an example of the simplest possible player that can be used in a game. Opponents. There are no opponents in this game, but some suggestions will be provided for adding them if you wish to expand on this game later. The world. The game world is a simple cauldron-shaped pit. This pit will contain a lake of lava. Our maze will consist of individual shapes that we place using scripts and level-definition files. We will place some environmental objects to spruce the place up. Additionally, there will be a sky box, celestial bodies, clouds, wind, rain, and even lightning. We're going all out on special effects to show how to use as many Torque features as is reasonable. Obstacles. There are two types of active obstacles and three static obstacles. The active obstacles include level blocks (individual and grouped) that fade, disappear, and reappear over time. There are also blocks that shoot fireballs in any of eight fixed compass directions (N, NE, E, SE, S, SW, W, NW), or down, or any of the prior directions, but randomly. The static obstacles are open horizontal spaces between blocks, vertical spaces between blocks, and blocks themselves.

572

'-----

- --- -

-------

-------

Putting It All Together

Chapter 14

Getting around. To get around the maze, the player will run and jump. Also, there can be up to three distinct teleport stations; that is, teleport stations can be grouped in sets, and there can be up to three distinct sets of teleport stations in a level. Additionally, if any set contains more than two stations, entering one station will randomly send the player to anyone of the other stations in the set. Pickups and power-ups. The only pickup in the game is the coin. Picking up all coins is the primary goal. A HUD will show the total coins picked up and the number of coins remaining for the level. Inventory system. We will use the "Simple Inventory" system that comes with this guide and is described in Chapter 7, "Gameplay Classes." It will provide all the mechanics necessary to pick up coins and remove them from the game world. Miscellaneous "glue" scripts. We will end up writing quite a few scripts to tie the game together, to track the score and our lives count, as well as to load the levels.

14.3 Game Goals, Rules, and Mechanics


Great! Now we know generally what the game is about and what elements it has. The last thing we need to do is describe how the individual game elements interact. The goal of this game is very simple: score as high as possible by finishing as many levels as possible before losing all of your lives. The rules and mechanics for this game are as follows. Pick up all the coins. Picking up all coins on a level ends the level and takes the player to the next level. Stay alive. Falling into the lava below or getting hit by a fireball kills the player. Gain lives. To gain more lives, simply complete a level. One new life is gained for each level completed. Teleporting. We can place up to three sets of teleport stations. Each set may have two or more stations. If there are only two stations in a set, the stations will teleport back and forth between each other. If a set has three or more stations, the spawn point will be randomly selected. Teleporting occurs by running over a station. The destination station will be temporarily disabled to avoid infinite teleport loops. It will not operate again until you walk off the station. Teleporting is not instantaneous, so be careful about fireballs that cross stations, as you are temporarily unable to move when teleporting. Respawning. When the player is killed, it will respawn in the location where it was first dropped into the game.

573

Part IV

Making the Game

Level loading. To make this game easily maintainable, tunable, and modifiable by players, all level loading is controlled by a text file (the level file). Players can add new levels and redefine levels to their hearts' content.

14.4 Setting Up Our Workspace


Before we can work on any lessons, we must first set up a work area. Everything that you need to do this is supplied on the CD that comes with this guide. If you examine the CD, you will find the following directories. "\Appendices". This directory contains the GPGT appendices. "\Base". This directory contains data and scripts that are used in the lessons and can also be used later to make new games. Please see the "Lesson Kit Assets" appendix for additional information about the contents of this directory. "\LessonKit". This directory contains the GPGT lesson kit. For more information about it, please read the "Lesson Kit User's Guide" appendix. "\MazeRunner". Excluding the data and scripts in "\Base" and some content we will copy from the TGE demo that you should install using one of the installers found in "\TorqueDemoInstallers", this directory contains all of the unique resources and scripts required to build the MazeRunner prototype. "\MazeRunnerAdvanced". This directory contains a completed version of MazeRunner with several additional features as suggested in Section 14.10, "Improving The Game". "\TorqueDemoInstallers". This directory contains installers for TGE. At this time, if you do not have the demo installed on your machine, please do so by running the appropriate installer (based on your computer and operating system type). Once you have finished, please continue reading.

If you are a Linux user, I must apologize. At the time this book went to print, version 1.4 ofTGE for Linux was still being worked on. Please check the GarageGames website to see if it is ready and, if so, download the demo kit. Otherwise, I suggest using one of the other versions of the engine in the interim.

14.4. 1 Starting from Torque Demo

First, be sure to install a copy of the TGE demo using one of the installers found in "\TorqueDemoInstallers". Feel free to install this anywhere you please. While writing our game, we will be copying files out of the installed demo to a working directory. Second, let's make a new (working) directory named "MazeRunner" and place it on a drive with at least 100 MB of free space. We'll want some elbow room while we work. Please note, while we are writing our game (reading the numbered lessons), this is the directory we will be working in. We will be copying materials from the CD to this directory and editing them in some places. Do not confuse this with the GPGT Lesson Kit which is also included on the CD. The GPGT Lesson Kit is a separate application containing several
574

Putting It All Together

Chapter 14

mini-tools and samplers. To learn more about this application you should read Appendix B, "GPGT Lesson Kit Docs." Third, now that we have a place to work, let's copy the entire contents of the TGE demo directory (from wherever we installed it in step one) into our new directory "MazeRunner".

14.4.2 Write Cleanup Scripts


It is a good idea to have the ability to remove temporary files from a working directory. If we remove all compiled scripts (DSOs) before rerunning the engine, we are insuring that only new script content will be used. Additionally, it is a good idea to occasionally remove terrain lighting files (ML). To accomplish these two tasks, we will write some scripts .. The first script (if you are running Windows) will be a batch file called "DELDSO.bat". It is used to delete all compiled script files (DSO cleaning) and contains the following simple line of script.
del IS

IF

*dso

In UNlXjLinuxjOSX, the file would be "deldso", and the content of the file is the following.
rm -rf *dso

The second file (if you are running Windows) will be called "DELML.bat". It is used to delete all terrain lighting files (ML cleaning) and contains the following simple line of script.
del

Is IF

*ml

In UNIXjLinuxjOSX, the file would be "delml", and the content of the file is the following.
rm -rf *ml

We'll run the DSO cleaner each time we modify our scripts, and occasionally we'll run the ML cleaner to get rid of stale lighting files.

14.4.3 Copy Mod Directory


Although it is possible to modify the demo to create MazeRunner, it will be far simpler to start with a blank slate instead. To that end, a bare-bones mod has been provided. To start with this mod, please copy "\MazeRunner\ A_SettingUp\prototype" from the accompanying disk into "\MazeRunner". 575

----~------------------------

Part IV

Making the Game

14.4.4 Modify "main.cs"


Next, edit "main.cs" and change line 6 from this:
$defaultGame
"demo";

to this:
$defaultGame
=

"prototype";

This will use our new "prototype" mod instead of the demo mod.

14.4.5 Add Systems Scripts


The accompanying disk comes with a number of scripts that are provided to simplify your game-writing endeavors. We discuss some of these scripts in the guide, and those we do not discuss are documented in the "Scripted Systems" appendix. From the accompanying disk, please copy the "\Base\Scripts\EGSystems" directory into "\MazeRunner\prototype". Then, edit the onStart () function in "\MazeRunner\prototype\main.cs" so it looks like the following (bold lines are new code).
function onStart () { // Maze Runner Changes Begin --> exec("./EGSystems/Simplelnventory/egs_Simplelnventory.cs") ; exec ("./EGSystems/SimpleTaskMgr/egs_SimpleTaskMgr.cs") ; exec ("./EGSystems/Utilities/egs_ArrayObject.cs") ; exec ("./EGSystems/Utilities/egs_Misc.cs") ; exec ("./EGSystems/Utilities/egs_Networking.cs") ; exec("./EGSystems/Utilities/egs_SimSet.cs"); exec ("./EGSystems/Utilities/egs_String.cs") ; // <-- Maze Runner Changes End II .. leave remaining code alone

14.4.6 Add Maze Runner Data


You are not expected to create your own content for this game. I have included all of the models, textures, and sounds you will need. From the accompanying disk, please copy the following directories.
1. "\Base\Data\GPGTBase" directory into U\MazeRunner\prototype\data",

576

and 2. "\MazeRunner\A_SettingUp\MazeRunner" directory into "\MazeRunner\ prototype\data".

Putting It All Together

Chapter 14

14.4.7 Create Maze Runner Scripts Directory


Although we will not be placing anything in it yet, in preparation for our lessons, let's create the directory "\MazeRunner\prototype\server\scripts\MazeRunner".

14.4.8 Test Run


After saving the modified "main.cs" and "prototype\main.cs", run the executable you placed in "MazeRunner", and the prototype should start up. If it does not, please retrace your steps and see if you missed something.

Windows Users
On Windows platforms, some users will get a warning about a missing or wrong sound setup. If, and only if, you get this message, copy the "\MazeRunner\A_SettingUp\OpenAL32.dll" file (found on the accompanying disk) into your "MazeRunner" directory and try again. If that does not work, read through the "Getting Help" section in Chapter 1 of this guide.

14.4.8 Ready To Start


OK, if you got the executable to run, you're ready to start.

14.5 90 Percent or 10 Percent?


If we ignore the iterative nature of game creation, we can roughly divide game development into two parts: the first 90 percent and the last 10 percent. I know, that probably sounds like a bunch of tripe, but bear with me for a moment. The first 90 percent should be all about planning and implementing. The last 10 percent should be about polishing. If you are doing the polishing first or spending too much time creating polished content, you are simply wasting your time. The above percentages do not have anything to do with the duration of tasks but rather with the amount of effort that you should put into these two parts when making your prototype. You may have the goal of making games for fun or making them for profit (hopefully for both). In the end, either goal will only be accomplished by focusing on getting your game from the idea state to a playable state as fast as you can. Without a doubt, nice art, clean interfaces, and special effects are very important to a game and to its ability to sell, but in order to have something to selL you must first have something to play. Some special effects and artistic

577

---.-----._-_.-------

Part IV

Making the Game

elements are critical to the playability of a game, but most are not (this does not negate their value in the final version of a game). To find out if a game is fun, you must be able to play it. Thus, the only goal you should have is to get the game you are working on to a playable stage. Often, when you play with your game prototype, you will find that an idea that seemed great doesn't really work or just isn't really fun. Just as often, you may be surprised to find that things you didn't plan on doing turned out to be really fun and/or cool. In either case, you'll never know until you play your game. In this guide, we do lessons that can be considered either part of the 90 percent or part of the 10 percent. To help you, those lessons that are related to game playability have been marked as "Maze Runner Lesson (90 Percent Step)," and those that are important to the look and feel of the game have been marked as .. Maze Runner Lesson (10 Percent Step)." You can safely skip the latter lessons and the game will still be playable. As a parting note, just remember this when you are tempted to work on 10 percent stuff first: While a 90 percent is probably a B, 10 percent is definitely an F.

14.6 Returning to Chapter 21


You may be reading this as a result of having been directed here from the end of Chapter 1. If so, you should now return to Chapter 2. "Torque from 10,000 Feet," and continue from there. Otherwise, feel free to continue here.

14.7 Finishing the Prototype


Thus far, you have probably been working your way through the guide, learning about various features of the Torque Game Engine. Along the way, we have stopped to do little lessons that created one or more game elements to be used in the game. At this point, we don't really have a playable game. We have just a short distance to go before our game reaches the playable prototype stage. To get our game ready for play testing we must do the following two things.

578

1. Finish gameplay code. At this point, we can start the Maze Runner mission and then manually load a level, but our player doesn't get moved to the right spot on the level, and there is pretty much no interaction. We need to change this. Specifically, we need to make the levels load automatically, have the player die when struck by a fireball or after falling into the lava, load the next level when all the. coins are collected, and award our player with a new life on a successful level completion.

Putting It All Together

Chapter 14

2. Improve feedback. With the final mechanics in place, we need to provide just a little more feedback to the player. Specifically, we need to update the play GUI to show how many lives we have, how many coins we've collected (score), and how many coins are left for a level. Also, while we are about this, we will add sounds for the fireball firing and explosions and then add some GUI sounds and music to make jf feel like a completed package.

14.8 Finish Gameplay Code


By this point, you should be feeling pretty comfortable with TorqueScript and with navigating the prototype directory structure. So, the kid gloves are coming off. In the next few pages, we will run through some terse discussions. We will examine newly added scripts and modifications to scripts we discussed in prior lessons.

14.8. 1 Copy Required Files


Before we continue, please do the following. 1. Copy "\MazeRunner\MazeRunner_PosCFinishin~the_Prototype\prototype2" into "\MazeRunner\". 2. Copy "\MazeRunner\MazeRunnecPoscFinishin~the_Prototype\main.cs" into "\MazeRunner\ ". The new 'main.cs" file points to the newly added "prototype2". mod directory. The directory "prototype2" contains all of the changes we are about to discuss and is ready to play, if you would like to try it before continuing.

14.8.2 Breaking the Law


The first thing we will do is break the law. OK, we're not breaking the law, but we are doing something that I warned you not to do earlier. Namely, we are going to make a global variable for tracking the lD of the player. Then, we are going to use it to implement gameplay scripts and later to keep our interfaces up to date. We are, in effect, ignoring the client-server divide. This is both good and bad. It is good because it makes writing the scripts for our single-player game simple. It is bad because it ties us to a single-player game only. If later we decide to make this game support multiple players, we will experience at least some pain modifying our scripts to handle this new mode. So, why are we doing this? Well, first, I know that in this book we will only ever play this game in single-player mode. Second, the game is simple enough that later, if you do convert this to multiplayer, the pain won't be too bad and it will serve as an excellent object lesson in making good decisions.

579

Parr IV

Making the Game

Excuses and reason aside, we must implement this change. To do so, [ have modified the method GameConnection: : create Player () in "game.cs" to look like the following (bold lines are new code):
function GameConnection: :createPlayer(%this, %spawnPoint) II Create the player object %player = new Player() ( dataBlock = MazeRunner; II Change this line client = %this;
) ;

MissionCleanup.add(%player)i $Game: : Player = %player; II MazeRunner

Now, whenever we want the player's ID, we can just reference the global
$Game: :Player.

14.8.3 Automatic Startup


To this point, we have been manually loading mISSIOns by typing buildLevel (0);. That is just fine for testing purposes. but we really need the game to load when the mission is loading.

Experiments in Loading
If we examine the "game.cs" file closely, we will see that it has a variety of functions and methods. Among these. are some promising-sounding places to put a script for automatically loading our first level.
onMissionLoaded () . Hmmm... this sounds good. The mission is loaded.

so we should be good to go. startGame (). This sounds good, too. I mean, we do want to start the game. right? GameConnection:: createPlayer () . OK, maybe you wouldn't think of this one. This is a hint, actually. Great, we have some possible places to do the level loading, but what are the steps we need to follow in order to load our level? Can we simply put a buildLevel () call in one of these? Why don't we try it? Add the following code to the end of onMissionLoaded () (bold lines are new code).
startGame(); buildLevel (0) ;

580

After restarting the game and reloading the mission, this may work, or it may work partially, or the game may hang. It depends.

Putting It All Together

Chapter i 4

At this point in the game startup process, there is some ambiguity in timing due to latencies that can vary from run to run. This means that any of the following actions can occur. 1. The game starts correctly, and the player is on the correct spawn point. This is what we want. Unfortunately, this doesn't always happen. 2. The level loads and the player gets dropped on the safe spawn point-end of story. Now we're stuck. 3. If timing conspires against you, all the resources that need to have been loaded won't be ready, and the loading code will just hang. This is the worst possibility. So, what is happening here? Well, the mission was loaded, but the player had not been created yet, so our scripts for moving the player can't work. They have no object to move. (If you're curious, you can see the player-moving script by looking at the playerDrop () function in "levelloader.cs".) Since putting bui IdLevel () after startGame () didn't work, that pretty much rules out our placing the function call in startGame (), too. What about GameConnection: : createPlayer (), then? Let's try that next.
%this.player = %player; %this.setControlObject(%player);

BuildLevel(O); //MazeRunner

Perfect! This is guaranteed to work properly every time. The level is always loaded after the player is created, so the scripts have valid object IDs to work with.

14.8.4 Dying
Another problem with our prior revision of this game was that we didn't get killed by the lava or fireballs. Let's remedy that now.

KiliZone
To be killed by the Java, we need some way to know we're in it. Now, we could make our water block into a lava block by changing the water type. However, as part of our game design, we chose to make the player invincible, so this won't really help. I mean, we could in theory make our player have a very low damage level, make it damageable, and then maybe, just maybe, falling in the lava would kill him. The thing is, we don't really want the player object to be destroyed. We just want to decrement a life and move to the spawn point. When a player

581

Part IV

Making the Game

object is in the destroyed/dead state (getState () returns "dead"), the player will no longer move or take move commands until it is replaced with a new instance. This is by design and is not what we want in this instance. So, long story short, we get creative. Let's create a really big trigger (named KillZone) and place it in the lava. Then, we can just write an onEnterTrigger,O callback that will take away a life and move us to the spawn point. Perfect!
datablock TriggerData(KillZoneTrigger) tickPeriodMS = 100;
};

function KillZoneTrigger:,:onEnterTrigger(%DB , %Trigger , %Obj) %Obj.1oseALife();

The above code defines the datablock for this trigger, and the callback calls the method loseALife () (described below) on the object entering the trigger. But what about placement? The following code will do the placement.
function buildKillZone () { new Trigger{KillZone) { position= "-256 256 40";, rotation = "1 0 a 0"; scale = "512 512 25"; dataBlock = "KillZoneTrigger"; polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000";
};

MissionGroup.add( Ki1lZone );

"
Then we can add a call to this code in onMissionLoaded () to do the creation (bold lines are new code):
function onMissionLoaded() { buildKillZone(); II MazeRunner startGame();

So, what about that loseALife () thing?,


582

Putting It All Together

Chapter 14

Player: :loseALife()
The easiest way to handle removing lives is to make a method scoped to the Player class (so it can be called on the Player object) that handles all of the bookkeeping. This simplifies things greatly. Yes, right now only two things can kill the player, but later you might add more, and having killing code all over the place would be very bad. Here is the code (located in "mazerunnerplayer.cs"). function Player: :loseALife( %player ) {

II

%player.lives--;

II 2
if ( %player.lives <= 0 ) ( schedule ( 0 , 0 , endGame ); return;

II 3
%player.setVelocity("O 0 0"); %player.setTransform(%player.spawnPointTransform);

This code does the following.


1. It decrements the player's life counter. (Yes, we haven't talked about this yet. It's coming up soon.)

2. It checks to see if all of our lives are gone and then schedules a call to endGame () (in "game.cs") to unload the mission, destroy the player, disconnect the client from the server, and get us back into the main menu.
Why not call endGame () directly?

You may wonder why we schedule a call to endGame () instead of calling it directly. The reason we do this is that, when we call endGame ( ), we indirectly cause the player to be deleted. However, the player is the object that the los eAL ife () method was called on, so when the engine tries to return from the call to endGame ( ), it will not have anywhere to return to. This will crash the engine. The lesson here is to never delete the current Object in a method that is called on that object. Always defer that deletion by using a call to schedule () . Calling schedule () with a time of 0 milliseconds tells the engine to run the function as soon as possible after returning from all nested function calls. In practice, this will always be on the next processing cycle or later.

583

'--

------

Part IV

Making the Game

3. If the game is not over, the player is moved back to its last spawn point. This information is stored in the player by playerDrop () in the file "levelloader.cs n:
$Game: :Player.spawnPointTransform (%actX 8PC %acty 8PC $CurrentElevatian) ;

Initial Lives

In order to take away lives, we must have lives to take. The best place to add initial lives to the player is either in its onAdd () method or at the location where we create it. I chose the onAdd () method (in "mazerunnerplayer.cs"; bold lines are new code):
function MazeRunner:: onAdd ( %DB , %Obj ) Parent: :onAdd( %DB I %Obj ); %Obj.lives 3; (

Fireballs

OK, we got a little off topic there, but we're back now. The next question is: how do fireballs kill? The projectile object has an oneall is ion () callback that is called for collisions with any world object. So, if. we write a: version of this callback in the namespace of our projectile, we can have that callback check to see if the player was hit and call loseALi fe ( ) .
function FireBallProjectile: :onCollision( %projectileDB , %projectileObj %collidedObj , %fade , %vec , %speed) ( if (%collidedObj.getClassName() $= "Player H ) ( %collidedObj.loseALife();

In the above callback (located in "fireballs.cs"), the engine is asked to get the class name for the collided-with object. It then compares this to "Player If the comparison returns true, loseALife () is called on the collided-with object.
H

584

.... .

.. ..

. ..

,-.

_ ,_.----..

Putting It All Together

Chapter

14

Alternate Solution # 1

There is an alternate way to write this code that would actually work in more cases (i.e., for Player and aiPlayer).
II Alternate implementation function FireBallProjectile: :onCollision( %projectileDB , %projectileObj , %collidedObj , %fade , %vec , %speed )
if (%collidedObj.getType() $= $TypeMasks: :PlayerObjectType ) (

%collidedObj.loseALife();

This alternate implementation uses the getType () method to get a bitmask for the collided-with object. The bitmask contains bit settings for all classes from which the object is derived as well as for the class itself. So, as I alluded to, if the collision occurred against an aiPlayer (which is derived from Player), this comparison would still work, whereas the prior code would not. In this game, we don't have that worry, so let's leave it as is.
Alternate Solution #2

Originally, as I wrote this code for the book, I was using a bleeding-edge version of the engine (version 1.4 before release), and I ran into a bug (that has since been fixed) where %collidedObj was always getting "I". For a moment, I thought I was stuck. Then, it occurred to me that there are other ways to solve the identification problem, and I wrote the following code. %Offset = vectorSub( %vec , $Game::Player.getWorldBoxCenter() ); %Len = vectorLen( %offset ); if ( % len < 1. 7 ) ( $Game: :Player.loseALife();

This code uses the position of the projectile's collision and then compares it to the position of the player's centroid. If the distance between them is small (1.7 world units or less), in all likelihood the object that was hit is the player, and I call1oseALife () . This solved my temporary problem, and in the occasional instance when the player wasn't hit but was just close to the collision point, the difference was not noticeable. The lesson here is that TGE is very flexible, and you can often solve the same problem in many ways. So, don't let one problem stop you.
585

Part IV

Making the Game

Out of Lives

At some time, after all this losing of lives, the player will be out of lives. According to our initial rules list, this means the game is up, time to go home. As we have already seen (above) the loseALife () method handles this case and ends the game for us.

14.8.5 Moving On
The last things we need to fix with regard to gameplay are moving on to the next level and getting our extra life.

Last Coin
Our design rules stated that, when the last coin is picked up, the current level should be unloaded and the next level should be loaded. So, how do we do this? If you recall, the inventory system has a callback called onPickup () . When we discussed this callback, I said that you might want to override it to implement special behaviors. This is one of those times. If you will look in "coins.cs", you will find the following implementation of onPickup () .
function Coin: :onPickup( %pickupDB , %pickupObj , { %ownerObj

II

%status

Parent: :onPickup( %pickupDB , %pickupObj %ownerObj );

II 2
if (CoinsGroup. getCount () == 0 ) { buildLevel($Game: :NextLevelMap); $Game::Player.lives++;

II 3
return %status;

This callback does the following.


1. It takes advantage of the prewritten pickup code by calling the Parent::

version. 2. It then checks to see if the 5imGroup CoinsGroup is empty. In the case that it is empty, buildLevel () is called with the stored numeric ID of the next level, and a new life is added to our player.

Putting It All Together

Chapter 14

3. Last, but not least, it returns the return status from the Parent call. This is important because the method/callback that called onPickup () in the first place might care if the pickup was successful or not.

14.8.6 Gameplay Scripting Completed


We are officially done with the gameplay scripting now. The game is now in a playable state, and we could define some levels and ship it off to our testers at this point. If this were a business venture, that would be the plan, but since we're learning about Torque and not running a gaming business, let's continue.

14.9 Improve Feedback


To make the game easier to play, we should provide some information to the player about how many lives are remaining, what the score is, and how many coins are left on a level. Also, adding sounds to the fireballs will make them a little easier to detect. Lastly, if we add some sounds and music, we will have a nicely rounded prototype.

14.9.1 Copy Required Files


Before we continue, please do the following. 1. Copy "\MazeRunner\MazeRunner]osUmprove]eedback\prototype3" into "\MazeRunner\". 2. Copy "\MazeRunner\MazeRunnecPosCImprove_Feedback\main.cs" into "\MazeRunner\". The new "main.cs" file points to the newly added "prototype3" mod directory. The directory "prototype3" contains all of the changes we are about to discuss and is ready to play, if you would like to try it before continuing.

14.9.2 New piayGUI HUDs


If you start the game and run the "Maze Runner" mission, you will see that the new and improved playGUI has three HUDS at the top of the screen (Figure 14.1.) The three HUDs are the following.

Lives counter (upper-left). Shows number of lives the player has left. Score (upper-middle). Shows number of coins thus far recovered. Remaining coins for level (upper-right). Shows coins left till end of level. These HUDS should look quite familiar. They are the same counters we discussed in Chapter 13, "Game Interfaces," being put to good use in our prototype game.
587

Part IV

Making the Game

Figure 14.1

New HUDs.

To make your life easier, I have created a completely new playGUI containing these HUDS and placed it and pll the scripts and content associated with it in " \client\ui\PlayGUIs\". To get this new playGUI interface loaded instead of the old one, I changed the ini tClient () function in " \client\ init.cs" as follows.
N

function initClient()

//

...

I I Prior to Maze Runner exec("./ui/PlayGUIs/PlayGui.cs"); II MazeRunner (Load MY GUI) / / ... Ilexec("./scripts/playGui.cs"); II Prior to Maze Runner / / ...

Ilexec (". lui/PlayGui. gui") ;

This change simply tells the function NOT to load the old "PlayGUI.gui" and "PlayGUI.cs" and to load my "PlayGUIs/PlayGuLcs" intstead. This new script will automatically load the remainder of the scripts required to build the new playGUI. Now, let's talk about how these HUDs are hooked up.
588

Putting It All Together

Chapter 14

Hooking up the Lives HUD The lives counter is initialized in the MazeRunner: : onAdd () callback, from the file "mazerunnerplayer.cs" (bold lines are new code):
function MazeRunner: : onAdd ( %DB , %Obj ) { Parent: :onAdd( %DB , %Obj );. %Obj.lives = 3; livescounter.setCounterValue(%Obj.lives) ;

It is decremented in Player: : loseALife (), from "mazerunnerplayer.cs"

(bold lines are new code).


function Player:: loseALife ( %player ) {

II 1
%player.lives--;

livescounter. setCounterValue (%player. lives) ;


1/ ...

It is incremented in Coin: : onPickup () , from "coins.cs" (bold lines are new

code).
function Coin: :onPickup( %pickupDB , %pickupObj , %ownerObj ) {

1/ ...
if (CoinsGroup.getCount() == 0 ) { 1/ ... livescounter.setCounterValue($Game::Player.lives);

//

...

Hooking up the Score HUD


The score counter is initialized in GameConnection: : createPlayer (), from" \server\scripts\game.cs" (bold lines are new code).
N

function GameConnection: :createPlayer(%this, %spawnPoint)

I I ...
BuildLevel(O); scorecounter.setCounterValue(O) ;

589

Part IV

Making the Game

It is incremented in Coin: : onPickup (), from "coins.cs" (bold lines are new code).
function Coin::onPickup( %pickupDB , %pickupObj , %ownerObj ) ( / / ... scorecounter.setCounterValue( scorecounter.getCounterValue() + 1 );

//

...

Hooking up the Remaining Coins HUD


The coins counter is initialized at the very end of BuildLevel (), from "levelloader.cs" (bold lines are new code).
function BuildLevel( %levelNum / / ... coincounter.setCounterValue( CoinsGroup.getCount()

);

It is decremented in Coin: : onPickup (), from "coins.cs" (bold lines are new code):
function Coin::onPickup( %pickupDB , %pickupObj , %ownerObj ) (

/ / ... coincounter.setCounterValue( CoinsGroup.getCount() ); / / ...

14.9.3 Adding Sounds


To give the game a little more pizzazz and to make it feel more finished, we need to add a few sounds. As you will recall, in Chapter 11, "Special Effects," we made several audio descriptions and audio profiles. I have included all of these and a few others in two separate places. The 2D sound descriptions and profiles have been added to a new file named " '" \client\scripts\MazeRunnerGUISounds.cs". This includes the following.

MazeRunnerNonLooping2DADObj. A non-looping 2D AudioDescription object for use with AudioProfile objects. MazeRunnerLooping2DADObj. A looping 2D AudioDescription object for use with AudioProfile objects. MazeRunnerGGSplashScreen. An AudioProfile object to play music when the GarageGames splash screen is displayed. 590

------_.

Putting It All Together

Chapter 14

MazeRunnerButtonOver and MazeRunnerButtonPress. lWo AudioProfile objects used to play button over and press sounds. MazeRunnerLevelLoop. An AudioProfile object used to play an ambient loop during game play. This file is loaded by " \client\init.cs" using the following code.
N

/// Load client-side Audio Profiles/Descriptions exec("./scripts/audioProfiles.cs"); exec("./scripts/MazeRunnerGUISounds.cs"); II Maze Runner

The 3D sound descriptions and profiles have been added to the existing "firebaIls.cs" file at the top and include the following. MazeRunnerNonLooping3DADDB. A nonlooping 3D AudioDescription datablock for use with AudioProfile datablocks. MazeRunnerFireballExplosionSound. An AudioProfile datablock that is played for each fireball when it is shot. MazeRunnerFireballExplosionSound. An AudioProfile datablock that is used by the FireBallExplosion datablock to play an explosion sound. These sounds will now be loaded when "fireballs.cs" is executed. Now, let's briefly discuss how each of our new sounds is used.

Adding Sound To Splash Screen The simplest way to add a sound to the GarageGames splash screen is to play the sound when the splash screen is displayed. If we look in the file \client\ui\StartupGuLgui", we will find a method named loadStartup (). This method is used to display the splash screen. To have the game playa sound when the splash screen is displayed, I made these changes.
"N

alxPlay () function loadStartup() (

1/ ...
IlalxPlay(AudioStartup); 1/ Before Maze Runner alxPlay(MazeRunnerGGSplashScreen); II Maze Runner

And Other Sound Functions


We did not explicitly discuss the alx* () functions in the guide, but they are all documented in the accompanying "Console Functions Quick Reference" that is part of Appendix A on the accompanying disk.

Adding Sound to Buttons To have the menu buttons playa sound when the mouse passes over a button and when a button is clicked, I needed to define a new GuiControlProfile object and fill in the proper fields.
if(lisObject(MainMenuButtonProfile)) new GuiControlProfile (MainMenuButtonProfile)"(

591

Part IV

Making the Game

I I ...
soundButtonOver soundButtonDown
};

= =

"MazeRunnerButtonOver"; "MazeRunnerButtonPress";

I then made sure that each button in the main menu (" mainMenuGuLgui")' used this new profile.
I I ...
new GuiButtonCtrl() profile = "MainMenuButtonProfile";

\client\ui\

I I ...

Adding Ambient Loop to Game

To add the ambient loop to our game, I simply added an alxPlay () statement to the onWake () callback and a reciprocal alxStop () statement to the onSleep () callback for the new piayGUI. Both of these callbacks are located in " \client\ui\playGUIs\playGUI.cs" and now look like this.
N

function PlayGui:: onWake ( %this ) ( $enableDirectlnput = "1"; activateDirectlnput(); II Activate the game's actioD map moveMap.push(); II Maze Runner %this.levelLoop = alxPlay(MazeRunnerLevelLoop);

function PlayGui:: onSleep ( %this ) { II Pop the keymap moveMap.pop();

if(isObject ( %this.levelLoop ) ) alxStop(%this.levelLoop); II Maze Runner

Notice that I simply store the handle returned from alxPlay () into an aptly named dynamic field levelLoop created on the fly in the piayGUI control object. Later, I check to see if the handle represents a valid handle and stop playing the sound associated with it using 'alxStop () .
592

l-,

Putting It All Together

Chapter 14

Playing Sounds When Fireballs Are Fired


To play the firing sound, we will again use the playAudio () ShapeBase method. Although we don't care in this single-player game, by doing this, we insure that every client will hear the sound with no extra effort on our part. To do this, I modified the StaticShape: : shoot FireBall () console method to include the following code.
function StaticShape::shootFireBall( %marker, %projectile , %pointingvector %veloci ty) ( I I ... %marker.playAudio( 0 MazeRunnerFireballFiringSound );
I I

If you recall, all fireballs are fired from the center position of a fireball block's world box. Thus, we can approximate the correct location for the firing sound by simply playing the firing sound using the block that marks the origin of the shot itself. In this case I merely called playAudio () and played the MazeRunnerFireball Fir ingSound AudioProfile datablock in sound slot O.

Adding Explosion Sounds to An Explosion Datablock


The last sound that was added is the explosion sound. This was accomplished by assigning the new MazeRunnerFireballExplosionS9und AudioProfile datablock to the existing Fire8allExplosion datablock's soundProfile field.
datablock ExplosionData(FireBallExplosion) {
II ...
=

soundProfile II
};

"MazeRunnerFireballExplosionSound";

That's it. We now have a working prototype that we can distribute for testing. What's next?

14.10 Improving the Game


At this point, the game is working and completely playable. However, it is a long way from being a completed, or perhaps even fun, product. This short section is about getting the game from sort-ot-boring prototype to fun-to-play finished product.
593

._------._-.-

Part IV

Making the Game

Also, to show some of the things that can be done to improve this game, an improved version of the game has been supplied on the accompanying disk titled "MazeRunnerAdvanced".

14. 1O. 1 Add More Features


Before you jump into adding new features, I suggest that you play with your final prototype and study the scripts that make it run. Make some sample levels and play them. Then, once you feel confident enough, write down a list of new features and start adding them. To help your muse, here is a short list of suggested features. Rewrite the level loader. Get rid of the manual level-editing process and add a visual editor. Write a new level loader to load the files generated by the level editor. Add new gameplay elements. Gravity chutes. Flaming pipes. Falling blocks. Blocks that disappear (permanently) on contact. Opponents that block the path and kill the player on contact.

14. 10.2 Use Missions Instead


As an exercise, consider changing the scripts to dynamically create a mission file then load the mission file instead of generating the level on the fly.

14. 10.3 Fix Safe Block


Currently, the player is sent to a "safe" block during level tear-down and build-up. This is kind of weird and not all that pleasant to look at. Come up with a better idea, like the following. Overlay the screen with a "loading" GUI while building. Fade the screen to black while building.

14. 10.4 Cleanup


There are a tremendous number of scripts and assets going unused in the game. Get rid of these to give the game a smaller disk footprint.
594

L- -

Putting It All Together

Chapter

14

14.10.5 Maximize Networking Performance


As a single-player game, you might not think networking code would matter much. but it still does. By default, the networking settings are a bit low. Because our connection is local, we can maximize these settings. This will help decrease the time it takes to build our levels (since all dynamically generated objects are being ghosted on the fly from the local server to the local client). So put the following settings in "game.cs" at the top.
$pref::Net::PacketRateToServer $pref::Net::PacketSize
=

32:
=

450; 32:

$pref::Net::PacketRateToClient

14.10.6 Experiment with Art and Special Effects


Improve the artwork, add more special effects, and tune the ones that are there. Try using blocks that do not self-illuminate.

14.10.7 Features Added To Maze Runner Advanced


Several new features were added and many old features were changed in Maze Runner Advanced.
New Art

The first thing that was changed in Maze Runner Advanced was the art. I had a professional artist replace my ugly programmer art with something that had a lot more style (Figure 14.2).
Figure 14.2

New art.

595

Part IV

Making the Game

Added More Splash Screens


Although this is technically new art, too, I want to point out that I needed to allow this product to properly represent all the parties involved, so I added a splash screen for Hall Of Worlds, LLC (my company), and a title screen for the game (Figure 14.3).
Figure 14.3
New splash screens.

Visual Level Editor


Because I realized early on that the method for adding levels was difficult at best and heinously frustrating at worst, I added a visual editor. This editor uses modifications to the old programmer art and some tricky use of datablocks to supply a greatly simplified level editor (Figure 14.4).
Figure 14.4
Visual editor and resulting

level.

Credits and Help Dialog


I added a credits page and a help dialog containing instructions on using the game, editor instructions, a description of the game, etc. (Figure 14.5.)

596

Putting It All Together

Chapter 14

Figure 14.5

Credits and help.

14.11 Summary
In this chapter, we quickly tied up the loose ends for our gameplay scripts by enabling auto-loading of the mission, scripts to kill the player, more scripts to reward the player with extra lives, and scripts moving us on to the next level or ending the game based on coin and life counts, respectively. We learned that there are multiple solutions for each problem we face, and we examined a concrete example of a case where a bug (originally) prevented me from writing the game the way I wanted to. Lastly, we discussed the fact that this game is far from done, and then we brainstormed some ideas for improving it and looked at what some of those improvements entailed. At this point, you should feel fairly confident that you can in fact make a game, and that the Torque Game Engine will have the power and the features to make that game a reality. With that said, I wish you good luck and happy Torqueing!

597

Index
A ActionMaps 33, 356 actions 359 defining 357 devices 359 moveMap 222 unbinding 361 vehicle ActionMaps 235 add parent 89 alarmMode 197 animation 169 blended 20 cyclic 169 direction 170 non-blended 20 pausing 170 playing 169 animation sequences activateBack 230, 231 activateBot 230 back 225 brakelight 230 Damage Animations 171 fall 225 jump 225 land 225 maintainBack 230, 231 maintainBot 230, 231 root 225 . run 225 side 225 springO " spring7 230 standjump 225 steering 230, 231 Vehicle 228 Atlas 268 Audio Emitters 296 B Blue Guy 16, 17,223 brushes brush hardness 61, 62 brush mode 59, 60 editing actions 60 selection and < Radius> 62 selection mode 59,60,62 bump mapping 265

C callbacks 21,355,383 Canvas 456 classes animating 329 animations 196 as control object 208 AudioDescription 448 AudioEnvironment 448 AudioProfile 448 AudioSampleEnvironment 448 bouncy 178 Camera 169,201 CameraData 201 collisions 196 controlling 221 Debris 419 DebrisData 419 DecalData 426 ExplosionData 427 field of view (FOV) 205 FileObject 369 friction 179 GameBase 31,143,155 GameBaseData 143, 155 gravity 179 GuiControl 470 HoverVehicie 240 HoverVehicieData 240 Interim'Instance 17, 31, 197 Item 157,175 ItemData 175 movement 217 namespaces 353 networking 356 pitch 208
599

------

Index

Player 213 PlayerData 213 POV Cookbook 210 Projectile 437 ProjectileData 438 restricting POV 208 rotating 177 SceneObject 31,143,151 ScriptGroup 31, 352 ScriptObject 31, 352 Selecting Node 208 ShapeBase 31, 158 ShapeBaseData 31, 158 ShapeBaselmageData 157, 189 SimDataBlock 143, 148 SimGroup 31,350 SimObject 31, 143 SimSet 31, 347 static 177 StaticShape 157, 183 StaticShapeData 183 sticky 178 TSStatic 31,187 Vehicle 231 VehicleData 231 WheeledVehicle 236 WheeledVehicleData 236 WheeledVehicleSpring 238 WheeledVehicleTire 237 yaw 209 client-server architecture. See networking, client-server cloaking 160 clouds 281 storm 284 collision detection (COLDET) 17, 19, 153,220 collision meshes 18 collision timeout 180 onCollision() 21,234,249,385 ShapeBaselmageData 196 TSStatic 187 concave 18 console callbacks applyDamage() 163 click() 494,508,544,545 doDismount () 235 eval () 413,414

600

133,181,185,223,226,256, 257, 315, 364, 372, 380, 576 onAction () 518 onClearSelected() 486 onCollision () 21,135,233,234, 248, 249, 252, 385, 584, 585 onEnterTrigger() 339,342,386, 582 onlnputEvent() 525 onlnspect() 535 onLeaveTrigger() 339,342,343,386 onMount () 234 onPickup () 21 onRightMouseDown() 535 onSelectPath() 524 onSleep() 385,459,544 onTabComplete() 503 onTabSelected() 486 onTickTrigger() 339,340 onTrigger () 340 onTriggerTick() 340 onURL () 497 onWake() 385,459,491,544,551,552 console functions 114 acti vatePackage () 123, 124, 125 addMaterialMapping() 217 calcExplosionCoverage() 434 call () 414 cancel() 390,518 commandToClient() 416,417 commandToServer() 250,251,364, 415,416,418 compile() 379,380 containerRayCast() 401 detag () 107 echo () 100, 144, 145, 146, 154, ISS, 156,168,177,179,184,341,348, 349, 350, 351, 353, 354, 355, 358, 365,366,369,384,388,390,393, 394,395,396,397,398,399,400, 404,405,406,411,412,413,414, 434,473,474,475,494,495,505, 506, 507, 520, 527, 532, 551 error () 405 eval () 413,414 exec () 133,181,185,223,226,256, 257, 315, 364, 372, 380, 576 expandFilename() 367,369,490, 492, 551, 556
exec ()

_ _ _ _ _ _

._

-0

Index

console functions (continued)


fileBase () 368 fileExt () 368 fileName() 367,368,369,490,492, 551, 556 filePath () 367 findFirstFile() 364,365,366 findNextFile() 364,365,366 firstWord() 392,393 getBoxCenter() 404 getEventTimeLeft() 389 getFieldCount() 395 getFields () 135, 395 getFileCount() 366 getFileCRC() 366 getRandom() 405,409 getRandomSeed() 405 getRealTime() 390,391 getRecord() 394,395 getRecordCount() 394,395 getRecords() 394 getScheduleDuration() 390 getSubStr() 396,397,557 getTimeSinceStart() 389 getWord() 153,392,472,562,566 getWordCount() 392,393 getWords() 153,243,343,392 isEventPending() 389 isFile() 368 isObject() 258,344,375,409,412, 413, 417, 528, 550 ltrim() 399 mAbs () 400,402 mAcos () 402, 565 mAsin () 402 mAtan () 402 ~atrixCreate() 403 MatrixMulPoint() 400,401,403 MatrixMultiply() 403 mCeil () 400, 402 mCos () 402 mDegToRad () 402 mFloatLength() 405,406 mFloor() 400,402.560 mLog () 402 mPow () 400. 402 mRadToDeg () 402 mSin() 402 mSolveCubic() 403.404

mSolveQuadratic() 403,404 mSqrt () 400,402 mTan () 402 NextToken() 393,394 quit () 90, 547 removeField() 395 removeRecord() 394,395 . removeWord() 392,393 restWords() 392,393 rtrim () 399 schedule() 148,161.171, 377, 387, 388,389,390,391.392.406,407,

409,491,561
setDefaultFov() 203,205,206 setField() 395 setFov() 203,205,206 setRandomSeed() 405 setRecord() 394,395 setWord() 392,393 setZoomSpeed() 203,206 strchr() 396,398 strcmp() 398 stricmp () 398 stripChars () 399 StripMLControlChars() 399 stripTrailingSpaces() 399 strlen() 396,397,556 strlwr() 396 strpos() 397 strreplace() 397,398 strstr() 397 strupr () 396 trim() 399 VectorCross() 402 VectorDist() 402 VectorDot() 402,565 VectorLen () 179, 243, 402, 585 VectorNormalize() 402,447.565 VectorOrthoBasis() 402 VectorScale () 168,402.444,447 VectorSub() 243,341,402,528, 585 console methods 118, 120 activateLight () 197 add () 110,135,153,163.167,177, 181, 182,257,258,259,334,342, 347,348,350.351,355,375,383,

384,402,444,447.458,494,517.
543, 580, 582, 584, 589

601

--- - - - - -

Index

console methods (continued) addColumn () 480 addMenu () 513 addPage () 486 addRow () 480. 504 addScheme () 516 addSelection() 533,534.535 addText () 498, 551 applyDamage() 163 applylmpulse() 168,256 applyRepair() 163 attach () 496 bind() 357.360.361.364,415.418 bindCmd () 250, 251. 358, 360, 361 bringToFront() 349 buildlconTable() 530 clear() 350,505,518,532 clearMenultems() 513 clearMenus() 513 clearSelection() 533 close() 369.370,371,551 delete () 146, 147, 148,258, 344, 348,351.369,370,371,375,384, 388. 389. 543, 551 deleteLine() 496 deleteSelection() 534.535 detach() 496,497 dump () 139, 147, 148,247,496 echoTriggerableLights() 197 findltemByName() 532 findText () 517 findTextlndex() 506 forceOnAction() 518 forceRefiow() 498,551 get() 543 getChild () 535 getClassName() 145,148,149,584 getColumnCount() 480 getColumnOffset() 480 getControlObject() 205 getCount() 348,349,350,351,377, 406,409,417.586,589,590 getCursorPos() 503
getDamageLevel() 164 getDataBlock() 145, ISS, 163,

getEyeVector() 168 getForwardVector() 154 getGroup() 148,343,528 getId () 119,144.145,148,250,

251,348,349,350,413
getltemText() 532 getltemValue() 532 getLineText() 495 getMountNodeObject() 243 getMuzzlePoint() 437,444 getMuzzleVector() 444 getName () 145.148,255,355,384,

388
getNextSibling() 535 getNumDetailLevels() 198 getObject() 348.349,417 getObjectBox() 154,401 getParent () 535 getPathid () 336 getPosition() 334,342,434.472,

566
getPoweredState() 184 getPrevSibling() 535 getRowCount() 480 getRowld () 505 getRowNumByld() 505 getRowOffset() 480 getRowText () 50S getRowTextByld() 50S getScale() 152,314 getSelected() 517 getSelectedFile() 524 getSelectedld() 505,506 getSelectedPath() 524 getSlotTransform() 243 getState() 417,582 getText() 508,517 getTextByld() 517 getTransform() 153,207,401 getType () 146. 148. 180,585 getValue() 519,520 getVelocity() 167,444 getWorldBox() 154 getWorldBoxCenter () 154,168,

243,341,447,585
identity() 519
init ()

164, 168
getExtent() 562,566 getEyePoint() 168 getEyeTransform() 168

425

602

insertLine () 495 isActive() 474

Index

console methods (continued)


isAwake () 474 isEOF() 369,376,551 isRotating () 177 isRowActive() 506 isStatic () 177 isVisible() 474,561 listObjects() 350 makeFirstResponder() 461,462, 473,482 mountImage () 174, 175 mountObject() 173,174 moveSelection() 533,535 open () 531 openForAppend() 371 openForRead() 369,551 openForWrite() 370 pauseThread() 170 performClick() 508 PhysicalZone() 128,129,334,342 playAudio () 172 playThread () 169, 170, 171 pop () 250, 362 popBackLine() 496 popDialog() 457 popFrontLine() 496 push() 362 pushBackLine() 495 pushDialog() 457 pushFrontLine() 495 pushToBack() 349 readLine () 369, 551 reload () 551, 552 remove() 258,344,349,355,376, 383,384,459,543 removeColumn() 480 removeMenu() 513 removeRow() 480,504 removeRowByld() 505 replaceText() 517 resize() 472,497,558,562,566 rowCount() 480,505 save () 148, 361 scrollToBottom() 482 scrollToTag() 498 scrollToTop() 482,498 scrollVisible() 506 select () 516, 518, 535 setActionThread() 415,417

setActive () 474 setAlarmMode () 197 setBitmap() 490,491,509,557 setCloaked () 160 setCollapsed() 485 setCollisionTimeout() 180 setColumnOffset() 480 . setContent() 93,456,545,547, 549 setControlObject() 205,207,581 setCursor () 521 setCursorPos() 503 setDamageFlash() 165 setDamageState () 164, 166 setDataBlock () 155, 156 set Detail Level () 198 setEnergyLevel() 167 setFlyMode() 203,207 setHidden () 407 setlnvincibleMode() 164 setMenultemBitmap() 514 setMenultemChecked() 515 setMenultemEnable() 515 setMenultemText() 515 setMenultemVisible() 515 setMenuText() 515 setMenuVisible() 515 setName () 148 setOrbitMode() 203,207 setpath() 524 setPoweredState () 184 set Profile () 471 setRechargeRate() 167 setRepairRate() 163 setRowActive() 506 setRowByld () 504 setRowOffset() 480 setScale () 152, 188, 314 setSelectedByld() 506 setSelectedPath() 524 setSelectedRow() 506 setSkinName () 161,185 setText() 497,498,501,508,517 setThreadDir() 170 setTransform() 153,188,343,583 setValue() 490,491,519,551 setVelocity() 167,583 setVisible() 474,561 setWhiteOut() 165

603

Index

console methods (continued] size() 472,497,558,562,566 sort () 507,518 startFade() 161,407


stopAudio() 172 s topThread () 170, 171 stormClouds() 284 stormFog() 283 stormFogShow() 283 toggle () 80 writeLine() 370,371

console objects lI5, 133 console methods 118 dynamic fields 119 fields 118 handles 118 names 118 control statements 112 branching 112 for 113
if-then-else 112 switch 112 switch$ 113 while 113

strings 106 string operators 107 vectors 110 debugging dump() 139,147 tree () 139 DecalManager 426 Decals 425 destroying 162 dialogs popping 457 pushing 457 D1F. See Interiors disabling 162 DML 279,280,281,284,285 DTS. See shapes Dynamix 3, 16, 18

E
Earthsiege 3

emitters
backwardJetEmitter 229 damageEmitter 230 damageEmitterOffset 230 dustEmitter 230 dustTrailEmitter 230 footPuffEmitter 215 forwardJetEmitter 229 numOmgEmitterAreas 230 particleEmitter 304,305,315,

conversion 396 convex 17,18,19,158 CRC 175,366

o
damage flashes 165 damaging 162, 163 Damage States 163 Invincibility 164 Visual Feedback 165 datablocks 29, 127, 133, 149 accessing fields 132 creating objects with 129 declaring 130 data types 106 arrays 109 Booleans 108 cleaning 399 comparisons 398 escape sequences 107 manipulating 392 metrics 396 numbers 106 searching and replacing 396

316,317,341,344,430,436,440, 445,446 splashEmitter 216


stateEmitter 192 stateEmitterNode 192 stateEmitterTime 192 tireEmitter 230 trailEmitter 229,230 useEmitterColors 306

energy 166 environmental mapping 160 events 386 accuracy 390 cancelling 390 checking for 389 repeating 391 scheduling 387,388 times 389

604

1':--

- - - _ .. -

__

.....

Index

explosions 162, 166,427


eyeOffset 190 eyeRota tion 190

THbes 1 & 2 3,64,99,197,268,273,

401
getType()

F
fields 395 field of view (FOV) 205 files appending to 371 calculating CRC 366 counting 366 Dot (.) versus Slash (/) versus Tilde ("') 367 expanding names 367 extracting name 367 extracting path 367 extracting prefix 368 extracting suffix 368 filename wildcards 366 locating 364 overwriting 370 reading 368, 369 writing 368, 370 file I/O 364 first Person 190 fog 282 general 282 layers 282 forces and factors 217 forward vector 154 fxFoliageReplicator 318 fxLight 335 fxShapeReplicator 318 fxSunLight 326

146 Type Masks 145 globals


$Camera: :movementSpeed 203,207 $cameraFov 203,204,208 '$movementSpeed 203,207,221 $mvBackwardAction 221 $mvDownAction 221 $mvForwardAction 221 $mvFreeLook 209 $mvLeftAction 221 $mvPitch 222 $mvPitchDownSpeed 222 $mvPitchOpSpeed 222 $mvRightAction 221 $mvTriggerCountO-$mvTriggerCountS 232,236,241 $mvOpAction 221 $mvYaw 222 $mvYawLeftSpeed 222 $mvYawRightSpeed 222 $pref::Decal: :decalTimeout

426
$pref::Decal: :maxNumDecals

426
$pref: :decalsOn 426 $pref::lnput::KeyboardTurnSpeed 222 $pref::lnterior::detailAdjust

198
$pref: :Net::PacketRateToClient 595 $pref: :Net::PacketRateToServer 595 $pref::Net: :PacketSize 595 $pref: :Terrain: :enableEmbossBumps 266 $thisControl 472

G
games
3-D Language Spain 5 dRacer 5 Earthsiege 3 Colden Fai11lJay 5 Lore 4 Marble Blast COW 4 Minions Of Mirth 6 Orbz 4 RocketBowl Plus 5 Starsiege 3 Think Tanks 4

gravity 179,180,182,183,241,304, 305,316,333,422,439,445 GUI accelerators 472 active 474 autosizing 469 awake 474 background color 465 bitmap arrays 463

605

Index

GUI (continued) borders 464 commands 472 cursors 464 extent 471 first responder 473 fonts 465 key and mouse attributes 469 margins 481 nodifiers 527 nouse events 525 position 471 profiles 470 scrollbars 481 size 471 skinning 476,482,484,487,492, 509, 510, 512, 523 text formatting 468 Torque Markup Language (TorqueML) 499 variables 473 visibility 472, 474

#13-Celestial Bodies 332 #14-Teleport Stopper 334 #15-Teleport 1tiggers 340 #16-MoveMap 363 #17-Level Loader 371 #18-Game Events 406 #19-FireBall Explosion 434 #20-The FireBall 444 #21-Game Sounds 450 About 11 level of detail (LOD) 19, 198 lightning 288 lights and lighting 191, 285 constantLight 176, 191 Interiors 197 lightColor 176, 191, 440 lightRadius 176,191,335,440 lightTime 191 lightType 176, 182, 191 noLight 176, 182, 191 pulsingLight 176,191
M

1/0
file 364 images 189 image file lists (lFLs) 21 impulses 167 interiors 17. See also Classes: InteriorInstance level of detail (LOD) 198 inventories 243

L
Lessons #1 -Terrain for Our Game 72 #2-Loading Datablocks 132 #3-Game Coins 181 #4-Fade and Fireball Blocks 184 #5-Maze Blocks 188 #6-Simplest Player 223 #7-Preparing Our Game Inventory 256 #8-Lava in the Cauldron 278 #9-Starry Night 284 #lO-Low Lighting 288 #l1-Stormy Weather 294 #12-Teleport Station Effect 315

l-

math 400 absolute value 402 addition 402 ceiling 402 centroids 404 conversion degrees to radians 402 radians to degrees 402 cosine 402 creation 403 creation (from Euler angles) 403 cross Product 402 cubics 403 distance (between) 402 dot product 402 floor 402 inverse cosine 402 inverse tangent 402 length 402 logarithm 402 modifying mantissas 405 multiplication 403 normalization 402 orthographic basis 402 point multiplication 403 power 402

606

Index

math (continued) quadratics 403 random numbers 404 scaling 402 sine 402 square root 402 subtraction 402 tangent 402 meshes collision-0-collision-8 185, 228 LOSCol-9-LOSCol-16 228 Mesh Nodes cam 165,204,208,210,223,224,229 chassis 228, 236, 260 contrailO-contrail3 229, 230 eye 204,208,210,223,224,229 hubO-hub7 229 JetNozzleO-JetNozzleX 229,230 mountO-mount31 172, 173, 174, 229, 242 Tire 228,237,238,239,240,260,261 mirrors 198,199,214 missions 22 mounting 172, 191 alternate positions (vehicles) 242 image-to-shape 174 mountPoint 191 nodes 172 offset 191 rotation 191 shape-to-shape 173 slots 172 vehicle 233 movement 217, 221
N

o
objects 28 objects (console) 115 operators string comparisons 111 p, packages 122 particles 302 paths 336 performance culling replicators 323 physical zones 333 portals 20 position 152 POV cookbook 210 precipitation 288

R
random numbers 404 records 394 render bans 280 repairing 162, 163 replicators 318 rotation 152

S
scale 152 scales over vertex brush scale 58 selected brush scale 58 ShapeBaselmageOata animations 195, 196 running scripts 195 shapes 16, 157 skinning 487 skins (shape) 161 multi-skinning naming convention 161 sky visibility 282 sky box 279,280 sound 172 20 22,297 3D 22,299 AudioOescription 448 AudioProfile 448 Audio Emitters 296 special effects 31

namespaces 126, 133 building 149 chaining 149 inheritance 150 rules 149 scope 151 networking client-server 24 communications 27 control object 28 division of labor 27 ghosts 28 scope 28

607

Index

squareSize 267,268,270,271 Starsiege 3 state machines 192 defining 193 doing work 194 running animations 195 running scripts .195 transitioning 193 strings cleaning 399 comparisons 398 manipulating 392 metrics 396 searching and replacing 396 Sun 285

setScale () 152, 188, 314 setTransform () 153, 188,343,583 world boxes 154 Thbes 1 & 2 3,99,197,268,273,401 triggers 338 group 340 type masks 145

U Unicode 467, 468 V

vehicles 227 animations 228 velocity 167, 178


getVelocity() 167,444 maxVelocity() 178,179,221,290 setVeloci ty () 167, 583 visibility 282

T
Terrain 263 ticks 24 tokens 393 TorqueScript built-in functions 103 transforms 153, 168
getEyeTransform() 168 getEyeVector() 168 getForwardVector() 154 getPosition() 334,342,434,472" 566 getScale () 152, 314 getTransform() 153,207,401 object boxes 151, 154,401

water 269 flowing 274 reflections 276 shoreline 275 types 273 waves 272 words 392 Z zooming 205

608

--------

-/lng"ail,..- pcIdOlo .wI....


-PhD Carlisle. Course !reader & Senior leoturer, University of
,N *for

e Animal Games

-1

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Appendix A. Quick References

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Table of Contents
A.1 TorqueScript Quick Reference................................................................................................. 5 A.1.1. Conventions............................................................................................................................ 5 A.1.2. Syntax/Rules........................................................................................................................... 5 A.1.3. Literals.................................................................................................................................... 5 A.1.4. Operators in TGE.................................................................................................................... 6 A.1.5. Keywords................................................................................................................................ 8 A.1.5. Engine Interfacing.................................................................................................................. 23 A.2 Console Objects Fields and Methods Quick Reference........................................................... 30 A.2.1. ActionMap............................................................................................................................. 30 A.2.2. AIConnection......................................................................................................................... 35 A.2.3. AIPlayer................................................................................................................................ 37 A.2.4. AIWheeledVehicle.................................................................................................................. 41 A.2.5. AudioDescription.................................................................................................................... 43 A.2.6. AudioEmitter.......................................................................................................................... 44 A.2.6. AudioProfile (AP).................................................................................................................... 45 A.2.8. Camera................................................................................................................................. 45 A.2.9. CameraData.......................................................................................................................... 47 A.2.10. ConsoleLogger..................................................................................................................... 47 A.2.11. Debris................................................................................................................................. 47 A.2.12. DebrisData.......................................................................................................................... 48 A.2.13. DecalData............................................................................................................................ 49 A.2.14. DecalManager...................................................................................................................... 49 A.2.15. EditManager........................................................................................................................ 50 A.2.16. Explosion............................................................................................................................. 51 A.2.17. ExplosionData...................................................................................................................... 51 A.2.18. FileObject............................................................................................................................ 52 A.2.19. FlyingVehicle........................................................................................................................ 55 A.2.20. FlyingVehicleData................................................................................................................. 55 A.2.21. fxFoliageReplicator............................................................................................................... 56 A.2.22. fxLight................................................................................................................................. 59 A.2.23. fxLightData.......................................................................................................................... 60 A.2.24. fxShapeReplicator................................................................................................................ 62 A.2.25. fxSunLight........................................................................................................................... 64 A.2.26. GameBase........................................................................................................................... 66 A.2.27. GameBaseData.................................................................................................................... 67 A.2.28. GameConnection.................................................................................................................. 67 A.2.29. HoverVehicle........................................................................................................................ 76 A.2.30. HoverVehicleData................................................................................................................. 77 A.2.31. InteriorInstance................................................................................................................... 78 A.2.32. Item.................................................................................................................................... 81 A.2.33. ItemData............................................................................................................................. 84 A.2.34. Lightning............................................................................................................................. 85 A.2.35. LightningData...................................................................................................................... 86 A.2.36. Marker................................................................................................................................ 86

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.37. A.2.38. A.2.39. A.2.40. A.2.41. A.2.42. A.2.43. A.2.44. A.2.45. A.2.46. A.2.47. A.2.48. A.2.49. A.2.50. A.2.51. A.2.53. A.2.53. A.2.54. A.2.55. A.2.56. A.2.57. A.2.59. A.2.60. A.2.61. A.2.62. A.2.63. A.2.64. A.2.65. A.2.66. A.2.67. A.2.68. A.2.69. A.2.70. A.2.71. A.2.72. A.2.73. A.2.74. A.2.75. A.2.76. A.2.77. A.2.78. A.2.79. A.2.80. A.2.81. A.2.82. A.2.83. A.2.84.

MissionArea......................................................................................................................... 87 MissionMarker...................................................................................................................... 87 NetConnection..................................................................................................................... 87 NetObject............................................................................................................................ 91 ParticleData (PD) ................................................................................................................ 92 ParticleEmitterData .............................................................................................................. 93 ParticleEmitterNode.............................................................................................................. 95 ParticleEmitterNodeData....................................................................................................... 95 Path.................................................................................................................................... 95 PathCamera......................................................................................................................... 96 PhysicalZone........................................................................................................................ 98 Player................................................................................................................................ 100 PlayerData......................................................................................................................... 103 Precipitation....................................................................................................................... 106 PrecipitationData................................................................................................................ 107 Projectile........................................................................................................................... 108 ProjectileData..................................................................................................................... 108 SceneObject....................................................................................................................... 109 ScriptGroup........................................................................................................................ 112 ScriptObject....................................................................................................................... 113 ShapeBase......................................................................................................................... 113 ShapeBaseData.................................................................................................................. 144 ShapeBaseImageData......................................................................................................... 147 SimDataBlock..................................................................................................................... 149 SimGroup........................................................................................................................... 149 SimObject.......................................................................................................................... 149 SimSet............................................................................................................................... 153 Sky.................................................................................................................................... 157 SpawnSphere..................................................................................................................... 160 Splash............................................................................................................................... 160 SplashData ...................................................................................................................... 160 StaticShape........................................................................................................................ 162 StaticShapeData................................................................................................................. 163 Sun................................................................................................................................... 163 TCPObject.......................................................................................................................... 163 TerrainBlock....................................................................................................................... 165 Trigger.............................................................................................................................. 166 TriggerData....................................................................................................................... 167 TSShapeConstructor........................................................................................................... 167 TSStatic............................................................................................................................. 167 Vehicle.............................................................................................................................. 167 VehicleData........................................................................................................................ 168 WaterBlock........................................................................................................................ 170 WheeledVehicle.................................................................................................................. 172 WheeledVehicleData........................................................................................................... 175 WheeledVehicleSpring......................................................................................................... 175 WheeledVehicleTire............................................................................................................ 175

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3 Console Functions Quick Reference..................................................................................... 178 A.3.1. OpenAL............................................................................................................................... 178 A.3.2. Debugging........................................................................................................................... 184 A.3.3. String Manipulation............................................................................................................... 197 A.3.4. NETWORKING...................................................................................................................... 217 A.3.5. CONSOLE............................................................................................................................ 227 A.3.6. DEVICE IO........................................................................................................................... 232 A.3.7. FILE I/O.............................................................................................................................. 236 A.3.8. PACKAGES........................................................................................................................... 242 A.3.9. OBJECTS............................................................................................................................. 243 A.3.10. EVENT SCHEDULING.......................................................................................................... 244 A.3.11. DATABLOCKS..................................................................................................................... 247 A.3.12. VIDEO / TEXTURING.......................................................................................................... 247 A.3.13. SPECIAL............................................................................................................................ 260 A.3.14. RESOURCE MANAGEMENT.................................................................................................. 264 A.3.15. SCENE............................................................................................................................... 265 A.3.16. CONTAINERS and RAYCASTS.............................................................................................. 265 A.3.17. EDITORS........................................................................................................................... 269 A.3.18. BUILD............................................................................................................................... 269 A.3.19. TIME................................................................................................................................. 271 A.3.20. GUIS................................................................................................................................. 271 A.3.21. MATH ............................................................................................................................... 272 A.4 GUI Controls Quick Reference............................................................................................. 286 A.4.1. Purpose............................................................................................................................... 286 A.4.2. GuiControlProfile Fields......................................................................................................... 286 A.4.3. Standard GUI Controls (Alphabetical Listing)........................................................................... 288 A.5 Callbacks Quick Reference................................................................................................... 355 A.5.1. Game Callbacks.................................................................................................................... 355 A.5.2. GUI Callbacks...................................................................................................................... 364 A.5.3. Other Callbacks.................................................................................................................... 365 A.6 Scripted Systems Quick Reference...................................................................................... 366 A.6.1. Simple Task Management System (SimpleTaskMgr)................................................................ 367 A.6.2. EGTGE Tasks Management Reference.................................................................................... 375 A.6.3. Simple Inventory System (SimpleInventory)........................................................................... 377 A.6.4. SimpleInventory:: Structure.................................................................................................. 378 A.6.5. Simple Inventory Console Functions....................................................................................... 378 A.6.6. SimpleInventory:: Console Methods....................................................................................... 379 A.6.7. ShapeBaseData:: Inventory Methods..................................................................................... 381 A.6.8. ItemData:: Inventory Methods.............................................................................................. 382 A.6.9. Item:: Inventory Globals....................................................................................................... 384 A.6.10. Item:: Inventory Helper Methods......................................................................................... 384 A.6.11. GPGT Utilities .................................................................................................................... 384

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.1 TorqueScript Quick Reference


A.1.1. Conventions
Throughout this document, for succinctness, I will refer to the Torque Game Engine Scripting language simply as Torque Script.

A.1.2. Syntax/Rules
Torque Script is a typeless script that is very similar in syntax to C/C++. You will find that most C/C++ operators function as expected in Torque Script. In addition to providing the strengths of C/C++, Torque Script provides:

Auto creation and destruction of local/global variables and their storage. String catenation, comparison, and auto-string-constant creation (see Literals below). Function packaging.

A.1.3. Literals
Numbers 123 1.23 1.00E-003 0xabc Strings "abcd" 'abcd' String Operators @ TAB SPC NL Escape Sequences \n \r \t \c0 .. \c9 \cr \cp \co \xhh \\ booleans TRUE FALSE integer floating-point scientific notation hexadecimal string tagged string catenation tab catenation space catenation newline catenation newline carriage return tab colorize subsequent console output reset to default color push color from color stack pop color from color stack two-digit hex value ASCII code backslash 1 0

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

arrays

$MyArray[n] $MyMultiArray[n,m] $MyMultiArrayn_m

single-dimension multi-dimension multi-dimension 4-element vector

vectors

"1.0 2.0 1.0 2.0"

A.1.4. Operators in TGE


Operator $ % * / % + ++ -Name global local multiplication division modulo addition subtraction auto-increment (post-fix only) auto-decrement (post-fix only) Less than More than Less than or Equal to More than or Equal to Equal to Not equal to Logical NOT Logical AND Logical OR Example Variable Operators $a %b Arithmetic Operators $a * $b $a / $b $a % $b $a + $b $a - $b $a++ $b-Relations and Logical Operators < > <= >= == != ! && || $a < $b $a > $b $a <= $b $a >= $b $a == $b $a != $b !$a $a && $b $a || $b Bitwise Operators ~ Bitwise complement ~$a flip bits 1 to 0 and 0 to 1. (i.e. ~10b == 01b) 1 if $a is less than % b 0 otherwise. 1 if $a is greater than % b 0 otherwise. 1 if $a is less than or equal to % b 0 otherwise. 1 if $a is greater than or equal to % b 0 otherwise. 1 if $a is equal to % b 0 otherwise. 1 if $a is not equal to % b 0 otherwise. 1 if $a is 0 0 otherwise. 1 if $a and $b are both non-zero 0 otherwise. 1 if either $a or $b is non-zero 0 otherwise. Multiply $a and $b. Divide $a by $b. Remainder of $a divided by $b. Add $a and $b. Subtract $b from $a. Increment $a after use. Note: ++$a is illegal. Decrement $b after use. Note: --$b is illegal. $a is a global variable. %b is a local variable. Explanation

&

Bitwise AND

$a & $b

composite of elements where bits in same position are 1. (i.e. 1b & 1b == 1b)

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Operator

Name

Example

Explanation composite of elements where bits 1 in either of the two elements. (i.e. 100b & 001b == 101b) composite of elements where bits in same position are opposite. (i.e. 100b & 101b == 001b) element shifted left by 3 and padded with zeros. (i.e. 11b << 3d == 11000b) element shifted right by 3 and padded with zeros. (i.e. 11010b >> 3d == 00011b)

Bitwise OR

$a | $b

Bitwise XOR

$a ^ $b

<<

Left Shift

$a << 3

>>

Right Shift

$a >> 3

Operator =

Name Assignment Compound Assignment

Example Assignment Operators $a = $b; $a op= $b; String Operators / Constants

Explanation Assign value of $b to $a. Equivalent to $a = $a op $b. op can be any of: * / % + - & | ^ << >> Concatenates strings $c and $d into a single string. Numeric literals/variables convert to strings. Same as catenation example with new-line between $c and $d. Same as catenation example with tab between $c and $d. Same as catenation example with space between $c and $d. 1 if $c equal to $d . 1 if $c not equal to $d. Substitute y if x equal to 1, else substitute z. Sixth element or array $a Select a console method or field ---This definition of the onCollision() function is in the Item namespace. This is a normal string. This is a tagged string. The value of a tagged string is sent only once to a client

op=

String catenation

$c @ $d

NL TAB SPC $= !$= ?: [] . () {} , ::

New Line Tab Space String equal to String not equal to Conditional Array element Field/Method selection Grouping Blocking Listing Namespace String constant (normal)

$c NL $d $c TAB $d $c SPC $d $c $= $d $c !$= $d Miscellaneous Operators x?y:z $a[5] %obj.field %obj.method() ---Item::onCollision() Hello world

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Operator

Name String constant (tagged)

Example Torque Rocks

Explanation from the server (across the network). Subsequently, only the tag is sent. All clients will store the string at and index identified by the tag and can look up the value instead of requiring it be sent repeatedly. Used to comment out a single line of TS. Used to comment out multiple consecutive lines. /* opens the comment , and */ closes it.

//

Single line comment

// This is a comment /* This is a a multi-line comment */

/* */

Multi-line comment

A.1.5. Keywords
break default function parent TRUE case else if return while continue FALSE new switch datablock for or switch$

Note: Although keywords are not reserved, it is considered bad practice to use variables that have the same spelling as a keyword.

break
Purpose Use break to exit the innermost for or while loop. switch or switch$ statement. %count = 0; while( 1 ) { echo(%count++); if (%count > 2) break; } See Also case, if, switch, switch$, while

break can also be used to exit a

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

case
Purpose Used to label cases in a switch or switch$ statement. See switch and switch$ examples. See Also break, switch, switch$

continue
Purpose The continue keyword causes the script to skip the remainder of the innermost loop in which it appears; %count = 0; while(%count++ < 8) { if (%count > 2) continue; echo(%count); } See Also: for, while

datablock
Purpose The datablock keyword is used to declare a new datablock. A datablock object is used in the declaration and initialization of a special set of classes that take a datablock(s). Datablocks are created on the server and ghosted to clients. Syntax datablock DatablockClass ( NewDatablockName : InheritDatablock ) { className = SomeName; }; DataBlockBody

DatablockClass A predefined engine class which inherits from SimDataBlock or one of its children. NewDatablockName The name for this datablock. Must be unique.

InheritDatablock Previously defined datablock to inherit (copy) DataBlockBody values from. Optional.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

className This is a special field which can be initialized with the name of a previously defined [in Torque Script] datablock. In effect, this inserts the SomeName name into the namespace calling sequence between NewDatablockName and DatablockClass. For some datablock classes, className can be a non-datablock name, but it isnt guaranteed to work for all calling sequences or classes. DataBlockBody Fields and the values they will be assigned for this datablock.

datablock SimDataBlock( myDataBlock ) { newField = "Hello"; newField2 = 10; }; Here we have declared a new SimDataBlock datablock named myDataBlock. Additionally, we have given it a new field named newField, initialized to Hello and a new field named newField2 initialized to 10. The namespace calling sequence for this datablock is: myDataBlock -> SimDataBlock datablock SimDataBlock( mySecondDataBlock : myDataBlock) { className = myDataBlock; newField2 = 15; }; Here we have declared a new SimDataBlock datablock named mySecondDataBlock that derives from myDataBlock. Because it is deriving from a prior datablock, it will copy any fields that were declared and/or initialized in the parent datablock. However, because we are re-declaring the field newField2. The new initialization value is taken in preference to the copied value, meaning that newField has the value Hello and newField2 has the value 15. Finally, we have defined className as myDataBlock, making the namespace calling sequence for mySecondDataBlock: mySecondDataBlock -> myDataBlock -> SimDataBlock See Also new, Parent

10

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Current Datablock Classes and associated ObjectClasses DataBlock Class CameraData DebrisData ExplosionData FlyingVehicleData GameBaseData HoverVehicleData ItemData LightningData MissionMarkerData ParticleData ParticleEmitterData ParticleEmitterNodeData PathCameraData PlayerData PrecipitationData ProjectileData ShapeBaseData SimDataBlock SplashData StaticShapeData TriggerData VehicleData WheeledVehicleData WheeledVehicleSpring WheeledVehicleTire Object Class Camera Debris Explosion FlyingVehicle GameBase HoverVehicle Item Lightning MissionMarker Particle ParticleEmitter ParticleEmitterNode PathCamera Player Precipitation Projectile ShapeBase - none Splash StaticShape Trigger Vehicle WheeledVehicle WheeledVehicle WheeledVehicle

11

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

default
Purpose This labels the default case in a switch or switch$ statement. executed if no other cases matches the switch/switch$ value.

i.e. the case that is It is a syntax error

Note: The default keyword must be listed after all case keywords. to place it before subsequent case keywords. See switch and switch$ examples. See Also break, switch, switch$

else
Purpose The else keyword is used with the if keyword to control the flow of a script. general form of the well known if-then-else construct is as follows: if (expression) { statement(s); } else { alternate statement(s); } Where the alternate statement(s) are executed if the expression evaluates to 0. See if example. See Also if

The

false
Purpose The false keyword is used for boolean comparison and evaluates to 0. if( false == 0 ) { echo( false evaulates to SPC 0 ); } See Also if, true

12

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

for
Purpose The for keyword is a looping construct whose general form is: for ( expression0 ; expression1 ; expression2 ) { statements(s); } expression0 usually of the form: variable = value expression1 usually of the form: variable compare op value expression2 usually of the form: variable op OR variable op value

The loop continues to execute statement(s) until expression0 evaluates to zero. Note: expression0, expression1, and expression2 are all required. expression0 or expression2 to be empty just insert a 0. If you absolutely need

Note2: Composite expressions of the form ( sub_expression0 , sub_expression1 , sub_expressionN ) are illegal. // Example 1 for( %val = 0 ; %val < { echo( %val ); } 3 ; %val++ )

// Example 2 Empty expression 0 and 2 %value = 0; for( 0 ; %val < 3 ; 0 ) { echo( %val ); %val ++; } // Example 3 Illegal sub-expressions // This would produce an error while compiling %val = 0; for( %val0 = 0 , %val1 = 3 ; %val0 < 3 ; %val0++, %val1-- ) { echo( %val0 ); echo( %val1 ); } See Also break, continue, while

13

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

function
Purpose The function keyword is used to define a new console function or method. Unlike procedural language, functions are not declared in one place and defined in another, but defined only. Redeclaring a named function later in script over-rides the previous definition. the format of a function takes one of two basic forms: // function definition function func_name( [arg0] , , [argn] ) { statements; } [return val;] func_name Name by which this function will be subsequently called. [arg0] , , [argn] Optional arguments. statements Any number of statements may be contained within the body of the function. val Functions may optionally return a value;

// console method definition function namespace::func_name( %this, [arg0] , , [argn] ) { statements; [return val;] } namespace The name of a datablock or object classname. :: Namespace resolution operator. %this The first argument of a console method is always the handle to the object which is calling the method.

// A simple function function test( %val ) { echo( test( SPC %val SPC ) ); if( 10 = %val ) return true; } return false;

// A simple console method function Item::test( %this , %val ) { echo( Item::test( SPC %this SPC , SPC %val SPC ) ); if( 10 = %val ) return true; } ... return false;

14

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

%obj = new Item() { // .. }; %obj.test(10); Item::test(%obj, 10); See Also none.

// Normal method of calling // Alternate method of calling

if
Purpose The if keyword is used with or without the else keyword to control the flow of a script. The general form of the well known if-then-else construct is as follows, if (expression) { statement(s); } else { alternate statement(s); } Where the statement(s) are executed if the expression evaluates to a non-zero value. if(0) { echo( hello ); } else { echo( goodbye ); } if(5) { echo( hello ); } else { echo( goodbye ); } See Also else

15

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

new
Purpose The new keyword is used to instantiate (create) a new copy of a conobject. is: an engine class available in the console (Item(), Player(), etc.), or a datablock (derived or otherwise).

A conobject

// New non-datablock (using) object %obj = new ScriptObject(); //New datablock (using) object datablock ItemData( GoldCoin ) { ... }; %coin = new Item( myGoldCoin ) { // ... datablock = GoldCoin; }; See Also datablock

16

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

package
Purpose The package keyword tells the console that the subsequent block of code is to be declared but not loaded. Packages provide dynamic function-polymorphism in TorqueScript. In short, a function defined in a package will over-ride the prior definition of a same named function when the is activated. When the package is subsequently deactivated, the previous definition of any overridden functions will be re-asserted. Packages have the following syntax: package package_name { function function_definition0() { [statements;] } ... function function_definitionN() { [statements;] } }; Things to know: The same function can be defined in multiple packages. Only functions can be packaged. Datablocks cannot be packaged. Packages stack meaning that deactivating packages activated prior to the currently active (s) will deactivate all packages activated prior to the being deactivated (see example below). Functions in a may activate and deactivate packages. Activating In order to use the functions in a package, the package must be activated: ActivatePackage(_name); Deactivating Subsequently a package can be deactivated: DeactivatePackage(_name); function testFunction() { echo( testFunction() - unpackaged. ); } package MyPackage0 { function testFunction() { echo( testFunction() - MyPackage0. ); } };

17

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

MyPackage1 function testFunction() { echo( testFunction() MyPackage1. ); }

}; ...

testFunction(); // prints => testFunction() - unpackaged. ActivatePackage( MyPackage0 ); testFunction(); // prints => testFunction() - MyPackage0. ActivatePackage( MyPackage1 ); testFunction(); // prints => testFunction() MyPackage1. DeactivatePackage( MyPackage0 ); // MyPackage1 is automatically deactivated. testFunction(); // prints => testFunction() - unpackaged. See Also function, Parent

18

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Parent
Purpose The Parent keyword is used with the namespace operator (::) to reference the previous definition of a function what has been over-ridden either through inheritance or packaging. The Parent keyword can only be used within specific contexts: From within a consoleMethod, or from within a packaged function.

// Calling an inherited parent datablock ItemData( GoldCoin ) { ... }; function ItemData::onAdd( %db, %obj ) { echo( ItemData::onAdd() ); } function GoldCoin::onAdd( %db, %obj ) { Parent::onAdd( %db, %obj ); } echo( GoldCoin::onAdd() );

// Calling a parent function testFunction() { echo( testFunction() - unpackaged. ); } { MyPackage0 function testFunction() { Parent::testFunction(); echo( testFunction() - MyPackage0. ); }

}; ...

testFunction(); // prints => testFunction() - unpackaged. ActivatePackage( MyPackage0 ); testFunction(); // prints => testFunction() - unpackaged. // prints => testFunction() - MyPackage0. See Also datablock, function

19

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

return
Purpose The return keyword is used to return a value from a function function equal_to( %arg0 , %arg1 ) { return ( %arg0 == %arg1 ); } echo( equal_to(10,11) ); // prints 0 echo( equal_to(11,11) ); // prints 1 See Also function

switch
Purpose The switch keyword is used to control the flow of a script. switch statement is as follows: switch (expression) { case value0: statement(s); break; case value1: statement(s); break; . . . case valueN: statement(s); break; default: statement(s); } Where expression is evaluated and the subsequently compared to the following case values. If a case matches the evaluated expression, the statement(s) associated with that case are executed. If no values match and a default statement exists, the statement(s) in the default case will be executed. switch is used ONLY for expressions that evaluate to a numeric value. Note: Unlike C/C++, the break statements in switches are superfluous. Torque Script will only execute matching cases and NOT automatically execute all subsequent cases. This is shown in the example below.

The general form of a

20

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

%tmp = 1; switch( %tmp ) { case 0: echo( 0 ); case 1: echo( 1 ); default: echo( "proof" ); } See Also break, case, default, switch$

switch$
Purpose The switch$ keyword is used to control the flow of a script. switch statement is as follows: switch (expression) { case string_value0: statement(s); break; case string_value1: statement(s); break; . . . case string_valueN: statement(s); break; default: statement(s); } Where expression is evaluated and subsequently compared to the following case values. If a case string_value matches the evaluated expression, the statement(s) associated with that case are executed. If no values match and a default statement exists, the statement(s) in the default case will be executed. switch$ is used ONLY for expressions that evaluate to a string value. Note: Unlike C/C++, the break statements in switches are superfluous. Torque Script will only execute matching cases and NOT automatically execute all subsequent cases.

The general form of a

21

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

%tmp = hi ; switch$( %tmp ) { case bye: echo( bye ); case hi: echo( hi ); } See Also break, case, default, switch

true
Purpose The true keyword is used for boolean comparison and evalulates to 1. if(true == 1) { echo( true evaulates to SPC 1 ); } See Also if, true

while
Purpose The while

keyword is a looping construct whose general form is:

while (expression) { statements(s); } Where expression is usually of the form: variable compare op value, and the loop continues to execute statement(s) until expression evaluates to false (i.e. 0). %val=5; while( %val ) { echo( %val-- ); } See Also break, continue, for

22

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.1.5. Engine Interfacing


The engine provides a concise set of tools to expose the core engine functionality and structures in the console (to Torque Script). The following problem-solution matrix summarizes the things we may want to do and how to do them.
Problem (Want to) Expose Member as Field. Expose Member as Field. Expose/Remove global C++ Variable or static Member as Local Variable Create Console Method from C++. Create Console Function from C++. addField() Con::addVariable() addFieldV() Con::removeVariable() ConsoleMethod() ConsoleFunction() addNamedField() ConsoleFunction() addNamedFieldV() ConsoleMethod() Con::addVariable() Con::removeVariable() addNamedField() addNamedFieldV() Solution addField() addFieldV()

23

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addField
Purpose The addField function provides a means to expose C++ class members as Torque Script object fields. This function is normally called in a class initPersistFields() method. Syntax addField( const const const const char * U32 dsize_t char * in_pFieldname, in_fieldType, in_fieldOffset, in_pFieldDocs ) in_pFieldname, in_fieldType, in_fieldOffset, in_elementCount, in_table, in_pFieldDocs )

addField( const char* const U32 const dsize_t const U32 EnumTable * const char *

in_pFieldname - String specifying variable name as used in console. in_fieldType - The variable type. (Types specified in consoleTypes.h). in_fieldOffset - This is a numeric value calculated using the Offset() macro. in_elementCount - Number of elements at offset. The default value is 1, but if you are referencing an array then this value will be the number of elements in the array. in_table - This argument is used when the field type is TypeEnum. In this special case, you need to define an EnumTable containing a map of the ENUM values and the strings to represent them in the console. in_pFieldDocs - A short string describing the field in plain English. This field is used in Torques automatic Console documentation functionality. class GuiCrossHairHud : public GuiBitmapCtrl { ... ColorF mDamageFillColor; // C++ declaration ... } void GuiCrossHairHud::initPersistFields() { ... // Added here addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor , GuiCrossHairHud )); ... } See Also addFieldV, addNamedField, addNamedFieldV

24

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addFieldV
Purpose The addFieldV function serves the same purpose as addField, but introduces the concept of a Type Validator classes. Type Validator Classes are used to restrict the values a field may be given. If illegal values are assigned to a validated field, the validator function will print an error to the console and set the field to a valid value. The two most commonly used Type Validator Classes are FRangeValidator and IRangeValidator. New validators may be derived from either of these, or their base class TypeValidator. Note: addFieldV does not support arrays or enumerated types. Syntax addFieldV( const char const U32 const dsize_t TypeValidator * in_pFieldname, in_fieldType, in_fieldOffset, * v ) in console. consoleTypes.h). the Offset() macro. of which there are several

* in_pFieldname - String specifying variable name as used in_fieldType - The variable type. (Types are specified in in_fieldOffset - This is a numeric value calculated using * v - This is a pointer to a TypeValidator class instance derived types to choose from.

// from projectile.cc around line 126 // light radius is limited to between // 1.0 and 20.0, inclusive. addFieldV( "lightRadius", TypeF32, ProjectileData, new FRangeValidator(1, 20) ); See Also addField, addNamedField, addNamedFieldV

25

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addNamedField
Purpose The addNamedField is a macro that makes use of the simple version addField function and provides a short-hand method of adding fields. It does not support enumerated types or arrays. Also, it does not provide a means of adding documentation strings. It is worth noting that the macro uses the same name for the field as the variable. Syntax addNamedField( fieldname , type , className ) ; fieldname - Name of variable to expose. Will be used as name of field. type - The variable type. (Types are specified in consoleTypes.h). className - Name of C++ class containing variable.

addNamedField( isBallistic , TypeBool , ProjectileData ); See Also addField, addFieldV, addNamedFieldV

addNamedFieldV
Purpose The addNamedField is a macro that makes use of the addFieldV function and provides a short-hand method of adding validated fields. Syntax addNamedFieldV( fieldname , type, className, validator );

fieldname - Name of variable to expose. Will be used as name of field. type - The variable type. (Types are specified in consoleTypes.h). className - Name of C++ class containing variable. validator - This is a pointer to a TypeValidator class instance of which there are several derived types to choose from.

addNamedFieldV( lightRadius, TypeF32, ProjectileData, new FRangeValidator( 1 , 20 ) ); See Also addField, addFieldV, addNamedField

26

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Con::addVariable
Purpose The Con::addVariable function provides a means to expose global C++ variables or static class Members as Torque Script global variables. This function is normally called in a class consoleInit () method. Syntax Con::addVariable( const char * name , t , void * dp );

name - String specifying name of global variable (in console). t - The variable type. (Types are specified in consoleTypes.h). dp - A pointer to the global C++ variable, or static Member.

// From camera.cc Con::addVariable( "Camera::movementSpeed" , TypeF32 , &mMovementSpeed ); See Also Con::removeVariable

Con::removeVariable
Purpose The Con::removeVariable function provides a means to un-expose global C++ variables or static class Members previously exposed as Torque Script global variables with Con::addVariable. This call is global. i.e. Once we remove a variable, we cannot put it back. Syntax removeField( const char* in_pFieldname ); in_pFieldname - String specifying field to be removed.

// 1. TerrainBlock is inherited from SceneObject. // 2. SceneObject links member mObjToWorld to the // Torque Script field position. // 3. TerrainBlock undoes this(in terrData.cc) removeField( "position" ); See Also Con::addVariable

27

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

ConsoleFunction
Purpose The ConsoleFunction macro provides a means to create function from C++ in the console. Syntax ConsoleFunction( { // Function body } name - This is the name of the function as it will be used in the console. returnType - Is the return type of the function. minArgs - Minimum arguments this function can accept. 1 is the minimum, because the name of the function is automatically passed as the first argument. maxArgs - Maximum arguments this function can accept. If you put 0 in this field, it means any number of arguments may be passed to the function. usage - Is a string that will be printed as a help statement if someone later attempts to use this function with the wrong number of arguments. // From main.cc ConsoleFunction( getSimTime , S32 , 1 , 1, "getSimTime() Time since game started.") { return Sim::getCurrentTime(); } See Also ConsoleMethod name , returnType , minArgs , maxArgs , usage )

ConsoleMethod
Purpose The ConsoleMethod macro provides a means to create Console Method from C++ in the console. The static variant of ConsoleMethod is for methods that you want to be able to call call statically. For example:, GameConnection::getServerConnection(). Syntax ConsoleMethod( className , scriptname , returnType , minArgs , maxArgs , usage ) { } // Method body

// or ConsoleStaticMethod( className , scriptname , returnType , minArgs , maxArgs , usage ) { // Method body }

28

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

className - The name of the class the method is in. scriptname - The name the method will be given in the console (i.e. used by TorqueScript). returntype - The return type of the method. minargs - The minimum arguments this method takes. The minimum is 2, because the name of the console method is automatically passed as the first argument and the objects handle is automatically passed as the second argument. maxargs - The maximum number of args that can be passed to this method. If you put 0 in this field, it means any number of arguments may be passed to the method. usage - A string that will be printed as a help statement if someone later attempts to use this method with the wrong number of arguments. This usage is also when you use the obj.dump() command.

//From SimBase.cc ConsoleMethod( SimObject , getId , S32 , 2 , 2 , "obj.getId()" ) { argc; argv; return object->getId(); } See Also ConsoleFunction

29

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2 Console Objects Fields and Methods Quick Reference


A.2.1. ActionMap
Console Method Summaries
bind getCommand isInverted bindCmd getDeadZone pop getBinding getScale push

Console Methods
bind( device , action , [ modifier , mod... ] , command ) Purpose Use the bind method to associate a function to a keystroke or other device input. Syntax device action modifier command Name of the device to bind the command to (see 'Device Table' below). Name of the action to watch for(see 'Action Table' below). Special modifiers (mouse only), such as dead spot, etc. The function to be called on make and break.

Returns No return value. Notes The command bound via the bind function must be specified as a flat name with no elipses or semi-colon termination and will be called on make and break events (i.e. key press and release for a mapped key). Args: Warning: When a function is bound to a keystroke or other device input, and no other versions of the binding are provided, the function will be called even if a modifier key like CTRL, ALT, or SHIFT is also pressed. For clarification, see 'Bind Sample' example below. See Also bindCmd, getBinding, unbind bindCmd( device , action , makeCmd , breakCmd ) Purpose Use the bindCmd method to associate up to two functions to a keystroke or other device input. Syntax device action makeCmd breakCmd Name of the device Name of the action The function to be The function to be to bind the command to (see 'Device Table' below). to watch for(see 'Action Table' below). called on make event. called on break event.

30

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Returns No return value. Notes The makeCmd is bound to the make event and the breakCmd is bound to the break event and in both cases, the commands are specified as complete scripts, with all arguments, elipses, and the terminating semi-colon. Either of these commands may be non-specified (NULL strings). For clarification, see 'Bind Sample' example below. See Also bind, getBinding, unbind getBinding( command ) Purpose Use the getBinding method to get the binding for a specified command. Syntax command The function to seek a binding for. Returns Returns a string containing the binding as a field (TAB separated string), or a NULL string meaning 'no binding found'. See Also bind, bindCmd getCommand( device , action ) Purpose Use the getCommand method to get the function associated with a specific device + action pair. Syntax device - Name of the device to bound to a command (see 'Device Table' below). action - Name of the action to watch for (see 'Action Table' below). Returns Returns the function name or specification associated with the specified device + action pair, or a NULL-string meaning 'no binding found'. See Also bind, bindCmd, getBinding

31

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getDeadZone( device , action ) Purpose Use the getDeadZone method to get the dead-zone associated with a specific device + action pair. Syntax device - Name of the device to bound to a command (see 'Device Table' below). action - Name of the action to watch for (see 'Action Table' below). Returns Returns a dead-zone specification, or "0 0" meaning that there is no dead-zone, or a NULL string meaning the mapping was not found. See Also bind, bindCmd getScale( device , action ) Purpose Use the getScale method to get the scale associated with a specific device + action pair. Syntax device - Name of the device to bound to a command (see 'Device Table' below). action - Name of the action to watch for (see 'Action Table' below). Returns Returns 1 if no scale is associated with the specified device + action pair, or the mapping was not found. See Also bind, bindCmd isInverted( device , action ) Purpose Use the Purpose method to determine if a specific device + action pair in inverted. Syntax device - Name of the device to bound to a command (see 'Device Table' below). action - Name of the action to watch for (see 'Action Table' below). Returns Returns 1 if the mouse (or other scrolling device) is inverted, 0 otherwise. Notes This only applies to scrolling devices. See Also bind, bindCmd

32

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

pop() Purpose Use the pop method to de-activate an ActionMap and remove it from non-global ActionMap stack. Returns No return value. See Also push push() Purpose Use the pop method to activate an ActionMap and place it at the top of the non-global ActionMap stack. Returns No return value. See Also pop save( [ fileName ] [ , append ] )

Purpose Use the save method to save an entire action map specification to a file. If append is not specified, or specified as false, fileName will be overwritten, otherwise the action map will be appended to the file. Syntax fileName Full path to file in which to store action map definition. append - If true, do not overwrite the file, else start from scratch. Returns No return value. unbind( device , action ) Purpose Use the unbind method to remove a previosly specified device + action pair from the action map. Syntax device - Name of the device to bound to a command (see 'Device Table' below). action - Name of the action to watch for (see 'Action Table' below). Returns No return value. See Also bind, bindCmd

33

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Device Table
Device keyboardN mouseN joystickN unkownN Description This is the Nth keyboard hooked up to the system. For the first keyboard, either "keyboard" or "keyboard0" is acceptable. This is the Nth mouse hooked up to the system. For the first mouse, either "mouse" or "mouse 0" is acceptable. This is the Nth joystick or gamepad hooked up to the system. This is the Nth unknown device up to the system. In other words, some devices has been sampled, but TGE doesn't know what it is.

Action Table
Action button0, buton1, ... , button31 a .. z A .. Z 0..9 F1..F12 shift ctrl alt lshift, rshift, lctrl, rctrl, lalt, ralt Description This is a mouse, joystick, or gamepad button press. For the mouse, buttons 0,1, and 2 are left, right, and middle buttons respectively. See the appendix for other button mappings. These are keyboard inputs. Because this list is so long and in order to accommodate possible variances for special keyboards and other devices a same GUI has been provided with the kit that displays the current action, be it keyboard, mouse, joystick/gamepad, or other device. Simple start the kit and click SampleGUIs -> Input. Follow the instructions provided in the sample. These are modifiers and are not used standalone, but they are included in the action string, for example: "shift p" is the shift key and the p key pressed at the same time. These are special modifier actions. Theye only register as 'break' events when one of these keys: left shift, right shift, left ctrl, right ctrl, left alth, or right alt is released.

Mouse Modifiers
Action Modifiers D %x %y S %s I R %s Description Has dead zone. This is used to add a dead zone for the mouse. Motions in this zone will not be recorded. This can be used to remove the jitter caused by a nervous hand. Has Scale. This is used to scale the mouse motion (by a multiple). Inverted. This is used to invert the mouse. Has Scale. Same as S.

34

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.2. AIConnection
This is a derivative of the class GameConnection (described later) and thus inherits all of that objects methods. Additionally, it adds features that allow this connection type to be driven by AI scripts.

Console Method Summaries


getAddress getTrigger getFreeLook setFreeLook getMove setMove

Console Methods
getAddress() Purpose Use the getAddress method to get the address an AIConnection is currently connected to. Returns Returns the address of the current connection in the format: " A.B.C.D:Port ", where A .. B are standard IP numbers between 0 and 255 and Port can be between 1000 and 65536. getFreeLook() Purpose Use the getFreeLook method to check if the current connection is in free look mode. Returns Returns true if the current connection is free look mode. See Also setFreeLook getMove( field ) Purpose Use the getMove method to get the move setting for field. Syntax field Move setting to check. Returns Returns a value between 0.0 and 1.0. See Also getTrigger, setMove, setTrigger

35

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getTrigger( trigger ) Purpose Use the getTrigger method to get the current value for a trigger specified by the numeric value trigger. Syntax trigger An integer value between 0 and 6 representing on of the seven triggers. Returns Returns 1 if the trigger is active/depressed, and 0 if it is not. See Also getMove, setMove, setTrigger setFreeLook( isFreeLook ) Purpose Use the setFreeLook method to set the current connection to a free look mode. Syntax isFreeLook A boolean value. If true, freelook will be enable, otherwise it will be disabled. Returns No return value. See Also getFreeLook setMove( field , value ) Purpose Use the setMove method to set a move type for the control-object connected to this AIConnection. Syntax field A string containing a move type. value A value between 0.0 and 1.0. Returns No return value. See Also getMove, getTrigger, setTrigger See 'move types' table below.

36

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setTrigger( trigger , set ) Purpose Use the setTrigger method to set or un-set a trigger on the control-object connected to this AIConnection. Syntax trigger An integer value between 0 and 6 representing on of the seven triggers. set A boolean value. If true, this trigger is enabled/set/depressed, otherwise, it is disabled/un-set/released. Returns No return value. See Also getMove, getTrigger, setMove

A.2.3. AIPlayer
This class is used to represent a basic AI driven player model. It has all the fields and methods of the player class. It adds several new methods to allow basic navigation and aiming. Most AI behaviors come from scripts using this small set of methods.

Console Method Summaries


clearAim getMoveDestination setMoveDestination getAimLocation setAimLocation setMoveSpeed getAimObject setAimObject stop

Console Methods
clearAim() Purpose Use the clearAim method to stop aiming at an object or a point. Returns No return value. See Also setAimObject, setAimLocation

37

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getAimLocation() Purpose Use the getAimLocation method to get the point the bot is currently aiming at. This will reflect the position set by setAimLocation(), or the position of the object that the bot is now aiming at, or if the bot is not aiming at anything, this value will change to whatever point the bot's current line-of-sight intercepts. Returns Returns an XYZ vector containing the location of the bot's current aim. See Also setAimObject, setAimLocation getAimObject() Purpose Use the getAimObject method to get the ID of the object the bot is currently aiming at. Returns Returns -1 if no object is being aimed at, or a non-zero positive integer ID of the object the bot is aiming at. See Also getAimLocation, setAimObject, setAimLocation getMoveDestination() Purpose Use the getMoveDestination method to get the last set move destination. Returns Returns a vector containing the <x y z> position of the bot's current move destination. If no move destination has yet been set, this returns "0 0 0". Notes The bot will not look at its destination unless it is told to, which means it can aim at one object or location while walking in another direction. See Also setMoveDestination

38

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setAimLocation( target ) Purpose Use the setAimLocation method to set the bot's aim location to target. Syntax target An XYZ vector representing a position in the game world. Returns No return value. See Also getAimLocation, getAimObject, setAimObject setAimObject( obj [ , offset ] ) Purpose Use the setAimObject method to set the current object for the bot to aim at, using an optional offset to modify that aim. Syntax obj A valid GameBase object ID or name. offset A three-element offset vector which will be added to the position of the aim object. Returns No return value. See Also getAimLocation, getAimObject, setAimLocation setMoveDestination( goal [ , slowDown ] ) Purpose Use the setMoveDestination method to set the bot's current move destination top goal. This will cause the bot to start moving immediately towards that destination. Syntax goal An XYZ vector containing the position for the bot to move to. slowDown A boolean value. If set to true, the bot will slow down when it gets within 5-meters of its move destination. If false, the bot will stop abruptly when it reaches the move destination. By default, this is true. Returns No return value. Notes Upon reaching a move destination, the bot will clear its move destination and calls to getMoveDestination will return a NULL string. See Also getMoveDestination, setMoveSpeed, stop

39

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setMoveSpeed( speed ) Purpose Use the setMoveSpeed method to modify the bot's movement rates between 0.0 (0%) and 1.0 (100%). Syntax speed A speed multiplier between 0.0 and 1.0. This is multiplied by the bot's base movement rates (from its datablock). Returns No return value. See Also setMoveDestination, stop stop() Purpose Use the stop method to stop the bot from moving. Returns No return value. Notes This does not clear the bot's move destination, so a call to getMoveDestination will be able to get it. See Also setMoveDestination, setMoveSpeed

40

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.4. AIWheeledVehicle
Fields
Field Name disableMove Description If this value is set to true, the vehicle will not respond to any movement commands. Sample or Range [ false , true ]

Console Method Summaries


getMoveDestination setMoveTolerance setMoveDestination stop setMoveSpeed

Console Methods
getMoveDestination() Purpose Use the getMoveDestination method to get the last set move destination. Returns Returns a vector containing the <x y z> position of the vehicle's current move destination. If no move destination has yet been set, this returns "0 0 0". Notes The vechicle will not look at its destination unless it is told to, which means it can aim at one object or location while walking in another direction. See Also setMoveDestination setMoveDestination( goal [ , slowDown ] ) Purpose Use the setMoveDestination method to set the vehicle's current move destination top goal. This will cause the vehicle to start moving immediately towards that destination. Syntax goal An XYZ vector containing the position for the vehicle to move to. slowDown A boolean value. If set to true, the vehicle will slow down when it gets within 5-meters of its move destination. If false, the vehicle will stop abruptly when it reaches the move destination. By default, this is true. Returns No return value.

41

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Notes Upon reaching a move destination, the vehicle will clear its move destination and calls to getMoveDestination will return a NULL string. See Also getMoveDestination, setMoveSpeed, setMoveTolerance, stop Args: goal - A vector containing the <x y z> position for the vehicle to move to. slowDown Slow down near destination? setMoveSpeed( speed ) Purpose Use the setMoveSpeed method to modify the vesicle's movement rates between 0.0 (0%) and 1.0 (100%). Syntax speed A speed multiplier between 0.0 and 1.0. This is multiplied by the vesicle's base movement rates (from its datablock). Returns No return value. See Also setMoveDestination, setMoveTolerance, stop setMoveTolerance( tolerance ) Purpose Use the setMoveTolerance method to set the tolerance radius for an AIWheeled vehicle's target position. In other words, how close to the target does the vehicle have to get before it is considered 'on target'? Syntax tolerance The maximum distance away from a move target the vehicle can be and still be considered 'on target'. Returns No return value. Notes Because vehicles can have major variances in size, and because of the way 'on target' is measured, this method is provided to make it easier for a wheeled vehicle to hit it's movement destination. See Also getMoveSpeed, setMoveSpeed

42

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

stop() Purpose Use the stop method to stop the vechicle from moving. Returns No return value. Notes This does not clear the vechicle's move destination, so a call to getMoveDestination will be able to get it. See Also setMoveDestination, setMoveSpeed

A.2.5. AudioDescription
Fields
Field Name coneInsideAngle coneOutsideAngle coneOutsideVolume coneVector environmentLevel is3D isLooping isStreaming Sweep angle of inner cone. Sweep angle of Outside cone. Maximum gain in Zone C (see above). Audio emitters eye (pointing) vector. Amount by which the audioEnvironment datablock used with this datablock will affect sound. Is this a 2D or a 3D sound? If true, sound loops, otherwise sound plays only once. If true, sound source is streaming, otherwise sound is from file. Number of times to loop this sound. -1 Loop infinitely. 0 Loop once and only once. 1 Loop once, possibly twice. (N > 1) Loop N times. Outer boundary for 3D sound sphere. Sound turns on-off here at this distance from 3D emitter. Maximum delay between subsequent loop. Minimum delay between subsequent loop. Distance at which sound turns on to 100% of current maximum gain. Audio type. There can be multiple numeric audio types, each with its own globally controlled max gain. Maximum gain for this source. Description

loopCount

maxDistance maxLoopGap minLoopGap ReferenceDistance type volume

43

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.6. AudioEmitter
Audio emitters are non-visible objects that can be placed in the game world and are used to produce 2D and 3D sound in game. All sounds produced by these emitters are networked. Therefore, all audio profiles, and other sound entities used by emitters must use the datablock keyword (not new).

Fields
Field Name coneInsideAngle coneOutsideAngle coneOutsideVolume coneVector description enableVisualFeedback fileName is3D isLooping loopCount Optional Audiodescription. Enable visual effects showing audio-cones, facing, and on-off indication. Relative or absolute path to sound file. Enables 3D sound. Enable looping. -1 Loop infinitely. 0 Loop once and only once. 1 Loop once, possibly twice. (N > 1) Loop N times. Sweep angle of inner cone. Sweep angle of Outside cone. Maximum gain in Zone C (see above). Audio emitters eye (pointing) vector. Description Sample or Range [0..360] [0..360] [0.0, 1.0] information only (use visual feedback to direct sound) AudioDescription [ false , true ] Filename [ false , true ] [ false , true ] See Description

maxDistance maxLoopGap minLoopGap outsideAmbient position Profile ReferenceDistance type useProfileDescription volume

Outer boundary for 3D sound sphere. Sound turns on-off here at this distance from 3D emitter. Maximum delay between subsequent loop. Minimum delay between subsequent loop. If true, plays outside only (turns off when player is inside interior). Optional audio profile. Maximum gain distance. Inner-cone gain is maxed when player is within this distance of 3D emitter. Distance at which sound turns on to 100% of current maximum gain. Audio type. There can be multiple numeric audio types, each with its own globally controlled max gain. Use audio profile instead of values set via inspector. Emitters maximum gain.

[ referenceDistance , inf ) [ false , true ] AudioProfile [ 0.0 , maxDistance ] [ 0.0 , inf.0 ) [ 0 , inf ) [ false , true ] [ 0.0 , 1.0 ]

44

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.6. AudioProfile (AP)


This is a special type of data block used to describe 2-D and 3-D sounds. It can be created using the new keyword or the data block keyword. In the prior case, the audio description is intended to be used for 2-D sounds usually for interfaces and other client-side sounds. Space In the latter case, the audio description is intended to be used for networked sounds. This is a crucial difference.

Fields
Field Name description environment fileName preload Path to WAV or OGG file. If set to true, file is pre-loaded, otherwise it is loaded on-demand. Description AudioDescription to use with this AP. - ignore Sample or Range see type -~/path/filename.wav [ false , true ]

A.2.8. Camera
The camera class is a lightweight class use to represent the position in view of the camera in the game world. It is derived from shape base, and adds no new fields of its own. It adds a few new methods, used to enable free fly mode and orbiting mode.

Globals
Variable Name Camera::movementSpeed Description Controls the free camera movement speed. Sample or Range [ 0 , inf )

Console Method Summaries


getPosition setFlyMode setOrbitMode

Console Methods
getPosition() Purpose Use the getPosition method to find the current world position of the camera. Returns Return a vector containing the XYZ position of the camera.

45

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setFlyMode() Purpose Use the setFlyMode method to toggle the camera between free fly mode and attached mode. In the prior mode, the camera is able to move about the game world free of an avatar, it is in effect the avatar. The latter mode, is when the camera is attached to another object, the control object. Returns No return value. Notes When if fly-mode, $Camera::movementSpeed controls the movement rate of the camera. setOrbitMode( orbitObject , transform , minDistance , maxDistance , curDistance , ownClientObject ) Purpose Use the setOrbitMode method to force the camera to orbit any particular game base object with a starting position and orientation and at a fixed minimum and maximum distance. Syntax orbitObject The object to orbits about. transform A matrix describing both the position and orientation of the camera. minDistance The minimum distance the camera is allowed to be from the object it is orbiting about. maxDistance The maximum distance the camera is allowed to be from the object it is orbiting about. curDistance The starting position of the camera from the object it is orbiting about. Note, this overrides the position portion of the transform, forcing the camera to be at this distance from the object. ownClientObject Set this to true if the object the camera is orbiting is owned by the client that owns the camera, otherwise set to false. Returns No return value.

46

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.9. CameraData
This is the data block that goes with the camera. Space It is used to do find any special attributes the camera should have, if it is not in fact to deriving these attributes from the object that it is attached to.

A.2.10. ConsoleLogger
This class is supplied to allow us to capture the output of the console and redirected to a file. Is useful in a variety of situations. it can be said to capture all output, only warnings, or only errors.

Fields
Field Name level Description Logging level for this logger. Sample or Range normal, warning, error

A.2.11. Debris
Debris objects are used to represent the refuse left behind by an exploding or destroyed object. However, this object is versatile enough to be used for various purposes, to include a rockfall that blocks the road, the remains of a fallen building, etc.

Fields
Field Name lifeTime Description Lifetime of debris in seconds. Sample or Range [ 0.0 , inf.0 )

Console Methods
init()

init( position , velocity ) Purpose Use the init method to set the initial position and velocity of a debris object. Syntax position A three-element floating-point vector representing the starting location of the debris object in the game world. velocity A three-element floating-point vector representing the direction and magnitude of the debris object's original path. Returns Returns true on success or false on failure.

47

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.12. DebrisData
The data block that goes with debris. This class is used to describe most of the behavior of the debris.

Fields
Field Name baseRadius bounceVariance elasticity emitters[0] emitters[1] explodeOnMaxBounce Explosion fade friction gravModifier ignoreWater lifetime lifetimeVariance maxSpinSpeed minSpinSpeed numBounces render2D shapeFile snapOnMaxBounce staticOnMaxBounce terminalVelocity texture useRadiusMass velocity velocityVariance Description Debris starts at minimum of this distance from creation point. Requires useRadiusMass to be true. Total bounces == numBounces +/- bounceVariance. How bouncy is this debris? Values > 1.0 add energy to the system. ParticleEmitterData datablocks used to trail particles behind moving debris. Does this debris explode when it hits maxBounce count? ExplosionData datablock used for exploding debris. If set to true, the debris will start to fade from view in the last second of its lifetime. How much friction is applied when thid debris slides? How much does gravity affect this debris? If set to false, debris will bounce off of water, else it will sink. Total life of particle == lifetime +/- lifetimeVariance in milliseconds. Total life of particle == lifetime +/- lifetimeVariance in milliseconds. Maximum angular rotation of debris in degrees-per-second. Minimum angular rotation of debris in degrees-per-second. Total bounces == numBounces +/- bounceVariance. If this field is set to true, debris will render a billboard using texture as the image source. The file to be used for this debris' mesh. If set to to true, and staticOnMaxBounce is also true, this debris will 'stick' in its last contact orientation on the surface it contacted. If set to to true, the debris will be replaced (temporarily) with a static shape. This shape will still be destroyed after the debris's total lifetime expires. Maximum velocity at which this debris will fall (or rise). A texture to be used for rendering a billboard, if render2D is set to true. If set to true (and baseRadius > 0.2) , the initial rendering position of the debris will be at a random position baseRadius from the creation point. Total initial velocity == velocity +/ velocityVariance. Total initial velocity == velocity +/ velocityVariance. Sample or Range ( 0.2 , inf.0 ) ( 0 , numBounces ) [ 0.0 , inf.0 ) ParticleEmitterData [ false , true ] ExplosionData [ false , true ] [ 0.0 , 1.0 ] ( -inf.0 , inf.0 ) [ false , true ] [ 0.0 , inf.0 ) [ 0.0 , lifetime ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) [ 0 , inf ) [ false , true ] ~/path/filename [ false , true ] [ false , true ] [ 0.0 , inf.0 ) ~/path/filename [ false , true ] [ 0.0 , inf.0 ) [ 0 .0 , velocity )

48

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.13. DecalData
A data block describing an individual decal.

Fields
Field Name sizeX sizeY textureName The vertical height in meters. The full path to the texture to use this decal. Description The horizontal width in meters. Sample or Range [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) ~/path/filename

A.2.14. DecalManager
All details are managed by the decal manager which provides a few global variables for managing the number of decals that are visible at any one time and the lifetime of the decal. As well we can disable all decals.

Globals
Variable Name $pref::decalsOn Description If set to true, decals are enabled, otherwise no decals will render. Maximum decals allowed at any one time. Once this limit is breached, old decals start to be removed as new decals are added. Time in milliseconds it takes for a decal to be destroyed. Sample or Range [ false , true ]

$pref::Decal::maxNumDecals $pref::Decal::decalTimeout

[ 0 , inf ) [ 0 , inf )

49

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.15. EditManager
While editing, it is possible to set bookmarks on the current position of the camera and then come back to that position by going back to the bookmark. Up to 10 bookmarks can be set. Setting a previously set bookmark will overwrite the old bookmark setting with a new bookmark setting. Bookmarks are not saved between sessions.

Console Method Summaries


setBookMark gotoBookmark

Console Methods
gotoBookmark( slot ) Purpose Use the gotoBookmark method to move the camera to the bookmark specified by slot. Syntax slot An integer between 0 and 9. Returns No return value. See Also setBookmark setBookmark( slot ) Purpose Use the setBookmark method to set a bookmark on the current position of the camera. To bookmark is saved in a position specified by slot. Syntax slot An integer value between zero and nine. Returns No return value. See Also gotoBookmark

50

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.16. Explosion
The explosion class is used to represent various pyrotechnic displays. It is in fact a combination of multiple other classes used simultaneously or in sequence to produce various special effects.

A.2.17. ExplosionData
This is the data block that goes with the explosion class. It is used to describe all of the action be its of an individual explosion.

Fields
Field Name camShakeAmp camShakeDuration camShakeFalloff camShakeFreq camShakeRadius Debris debrisNum debrisNumVariance debrisPhiMax debrisPhiMin debrisThetaMax debrisThetaMin debrisVelocity debrisVelocityVariance delayMS delayVariance emitter[0] emitter[1] emitter[2] emitter[3] explosionscale explosionShape faceViewer lifetimeMS lifetimeVariance lightEndColor lightEndRadius Description The camera shake amplitude for all three axes: X, Y, and Z. Time in seconds the shaking will occur over. Magnitude by which shaking decreases over distance to camShakeRadius. The camera shake frequency for all three axes: X, Y, and Z. Radius about the explosion in which shaking will be applied. DebrisData datablock to use for this explosion. Total debris produced == debrisNum +/- debrisNumVariance. Total debris produced == debrisNum +/- debrisNumVariance. Maximum degrees of angular debris projection about the Y axis. Minimum degrees of angular debris projection about the Y axis. Maximum degrees of angular debris projection about an arbitray axis in the XZ plane. Minimum degrees of angular debris projection about an arbitray axis in the XZ plane. Initial debris projection velocity == debrisVelocity +/- debrisVelocityVariance. Initial debris projection velocity == debrisVelocity +/- debrisVelocityVariance. Delay explosion 'effect' by delayMS +/- delayVariance after explosion object is created. Delay explosion 'effect' by delayMS +/- delayVariance after explosion object is created. ParticleEmitterData to play when explosion occurs. Up to four are allowed. Sample or Range "1.0 2.0 3.0" ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) "1.0 2.0 3.0" ( -inf.0 , inf.0 ) see type ( -inf , inf ) ( -inf , inf ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf , inf ) ( -inf , inf )

see type

The scale of this explosion. An optional shape that can be displayed at the time of the explosion with an (also) optional 'ambient' animation. Keep particles facing viewer. Total life of explosion == lifetimeMS +/- lifetimeVariance. Total life of explosion == lifetimeMS +/- lifetimeVariance. Ending color of light (emitted by explosion). Ending radius of light (emitted by explosion).

"1.0 2.0 3.0" ~/path/filename [ false , true ] ( -inf , inf ) ( -inf , inf ) "1.0 0.5 0.5" ( -inf.0 , inf.0 )

51

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name lightStartColor lightStartRadius offset

Description Starting color of light (emitted by explosion). Starting radius of light (emitted by explosion). Explosion effects offset from explosion creation point.

Sample or Range "1.0 0.5 0.5" ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 )

particleDensity particleEmitter particleRadius playSpeed shakeCamera sizes[0] sizes[1] sizes[2] sizes[3] soundProfile subExplosion[0] subExplosion[1] subExplosion[2] subExplosion[3] subExplosion[4] times[0] times[1] times[2] times[3]

Total number of particles to eject. The emitter description (ParticleEmitterData) for this explosion. Maximum radius from effect center at which particles will be randomly created. Overall rate of this explosion. Scales all effects. If set to true, enables camera shaking. Key-framing size controls for particles.

( -inf , inf ) see type ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) [ false , true ] "1.0 "1.0 "1.0 "1.0 2.0 2.0 2.0 2.0 3.0" 3.0" 3.0" 3.0"

description Sub-explosions to play as part of this explosion. Up to five sub-explosions are allowed.

see type

ExplosionData datablock Using parent explosions as sub-explosions or as children of sub-explosions will create an infinte loop of explosions. Key-framing time controls for particles. [ 0.0, times[1] ] [ times[0], times[2] ] [ times[1], times[3] ] [ times[2], 1.0 ]

A.2.18. FileObject
Fileobject is a class provided to allow us to read write and modify files on the disk.

Console Method Summaries


close openForRead isEOF openForWrite openForAppend readLine

Console Methods
close() Purpose Use the close method to close the current file handle. If the file was opened for writing, this flushes the contents of the last write to disk. Returns No return value. See Also openForAppend, openForRead, openForWrite

52

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isEOF() Purpose Use the isEOF method to check to see if the end of the current file (opened for read) has been reached. Returns Returns true if the end of file has been reached, false otherwise. See Also openForRead openForAppend( filename ) Purpose Use the openForAppend method to open a previously created file for appending. file specified by filename does not exist, the file is created first. Syntax filename The path and filename of the file to open for appending. Returns Returns true if the file was successfully opened for appending, false otherwise. See Also close, openForRead, openForWrite openForRead( filename )and Purpose Use the openForRead method to open a previously created file for reading. Syntax filename The path and filename of the file to open for reading. Returns Returns true if the file was successfully opened for reading, false otherwise. See Also close, OpenForAppend, OpenForWrite If the

53

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

openForWrite( filename ) Purpose Use the openForWrite method to previously created or a new file for writing. case, the file will be overwritten. Syntax filename The path and filename of the file to open for writing. Returns Returns true if the file was successfully opened for writing, false otherwise. See Also close, OpenForAppend, openForRead readLine() Purpose Use the readLine method to read a single line from a file previously opened for reading. Returns Returns the next line in the file, or a NULL string if the end-of-file has been reached. Notes Use isEOF to check for end of file while reading. See Also isEOF, openForRead writeLine( text ) Purpose Use the writeLine method to write a value ( text ) into a file that was previously opened for appending or over-writing. Syntax text The value to write to the file. Returns No return value. See Also openForAppend, openForWrite In either

54

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.19. FlyingVehicle
This class is used represent the flying variety of vehicles. It can be used to represent any variety of flying vehicle, including jets, biplanes, or rocket ships. If you do not actually need a flying vehicle, it is suggested that you use the hover vehicle instead which will closely approximate many similar needs.

Fields
Field Name disableMove Description If set to true, this vehicle will not be allowed to move. Sample or Range [ false , true ]

A.2.20. FlyingVehicleData
Fields
Field Name autoAngularForce autoInputDamping autoLinearForce backwardJetEmitter createHoverHeight Description Angular stabilizer force. This force levels you out when autostabilizer kicks in. Amount by which input is damped to reduce oscillations caused by too much input. Linear stabilzer force. This slows you down when autostabilizer kicks in. ParticleEmitterData datablock used for the thrust backward jet emitter. Emitter will be attached at node: JetNozzleX. If set to true, uses hoverHeight to specify height to hover after creation. ParticleEmitterData datablock used for the downward thrust jet emitter(s). Emitters will be attached at nodes: JetNozzle2 , and JetNozzle3. AudioProfile for engine ambient sound. ParticleEmitterData datablock used for the forward thrust jet emitter(s). Emitters will be attached at nodes: JetNozzle0 , and JetNozzle1. Wing thrust used in climbing and diving. Height to hover at after creation if createHoverHeight is set to true. AudioProfile for engine thrust sound. Horizontal thrust force. Autostabilizer/AutoLinearForce are enabled when the vehicle's velocity magnitude drops below this value. Minimum speed at which contrails turn on. Automatically applied rolling force used to 'right' a tilted vehicle. Amount of drag applied to rotation about the vehicles up axis. Strength of turning thrust. How much you roll when turning. Sample or Range [ 0.0 , 0.inf ) [ 0.0 , 1.0 ) [ 0.0 , 0.inf ) -[ false , true ]

downJetEmitter engineSound forwardJetEmitter horizontalSurfaceForce hoverHeight jetSound maneuveringForce maxAutoSpeed minTrailSpeed rollForce rotationalDrag steeringForce steeringRollForce

---[ 0.0 , 0.inf ) [ 0.0 , 0.inf ) -[ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf )

55

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name trailEmitter verticalSurfaceForce vertThrustMultiple

Description ParticleEmitterData datablock used for the contrail emitter(s). Emitters will be attached at nodes: contrail0 , contrail1, contrail2, and contrail3. Wing thrust used in 'sliding' side to side during turns. TypeF32

Sample or Range -[ 0.0 , 0.inf ) [ 0.0 , 0.inf )

A.2.21. fxFoliageReplicator
This class was originally intended to represent foliage in the scene. In fact, it can be used to represent anything that is displayed using a billboard. The value of this class is that all of the objects represented by a single foliage replicator are tied to a single instance of the class. Thus, regardless of the number of objects which may be displayed by any single foliage replicator the same network bandwidth is used. The billboards may be given a variety of behaviors and attributes. They may be stretched, self lighted, they may sway, and they may be are restricted to specific areas.

Fields
Field Name Transform position rotation scale Debugging useDebugInfo debugBoxHeight HideFoliage showPlacementArea placementAreaHeight placementColour Media/Replications seed foliageFile foliageCount foliageRetries Value used to deterministically generate random object positions and parameters. Texture file to use for foliage. Must be suitable for use as billboard. Number of billboards to replicate. Determines how many times to attempt to place a billboard. Retries are sometimes required in order to meet placement criteria. Failed placement attempts result fewer objects placed. [ 0 , inf ) ~/path/filename [ 0 , inf ) [ 0 , inf ) Enable debug feedback. Some features must be uncommented in code to turn them on. Quad-tree box heights. Stop displaying foliage. Show the placement feedback device. Changes height of feedback device. Changes color of feedback device. [ false , true ] [ 0.0 , 0.inf ) [ false , true ] [ false , true ] [ 0.0 , 0.inf ) 1.0 1.0 1.0 1.0 XYZ position of fx object. Values have no effect. Values have no effect. 10.0 20. 0.0 --Description Sample or Range

Area/Placement Radius innerRadiusX X dimension of inner do-not-place ellipse. Objects are not allowed in ellipse described by this and the InnerRadiusY dimension. Y dimension of inner do-not-place ellipse. Objects are not allowed in ellipse described by this and the InnerRadiusX dimension. [ 0.0 , 0.inf )

innerRadiusY

[ 0.0 , 0.inf )

56

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name outerRadiusX

Description X dimension of outer do-not-place ellipse. Objects are not allowed outside ellipse described by this and the OuterRadiusY dimension. Y dimension of outer do-not-place ellipse. Objects are not allowed outside ellipse described by this and the OuterRadiusX dimension. If scaling enabled (FixSizeToMax == false), this defines the billboards minimum width. If scaling enabled (FixSizeToMax == false), this defines the billboards maximum width. If scaling enabled (FixSizeToMax == false), this defines the billboards minimum height. If scaling enabled (FixSizeToMax == false), this defines the billboards maximum height. Prevents texture stretching. Forces all billboards to use MaxWidth and MaxHeight as their respective width and height. This allows you to assist placement by lowering or raising the billboard by a fixed amount. Allows random horizontal flipping of billboards, increasing variation for non-symmetric billboards. Enables culling algorithms. i.e. test whether a billboard is within view before rendering it. Warning: Computing overhead offsets value for small placements. Only use for large placements if at all. Determines size of culling quads. Lower values of CullResolution take longer to cull but more effectively removes non-visible billboards. Higher values of CullResolution take less time, but end up rendering more non-visible billboards. Tradeoff: Culling Time vs. Fill Rate. When a billboards is at distance ViewDistance from the camera, it will be completely visible. Controls closest point at which billboards will begin to fade out. Together, these determine width of region between fade-in and fade-out. Controls general alpha level for rendering billboards. Controls alpha in region near ground. For fixing artifacts between billboard and ground. Enables sway animation. All billboards sway in sync for this fx Object. Side-to-side swaying magnitude. Back-and-forth swaying magnitude.

Sample or Range [ 0.0 , 0.inf )

outerRadiuxY Dimension minWidth maxWidth minHeight maxHeight fixAspectRatio fixSizeToMax offsetZ randomFlip Culling <useCulling>

[ 0.0 , 0.inf )

[ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ false , true ] [ false , true ] ( -0.inf , 0.inf ) [ false , true ]

[ false , true ]

cullResolution

[ 8 , inf )

viewDistance viewClosest fadeInRegion fadeOutRegion alphaCutoff groundAlpha Animation swayOn swaySync swayMagSide swayMagFront

[ 0.0 , 0.inf ) [ 0.0 , 0.inf )

[ 0.0 , 0.inf ) [ 0.0 , 1.0 ] [ 0.0 , 1.0 ]

[ false , true ] [ false , true ] [ 0.0 , 0.inf ) [ 0.0 , 0.inf )

57

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name minSwayTime

Description Minimum sway time. Sway times are randomly chosen between a min and max on a per swing basis. Maximum sway time. Sway times are randomly chosen between a min and max on a per swing basis. Turns on luminance (self lighting). Light billboards in sync. Minimum light value. Maximum light value. Time required to transition from Min to Max and Max to Min. i.e. each duration is equal to lightTime. Allows objects to be placed on terrain. Allows objects to be placed on interiors. Allows objects to be placed on static shapes. Allows objects to be placed in area covered by water. Place on surface of water. Otherwise will be placed on terrain below water. Maximum slope to place on. Slopes beyond this value will be devoid of objects.

Sample or Range [ 0.0 , 0.inf )

maxSwayTime Lighting lightOn lightSync minLuminance maxLuminance lightTime Restrictions/Restraints allowOnTerrain allowOnInteriors allowOnStatics allowOnWater allowWaterSurface allowedTerrainSlope

[ 0.0 , 0.inf )

[ false ,true ] [ false , true ] [ 0.0 , maxLuminance ] [ minLuminance , 1.0 ] [ 0.0 , 0.inf )

[ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ]

Console Functions
StartFoliageReplication()

startFoliageReplication() Purpose Use the startFoliageReplication function to start the foliage replication system. Returns No return value. Notes This can be called before be called after replication objects have been placed, but it should only be called once, and if it is not called, replicators will not work. See Also startClientReplication

58

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.22. fxLight
The fxLight object is used to represent in-game lights with and respective flares. This object casts (dynamic) light in the scene. It renders both a representation of the light source flare, and casts light on terrain and other objects.

Fields
Field Name enable iconSize Description Boolean value enabling this light. Floating-point value specifying render size of flare. Sample or Range [ false , true ] ( -inf.0 , inf.0 )

Console Method Summaries


attachToObject detachFromObject reset

Console Methods
attachToObject( obj ) Purpose Use the attachToObject method to attach this fxLight object to GameBase object. Syntax obj A GameBase derived object to attach to Returns No return value. Notes This method name is bit of a misnomer. When an fxLight object is attached to another object, it doesn't mean the fxLight will move with the object, but rather that if the object the fxLight is attached to is deleted, this object will be deleted. Also, the fxLight will always be processed (for rendering) after the object it is attached to. See Also detachFromObject detachFromObject() Purpose Use the detachFromObject method to detach this fxLight from the last object it was attached to. Returns No return value. See Also attachToObject

59

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

reset() Purpose Use the reset method to lighting, animation, and texture of the current fxLight. Returns No return value. setEnable( enabled ) Purpose Use the setEnable method to enable or disable an fxLight. Syntax enabled A boolean value. Returns No return value. If set to true, this fxLight will cast light and be rendered.

A.2.23. fxLightData
The data block associated with fxLight. This data block describes nearly all of the attributes of the fxLight object.Fields

Fields
Field Name AnimBrightness AnimColour AnimOffsets AnimRadius AnimRotation BlendMode Enable color animation. Enable offset animation. Enable radius animation. Enable rotation animation. 0 = GL_SRC_ALPHA, GL_ONE 1 = GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 2 = GL_ONE, GL_ONE Key frame values for blue channel animation. Starting brightness. Key frame values for brightness animation. Round-trip animation time for brightness. Initial light color. Round-trip animation time for color. Screen size for flare if not animated. Enable screen size animation for flare bitmap. Ending offset for offset animations. Round-trip animation time for fade. Distance from light when flare achieves screen size of FarSize. Description Enable brightness animation. Sample or Range [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [0,2]

BlueKeys Brightness BrightnessKeys BrightnessTime Colour ColourTime ConstantSize ConstantSizeOn EndOffset FadeTime FarDistance

ABCDABCD [ 0.0 , 1.0 ] ABCDABCD [ 0.0 , 0.inf ) 0.5 0.5 0.7 1.0 [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ false , true ] [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf )

60

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name FarSize FlareBitmap FlareColour FlareOn FlareTP GreenKeys LerpBrightness LerpColour LerpOffset LerpRadius LerpRotation LightOn LinkFlare

Description Screen size for flare at FarDistance and beyond. Bitmap used represent the light source. Starting color for flare bitmap. Enable flare bitmap. Turns flare off in third person. Key frame values for green channel animation. Enable brightness interpolation. Enable color interpolation. Enable offset interpolation. Enable radius interpolation. Enable rotation interpolation. Enable light. Flag controlling whether the flare is linked to the light. If on then the flare tracks the color & brightness settings of the light to control its colorization. Flag controlling whether the flare is linked to the light by size. If on then the flare tracks the color & brightness settings of the light to control its size. The brighter the light, the larger the flare. The flare is scaled according to it's current animation e.g. it never gets any bigger than it would with this setting off. Maximum brightness when brightness animation is enabled. Maximum color when color animation is enabled. Maximum radius when radius animation is enabled. Maximum rotation when rotation animation is enabled. Minimum brightness when brightness animation is enabled. Minimum color when color animation is enabled. Minimum radius when radius animation is enabled. Minimum rotation when rotation animation is enabled. Distance from light when flare achieves screen size of NearSize. Screen size for flare at NearDistance and closer. Key frame values for offset animation. Round-trip animation time for offset. Initial light sphere radius. Key frame values for radius animation. Round-trip animation time for radius. Key frame values for red channel animation. Key frame values for rotation animation. Round-trip animation time for rotation. Use RedKeys to animate all colors at the same time. Starting offset for offset animation.

Sample or Range [ 0.0 , 0.inf ) ~/data/flare.png 0.5 0.5 0.7 1.0 [ false , true ] [ false , true ] ABCDABCD [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ]

LinkFlareSize

[ false , true ]

MaxBrightness MaxColour MaxRadius MaxRotation MinBrightness MinColour MinRadius MinRotation NearDistance NearSize OffsetKeys OffsetTime Radius RadiusKeys RadiusTime RedKeys RotationKeys RotationTime SingleColourKeys StartOffset

[ 0.0 , 0.inf ) 0.5 0.5 0.7 1.0 [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) 0.5 0.5 0.7 1.0 [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) ABCDABCD [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) ABCDABCD [ 0.0 , 0.inf ) ABCDABCD ABCDABCD [ 0.0 , 0.inf ) ABCDABCD [ 0.0 , 0.inf )

61

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.24. fxShapeReplicator
This class is used to replicate any shape. It works in nearly the same way as the fxFoliageReplicator. Each of the individual shapes may enable or disable a collision box allowing them to be interacted with or ignore it in collision interactions.

Fields
Field Name Transform position rotation scale Debugging HideReplications ShowPlacementArea PlacementAreaHeight PlacementColour Media/Replications Seed shapeFile ShapeCount ShapeRetries Value used to deterministically generate random object positions and parameters. DTS file to replicate. Number of shapes to replicate. Determines how many times to attempt to place a shape. Retries are sometimes required in order to meet placement criteria. Failed placement attempts result fewer objects placed. [ 0 , inf ) ~/data/myshape.dts [ 0 , inf ) [ 0 , inf ) Stop displaying shapes. Show the placement feedback device. Changes height of feedback device. Changes color of feedback device. [ false , true ] [ false , true ] [ 0.0 , inf.0 ) 1.0 0.5 0.5 1.0 XYZ position of fx object. Values have no effect. Values have no effect. 10.0 20. 0.0 --Description Sample or Range

Area/Placement Radius InnerRadiusX X dimension of inner do-not-place ellipse. Objects are not allowed in ellipse described by this and the InnerRadiusY dimension. Y dimension of inner do-not-place ellipse. Objects are not allowed in ellipse described by this and the InnerRadiusX dimension. X dimension of outer do-not-place ellipse. Objects are not allowed outside ellipse described by this and the OuterRadiusY dimension. Y dimension of outer do-not-place ellipse. Objects are not allowed outside ellipse described by this and the OuterRadiusX dimension. Allows objects to be placed on terrain. Allows objects to be placed on interiors. Allows objects to be placed on static shapes. Allows objects to be placed in area covered by water. Place on surface of water. Otherwise will be placed on terrain below water. Maximum slope to place on. Slopes beyond this value will be devoid of objects. [ 0.0 , 0.inf )

InnerRadiusY

[ 0.0 , 0.inf )

OuterRadiusX

[ 0.0 , 0.inf )

OuterRadiuxY Restrictions/Restraints AllowOnTerrain AllowOnInteriors AllowOnStatics AllowOnWater AllowWaterSurface AllowedTerrainSlope

[ 0.0 , 0.inf )

[ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ]

62

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name AlignToTerrain Interactions TerrainAlignment Object Transforms ShapeScaleMin ShapeScaleMax ShapeRotationMin ShapeRotationMax OffsetZ

Description Causes DTS shapes to align to up vector of terrain if placed on terrain. Enables collision boxes if DTS shape has one. Vector to adjust how shape aligns to terrain when AlighnToTerrain is true. Minimum randomly selected scale for DTS shape. Maximum randomly selected scale for DTS shape. Minimum random rotation for DTS shape. Maximum random rotation for DTS shape. This allows you to assist placement by lowering or raising the shape by a fixed amount.

Sample or Range [ false , true ] [ false , true ] 0.7 0.7 0.7

[ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) [ 0.0 , 0.inf ) 0.0 1.2 0.0

Console Functions
StartClientReplication()

StartClientReplication() Purpose Use the StartClientReplication function to start the shape replication system. Returns No return value. Notes This can be called before be called after replication objects have been placed, but it should only be called once, and if it is not called, replicators will not work. See Also startFoliageReplication

63

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.25. fxSunLight
This class is used to represent celestial bodies. You may defined as many fxSunlight objects as you need in your seeing. Each object may be animated independently. As a whole, this class provides several features allowing us to represent a variety of celestial bodies with many different behaviors.

Fields
Field Name AnimAzimuth AnimBrightness AnimColour AnimElevation AnimRotation AnimSize AzimuthKeys AzimuthTime Description Enable azimuth animation. Enable brightness animation. Enable color animation. Enable elevation animation. Enable rotation animation. Enable size animation. Key frame values for azimuth animation. Round-trip animation time for azimuth. 0 = GL_SRC_ALPHA, GL_ONE 1 = GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 2 = GL_ONE, GL_ONE Key frame values for blue channel animation. Starting brightness. Key frame values for brightness animation. Round-trip animation time for brightness. Initial color. Round-trip animation time for color. Key frame values for elevation animation. Round-trip animation time for elevation. Enable or disable fxSunlight object rendering. Round-trip animation time for fade. Size for flare bitmap. If true, enables flare in third person. Key frame values for green channel animation. Enable azimuth interpolation. Enable brightness interpolation. Enable color interpolation. Enable elevation interpolation. Enable rotation interpolation. Enable size interpolation. Flag controlling whether the flare is linked to the light by size. If on then the flare tracks the color & brightness settings of the light to control its size. The brighter the light, the larger the flare. The flare is scaled according to it's current animation e.g. it never gets any bigger than it would with this setting off. Lens flare texture. Sample or Range [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] ABCDABCD [ 0.0 , inf.0 ) [0,2]

BlendMode BlueKeys Brightness BrightnessKeys BrightnessTime Colour ColourTime ElevationKeys ElevationTime Enable FadeTime FlareSize FlareTP GreenKeys LerpAzimuth LerpBrightness LerpColour LerpElevation LerpRotation LerpSize

ABCDABCD [ 0.0 , inf.0 ) ABCDABCD [ 0.0 , inf.0 ) 1.0 1.0 0.0 1.0 [ 0.0 , inf.0 ) ABCDABCD [ 0.0 , inf.0 ) [ false , true ] [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ false , true ] ABCDABCD [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ]

LinkFlareSize

[ false , true ]

LocalFlareBitmap

~/path/filename.png

64

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name LockToRealSun MaxAzimuth MaxBrightness MaxColour MaxElevation MaxRotation MaxSize MinAzimuth MinBrightness MinColour MinElevation MinRotation MinSize NearDistance NearSize OffsetKeys OffsetTime RedKeys RemoteFlareBitmap RotationKeys RotationTime SingleColourKeys SizeKeys SizeTime SunAzimuth SunElevation

Description Elevation and azimuth for this object follows elevation and azimuth for Sun object. Maximum azimuth setting. Maximum brightness when brightness animation is enabled. Maximum color when color animation is enabled. Maximum elevation. Maximum rotation when rotation animation is enabled. Maximum size for flare. Minimum azimuth setting. Minimum brightness when brightness animation is enabled. Minimum color when color animation is enabled. Minimum elevation setting. Minimum rotation when rotation animation is enabled. Minimum flare size. Distance from light when flare achieves screen size of NearSize. Screen size for flare at NearDistance and closer. Key frame values for offset animation. Round-trip animation time for offset. Key frame values for red channel animation. The sun texture. Key frame values for rotation animation. Round-trip animation time for rotation. Use RedKeys to animate all colors at the same time. Key frame values for size animation. Round-trip animation time for size. Initial azimuth setting. Initial elevation setting.

Sample or Range [ false , true ] [ 0.0 , 360.0 ) [ 0.0 , inf.0 ) [ -90.0 , 90.0 ] [ 0.0 , 360.0 ) [ 0.0 , inf.0 ) [ 0.0 , 360.0 ) [ 0.0 , inf.0 ) 0.0 0.0 1.0 1.0 [ -90.0 90.0 ] [ 0.0 , 360.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) ABCDABCD [ 0.0 , inf.0 ) ABCDABCD ~/path/filename.png ABCDABCD [ 0.0 , inf.0 ) ABCDABCD ABCDABCD [ 0.0 , inf.0 ) [ 0.0 , 360.0 ) [ -90.0 , 90.0 ]

Console Method Summaries


Long descriptions for these functions have not been supplied because the functions are very simple and their names are quite descriptive.
reset() setBlendMode( mode ) setBrightnessTime( time ) setElevationTime( time ) setFlareBitmaps( local , remote ) setFlareSize( size ) setLerpAzimuth( status ) setLerpElevation( status ) setLinkFlareSize( status ) setMaxColour( r , g , b ) setMaxSize( size ) setAzimuthKeys( keys ) setBlueKeys( keys ) setColourTime( time ) setEnable( status ) setFlareBrightness( brightness ) setFlareTP( status ) setLerpBrightness( status ) setLerpRotation( status ) setMaxAzimuth( azimuth ) setMaxElevation( elevation ) setMinAzimuth( azimuth ) setAzimuthTime( time ) setBrightnessKeys( keys ) setElevationKeys( keys ) setFadeTime( time ) setFlareColour( r , g , b ) setGreenKeys( keys ) setLerpColour( status ) setLerpSize( status ) setMaxBrightness( brightness ) setMaxRotation( rotation ) setMinBrightness( brightness )

65

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

reset() setMinColour( r , g , b ) setMinSize( size ) setRotationTime( time ) setSizeTime( time ) setUseAzimuth( status ) setUseElevation( status )

setAzimuthKeys( keys ) setMinElevation( elevation ) setRedKeys( keys ) setSingleColourKeys( status ) setSunAzimuth( azimuth ) setUseBrightness( status ) setUseRotation( status )

setAzimuthTime( time ) setMinRotation( rotation ) setRotationKeys( keys ) setSizeKeys( keys ) setSunElevation( elevation ) setUseColour( status ) setUseSize( status )

A.2.26. GameBase
This is the base class to all classes using or requiring a data block. This class can be considered virtual as you do not create instances of it in the game world.

Fields
Field Name dataBlock Description The corresponding data block for this object. Sample or Range GameBaseData datablock

Globals
Variable Name $gameBase::boundingBox Description If true, objects will display their bounding boxes. Sample or Range [ false , true ]

Console Method Summaries


getDataBlock setDataBlock

Console Methods
getDataBlock() Purpose Use the getDataBlock method to get the datablock used by this object. Returns No return value. See Also setDataBlock

66

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setDataBlock( db ) Purpose Use the setDataBlock method to change the datablock for this object to a new datablock represented by db. Syntax db A datablock name or ID. Returns Returns true if the datablock with successfully changed, otherwise it returns false. Notes The new datablock must be of a compatible type with the current class. See Also getDataBlock

A.2.27. GameBaseData
This class is the corresponding data block the gamebase. It can be considered virtual also.

Fields
Field Name category className Description Creator category to place this object in. A new namespace to be inserted between the name of the data block and the class name of the data block. Sample or Range MyCategory SomeClassname

A.2.28. GameConnection
A class connecting the client in the current executable to a server in the current executable or to a server and external executable.

Console Method Summaries


activateGhosting delete getControlObject isDemoPlaying listClassIDs playDemo setCameraObject setControlObject setMissionCRC chaseCam getCameraObject getServerConnection isDemoRecording play2D resetGhosting setConnectArgs setFirstPerson startRecording clearCameraObject getControlCameraFov isAIControlled isFirstPerson play3D setBlackOut setControlCameraFov setJoinPassword stopRecording

67

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Methods
activateGhosting() Purpose Use the activateGhosting method to GameConnection instance to start ghosting objects to the client. Returns No return value. Notes This is called on each client connection by the server. See Also resetGhosting chaseCam( delay ) Purpose Use the chaseCam method to insert a delay in between translations and rotations of the control object and the attached 3rd POV camera. That is, if delay is a positive non-zero value, the camera will lag behind translations and rotations of the 3rd POV player. Syntax delay An integer value equal to or greater than zero. If this value is greater than zero, the camera will begin to lag the players translations and rotations. Notes This can quickly become disconcerting as it disconnects player input from the results. Use this feature cautiously.

68

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

delete( [ reason ] ) Purpose Use the delete method to destroy and disconnect the current connection, giving an optional reason. If reason is specified, it will be transmitted to the client/server on the other end of the connection. Syntax reason A string explaining while the connection is being severed. Returns No return value. getCameraObject() Purpose Use the getCameraObject method to get the current camera object ID.

Returns Returns the ID of the current camera object, or the control object if the POV is first person. See Also setCameraObject() getControlCameraFov() Purpose Use the getControlCameraFov method to FOV of the current control 'camera'. Returns Returns a FOV angle between 0.0 and 180.0. Notes This may not actually be the FOV of the camera object, but rather the FOV being used by the camera. The difference is subtle, but if a camera is attached to an object and using that object's FOV setting, then the FOV is actually coming from the object, not the camera. See Also getCameraObject, setControlCameraFov

69

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getControlObject() Purpose Use the getControlObject method to get the ID of the current control-object for this GameConnection. Returns Returns an integer value representing the ID of an object acting as the control-object for this GameConnection. If no control-object was set up, this will return 0. getServerConnection() Purpose Use the getServerConnection method to get the ID of the server this client-side GameConnection is attached to. Returns Returns an integer representing the ID of a server-side GameConnection on the other end of this client-side GameConnection. If no server is connected, or if this GameConnection is a server-side connection, the return value is 0. isAIControlled() Purpose Use the isAIControlled method to determine if this is an AIGameConnection. Returns Returns true if this is an AIGameConnection instance. Notes This will always return false for a GameConnection and always return true for an AIGameConnection. It was defined here to allow scripts to call it universally on either type of connection. isDemoPlaying() Purpose Use the isDemoPlaying method to see if this GameConnection is playing a demo. Returns Returns true if a demo is being played, false otherwise. See Also isDemoRecording, playDemo, startRecording, stopRecording

70

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isDemoRecording() Purpose Use the isDemoRecording method to see if this GameConnection is recording a demo. Returns Returns true if a demo is being recorded, false otherwise. See Also isDemoPlaying, playDemo, startRecording, stopRecording isFirstPerson() Purpose Use the isFirstPerson method to see if the camera attached to this GameConnection is in 1st POV. Returns Returns true if the camera attached to this GameConnection is in 1st POV, false otherwise. See Also setFirstPerson listClassIDs() Purpose Use the listClassIDs method to dump a list of all class IDs that this GameConnection knows about. Returns No return value. Notes This is a debug feature. play2D( AP ) Purpose Use the play2D method to play a 2D AudioProfile previously specified using the datablock keyword. Syntax AP A 2D AudioProfile previously specified using the datablock keyword. Returns Returns true if the sound can be played, false otherwise. Notes Be sure to use only AudioProfiles created using the datablock keyword. See Also play3D

71

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

play3D( AP , position ) Purpose Use the play2D method to play a 3D AudioProfile previously specified using the datablock keyword. Syntax AP A 3D AudioProfile previously specified using the datablock keyword. position The position to play this sound at. Returns Returns true if the sound can be played, false otherwise. Notes Be sure to use only AudioProfiles created using the datablock keyword. See Also play2D

playDemo( demoFileName ) Purpose Use the playDemo method to play a previously recorded demo on this GameConnection. Syntax demoFileName A path and file name pointing to a previously recorded demo. Returns Returns true if the demo was successfully played, false otherwise. See Also isDemoPlaying, isDemoRecording, startRecording, stopRecording resetGhosting() Purpose Use the resetGhosting method to reset ghosting. This in effect tells the server to resend each ghost to insure that all objects which should be ghosts and are in fact ghosted. Returns No return value. See Also activateGhosting

72

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setBlackOut( doFade , timeMS ) Purpose Use the setBlackOut method to fade (black) the screen out, or in. Syntax doFade A boolean value. If set to true, the screen is blacked out, if false, the blackout is reversed. timeMS An integer value specifying the time it takes to fade-out or to fade back in. Returns No return value. Notes This is only called on the client-side GameConnection (from client to server), calling in on a server-side connection (from server to client), will do nothing. The GameBase class provides two similar features for whiting out the screen and causing damage flashes. setCameraObject( newCamera ) Purpose Use the setCameraObject method to change the camera object associated with this GameConnection to newCamera. Syntax newCamera The ID of a valid camera object. Returns Returns true on success and false on failure. Notes This can only be called on the serer-side GameConnection. See Also getCameraObject setConnectArgs( name [ , arg1 , ... , arg15 ] ) Purpose Use the setConnectArgs method to set the connection arguments for this client-side GameConnection. These values will be passed to the server upon establishing a connection. Syntax name Generally, the first argument is the name of the player. arg1 , ... , arg15 15 additional arguments may be passed. Returns No return value. See Also setJoinPassword

73

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setControlCameraFov( newFOV ) Purpose Use the setControlCameraFov method to change the FOV of the control object 'camera'. Syntax newFOV A floating-point value between 0.0 and 180.0 representing the new field-of-view setting for any and all cameras attached to this connection. Returns No return value. Notes This setting is sticky. That is, as the camera object is changed, this value will be retained. These values are however constrained by the datablock min and max FOV settings that are currently in effect. See Also getControlCameraFov setControlObject( object ) Purpose Use the setControlObject method to change the control object for this server-side GameConnection to the GameBase object specified by object. Syntax object A valid GameBase object to make the new control object. Returns Returns true if the control object was successfully changed, otherwise returns false. Notes The same control object may NOT be used by more than one client connection. See Also getControlObject

74

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setFirstPerson( isFirstPerson ) Purpose Use the setFirstPerson method to change the current point-of-view to first or thirdperson, depending on the value of isFirstPerson. Syntax isFirstPerson A boolean value. If set to true, the POV for this GameConnection is set to 1st POV, otherwise it is set to 3rd POV. Returns No return value. Notes This will NOT override value specified in the control-object/camera datablocks enabling or disabling a particular POV. See Also isFirstPerson setJoinPassword( password ) Purpose Use the setJoinPassword method to set the password required to connect to this serverside GameConnection. Syntax password A string representing the case insensitive password to use for this server-side GameConnection. Returns No return value. Notes Pass a NULL string to clear the password. See Also setConnectArgs setMissionCRC( CRC ) Purpose Use the setMissionCRC method to set the cyclic-redundancy-check (CRC) value for the current mission. This allows clients to determine whether they need to relight a scene or not. The CRC value of a mission file is used to calculate the signature of a mission lighting file (.ml). If the signature does not match the current CRC, the mission will be relit. Syntax CRC An integer value representing the cyclic-redundancy-check code for the current misssion file. Returns No return value.

75

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

startRecording( fileName ) Purpose Use the startRecording method to tell the GameConnection to start recording a demo. The demo will be stored at the location specified by fileName. Syntax fileName To location to store the demo file. This should be a complete filename, including path and file name. Returns No return value. See Also isDemoPlaying, isDemoRecording, playDemo, stopRecording stopRecording() Purpose Use the stopRecording method to tell the GameConnection to stop recording a demo. Returns No return value. See Also isDemoPlaying, isDemoRecording, playDemo, startRecording transmitDataBlocks( sequence ) Purpose Use the transmitDataBlocks method to start sending datablocks to the client connected to this GameConnection. Syntax sequence A book-keeping value used by scripts. Returns No return value.

A.2.29. HoverVehicle
This class is used represent the hover variety of vehicles. Space A hover vehicle is any vehicle that must maintain a specific distance between the vehicle and the ground at all times, which is not the same as a flying vehicle which may change this distance.

76

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.30. HoverVehicleData
The datablock associated with hover vehicle class. This class describes all of the behaviors of the hover vehicle.

Fields
Field Name brakingActivationSpeed brakingForce dragForce dustTrailEmitter dustTrailFreqMod Description Minimum rate at which braking force will be activated. A force used to bring a drifting hover vehicle to a halt. A percentage of the current velocity applied in the opposite direction of the hover vehicles current motion. ParticleEmitterData datablock for dust trails. Dust trail modulator. Lower values equal more dense trail, higher values equal more sparse trail. A vector by which to offset the dust trail emitter from it's mount node. By default the emitter plays directly beneath the center of the vehicle. Engine sound special effect. Gravity modifier that is enabled while vehicle is in contact with other objects. When vehicle is NOT floatingFloating thrust is equal to: floatingThrustFactor ( floatingThrustFactor * mainThrustForce ) , otherwise it is 100% of mainThrustForce. floatSound forwardJetEmitter gyroDrag jetSound mainThrustForce normalForce pitchForce restorativeForce reverseThrustForce rollForce stabDampingConstant Floating sound effect. ParticleEmitterData datablock used for forward thrust jets, attached at JetNozzle0 and JetNozzle1. Gyroscopic force that resists tilting. Jetting sound special effect. Forward thrust factor. A force that attempts to right a tilted vehicle. A force applied forward or backward, depending on the amount of vehicle pitch. Another minor force used to level a tilted vehicle. Reverse thrust factor. A force applied left or right, depending on the amount of vehicle roll. A value used to keep the vehicle from bouncing up-and down with minor elevation changes. A limiting factor applied to the automatically generated stabilizing 'box'. This box grows and shrinks as the velocity of the hover vehicle increase and decreases. This phantom box will collide with objects so limiting it's maximum size is a good idea. A limiting factor applied to the automatically generated stabilizing 'box'. This box grows and shrinks as the velocity of the hover vehicle increase and decreases. If the box becomes too small, the vehicle will become unstable and prone to tilting. AudioProfile datablock ParticleEmitterData datablock [ 0.0 , inf.0 ) AudioProfile datablock [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , 1.0 ] [ 0.0 , 1.0 ] Sample or Range [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.01 , 1 ] ParticleEmitterData datablock ( 0.0 , inf.0 )

dustTrailOffset engineSound floatingGravMag

0.0 5.0 0.0 AudioProfile datablock [ 0.0 , 1.0 ]

stabLenMax

[ 0.0 , inf.0 )

stabLenMin

[ 0.0 , inf.0 )

77

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name stabSpringConstant steeringForce strafeThrustForce triggerTrailHeight turboFactor vertFactor

Description This value should be at least twice the mass of the hoverVehicle and is used to keep the vehicle from sinking to the ground. Controls strength of steering controls. Side to side thrust strength. Height at which dust trail emitter is enabled. A multiplier applied to thrustforce while jetting ($mvTriggerCount3 > 0 ). A multiplier to use to increase or decrease drag in the vertical direction.

Sample or Range [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 )

A.2.31. InteriorInstance
This class is used represent interiors. That is, objects/meshes use to represent buildings bridges and other large objects in the world which can be entered. As the name would imply, interiors have an inside, and interior. Interior instance utilizes a BSP collision algorithm, as well as portals and other features.

Fields
Field Name interiorFile showTerrainInside useGLLighting Description The DIF filename or for this interior. If false, and interior uses portals, terrain will not render inside the interior. If this is set to true, interiors wil have basic GL lighting as soon as they are placed, otherwise a relight will be required to make textures show up. Sample or Range ~/path/filename.dif [ false , true ] [ false , true ]

Globals
Variable Name pref::Interior::LightUpdatePeriod pref::Interior::ShowEnvironmentMaps pref::Interior::DynamicLights pref::Interior::VertexLighting pref::Interior::TexturedFog pref::Interior::lockArrays pref::Interior::detailAdjust pref::Interior::DontRestrictOutside Description Millisecond between dynamic light updates for interiors. Default is 66 milliseconds. Enables environmental mapping for all interiors. Enable dynamic lights for all interiors. Enable vertex lighting for all interiors. Enables advanced fog rendering for all interiors. Advanced rendering feature. LOD transition tolerance. Debug mode. Sample or Range [ 1 , inf ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ 0.3 , 1.0 ] [ false , true ]

78

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Method Summaries


activateLight getNumDetailLevels deactivateLight magicButton echoTriggerableLights setAlarmMode

Console Methods
activateLight( lightName ) Purpose Use the activateLight method to enable a named light in the current interior. Syntax lightName A string containing the name of a light to be activated. Returns No return value. See Also deactivateLight, echoTriggerableLights deactivateLight( lightName ) Purpose Use the deactivateLight method to disable a named light in the current interior. Syntax lightName A string containing the name of a light to be deactivated. Returns No return value. See Also activateLight, echoTriggerableLights echoTriggerableLights() Purpose Use the echoTriggerableLights method to dump a list of all the triggerable lights this interior contains to the console. Returns No return value. Notes This is a nice helper function for those of us who can't always remember our light names. See Also activateLight, deactivateLight

79

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getNumDetailLevels() Purpose Use the getNumDetailLevels method to determine the number of details that the current interior has. Returns Returns an integer value representing the maximum number of detail levels present in this interior. Notes Detail levels start from 0 and increment, where 0 has the most detail, 1 less than 0, 2 less than 1, et cetera. See Also setDetailLevel setAlarmMode( mode ) Purpose Use the setAlarmMode method to set the global alarm mode for this interior. enable or disable all alarm mode lights. This will

Syntax mode A string. If this value is set to On, then all alarm mode lights will be enabled. Any other value disables all alarm mode lights. Returns No return value. setDetailLevel( level ) Purpose Use the setDetailLevel method to force the current interior to render at a specific LOD, if the interior supports that level. Syntax level An integer value greater than or equal to zero, representing a LOD to render this interior at. Returns No return value. Notes Detail levels start from 0 and increment, where 0 has the most detail, 1 less than 0, 2 less than 1, et cetera. See Also getNumDetailLevels

80

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setSkinBase( basename ) Purpose Use the setSkinBase method to switch skins on an interior. Syntax basename Text string equivalent to new texture(s) prefix. Notes Currently not working.

A.2.32. Item
This class, derived from ShapeBase, is used to represent small object in the world which are intended to be interacted with and/or picked up. That is coinscoins, grenades, rifles, gems, etc. This class of object does not require a collision mass although having one is useful in certain circumstances, specifically for line of sight collisions. By default the engine will supply a simple collision mesh for object to object collisions. This object supplies a several physical as well as visible behaviors, including object specific gravity, elasticity, lighting, etc.

Fields
Field Name collidable rotate static Description -If set to a true, this item will auto-rotate. if this value is set to true, the item will not move when placed. Conversely, a non-static shape is allowed to move. Sample or Range deprecated [ false , true ] [ false , true ]

Console Method Summaries


getLastStickyNormal getLastStickyPos isRotating

81

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Methods
getLastStickyNormal() Purpose Use the getLastStickyNormal method to get the normal vector for the last sticky collision. If the item is not set to sticky, that is the datablock field sticky is set to false, this object will not track collisions. Returns Returns a string containing the XYZ values of the normal vector. If no normal vector is available, or if the item is not sticky this method will return the NULL string. Notes To enable sticky collisions, set the ItemData.sticky field to true. See Also getLastStickyPos getLastStickyPos() Purpose Use the getLastStickyPos method to get the position vector for the last sticky collision. If the item is not set to sticky, that is the datablock field sticky is set to false, this object will not track collisions. Returns Returns a string containing the XYZ coordinates of the last collision position. If no normal vector is available, or if the item is not sticky this method will return the null string (""). Notes To enable sticky collisions, set the ItemData.sticky field to true. See Also getLastStickyNormal isRotating() Purpose Use the isRotating method to determine if this item is playing the built-in rotation animation. Returns Returns true if the current item is rotating, that is if the rotate field was set to true when this object was created. Notes To enable rotation, set the rotate field in the item instance to true.

82

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isStatic() Purpose Use the isStatic method to determine if this is a static item. A static item will not change position as the result of the application of outside forces like gravity and impulses. Returns Returns true if the current item is static, that is the static field was set to true when this object was created. Notes To make an item static, set the static field in the item instance to true. A static item can still play animations and auto-rotate. See Also isRotating setCollisionTimeout( obj ) Purpose Use the setCollisionTimeout method to disable collisions between a specific GameBase derived object and this item. The default period for this timeout is 15 ticks (15 x 32ms == 480ms) or roughly one-half second. Syntax obj The name or ID of a GameBase derived object with which to ignore collisions for ~1/2 second. Returns Returns true if the collision timeout was successfully set. Notes To modify the default timeout duration, see sCollisionTimeout in item.cc. It should be noted, that the colliding object is the one that is told to (temporarily) ignore subsequent collisions, not the item.

83

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.33. ItemData
This is the datablock associated with the item object. This class specifies most of the behavior of an item.

Fields
Field Name Pickups pickupName Physics A floating-point value specifying how 'bouncy' this object is. i.e. How much of the shape's velocity is lost onCollision(). elasticity Note: Because of rounding errors between gravity and elasticity calculations, an object with 1.0 elasticity that is bouncing up and down will eventually bounce away. friction A floating-point value specifying how much velocity is lost to impact and sliding friction. A floating-point value specifying an individual variance of local gravity for this item. In other words, an item can be told to ignore gravity, or to be affected by 2x gravity, or even to float upward. A floating-point value specifying a limit on this item's maximum velocity. This can be used to keep the item from falling for flying too fast. It is also helpful to limit the effects of other settings like velocity, etc. A boolean value specifying whether this object will arrest (stop) on impact. Furthermore, if an object is sticky, it retains information about the last location is hit which can be accessed via console methods (see below). An integer value which, if specified, is added to the value returned by getType(). A three-value floating-point vector containing the RGB components of this item's light. A boolean value instructing the item to produce light, only in the case that the object field 'isStatic' is true. A floating point value specifying the radius for this item's light. A floating point value specifying the time (in milliseconds) it takes for a pulsing light to transition on-off-on. A string specifying the type of light, if any, this shape emits. [ true, false ] Suggested: [ 0.0 , 1.0 ) Allowed: [ 0.0 , inf ) Message to be printed when this object is picked up. It is the responsibility of scripts to print this message. -Description Sample or Range

Allowed:( -inf , inf ) Suggested: [ 0.0 , 1.0 ] Allowed:( -inf , inf ) Suggested:( -20.0 , 20.0 )

gravityMod

maxVelocity

sticky Type dynamicType Light lightColor lightOnlyStatic lightRadius lightTime

See dynamicType below

Individually: [ 0.0 , 1.0 ] [ false , true ] [ 0.0 , 20.0 ] ( 100 , inf ) NoLight ConstantLight PulsingLight

lightType

84

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.34. Lightning
This class is used to represent the special effect lightning. Lightning can be generated or rendered from a bitmap. Lightning bolts start at the top of the specified lightning block and terminate somewhere at the bottom or below the specified lightning block.

Fields
Field Name boltStartRadius chanceToHitTarget color Defunct. This field is used to specify the color of lightning when it is created. Note, this is the exact color for generated lightning but should be considered a filter color for lightning that he uses bitmaps This field is used to specify the color of lightning as it fades out. Note, this is the exact color for generated lightning but should be considered a filter color for lightning that he uses bitmaps This value is used to specify the radius in which lightning will strike. This value is used to specify the number of strikes permitted. Note, strikes occur at random but engine guarantees that at least strikes per minute strikes will occur space every minute. This value specifies the width of the bitmap or generated lightning, at the point of its strike. If such true this lightning will be affected by fog. Note, this only applies to lightning generated using bitmaps. Description This field specifies the radius within which lightning bolts will start. Sample or Range ( 0.0 , inf ) [ 0.0 , 1.0 ] "r g b a" (float)

fadeColor strikeRadius strikesPerMinute strikeWidth useFog

"r g b a" (float) ( 0.0 , inf ) [ 0 , inf ] ( 0.0 , inf ) [ false , true ]

Console Method Summaries


strikeObject strikeRandomPoint warningFlashes

Console Methods
strikeObject( obj ) Purpose Use the strikeObject method to cause lightning to strike the object specified object. Syntax obj The name or ID of a GameBase derived object to strike with lightning. Returns No return value. Notes Not working as of this time. See Also strikeRandomPoint

85

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

strikeRandomPoint() Purpose Use the strikeRandomPoint method to generate a random lightning strike. Returns No return value. See Also strikeObject

A.2.35. LightningData
This is the datablock associated with the lightning object, it is used to specify the bitmap textures and the sounds use with the lightning object.

Fields
Field Name strikeSound strikeTexture[0] ... strikeTexture[7] thunderSounds[0] ... thunderSounds[7] Audio profile to play on strike. Texture data to use when using textured lightning. Description Sample or Range AudioProfile datablock ~/path/filename.png

Audio profiles for thunder sounds.

AudioProfile datablock

A.2.36. Marker
This marker is used for building paths used by the PathCamera and other path following objects.

Fields
Field Name msToNext seqNum smoothingType Description Milliseconds to next marker in sequence. Marker position in sequence of markers on this path. Path smoothing at this marker/knot. Linear means no smoothing, while Spline means to smooth. Type of this marker/knot. A normal knot will have a smooth camera translation/rotation effect. Position Only will do the same for translations, leaving rotation un-touched. Lastly, a Kink means the rotation will take effect immediately for an abrupt rotation change. Sample or Range ( 0 , inf ) [ 0 , 40 ) Linear Spline Normal Position Only Kink

type

86

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.37. MissionArea
This object is used represent the outer bounds of the mission area. This is a reactive region in that, a callback will be entered each time an object enters or leaves the mission area. Please see the callbacks quick reference for information on these callbacks.

Fields
Field Name Area Description Four floating point values <Xc Yc Xw YD> representing: - Center of mission area <Xc Yc> (in world). - Size of mission area <Xw Yw> This value is used to specify the elevation at which a flying vehicles power. That is, when a flying vehicle reaches this elevation it will not be able to thrust vertically any longer. This value is used to specify a range below the flight ceiling at which a flying vehicle's to thrust begins to taper off. Sample or Range 0.0 0.0 1024.0 512.0

flightCeiling flightCeilingRange

[ 0.0 , inf.0 ) [ 0.0 , flightCeiling ]

A.2.38. MissionMarker
This class is used to represent generic marker in the game world.

A.2.39. NetConnection
This is the parent object to GameConnection. We do not create instances of net connection, but it is important to note that this class exists and provides several features which are available through the GameConnection class.

Globals
Variable Name pref::Net::PacketRateToClient pref::Net::PacketRateToServer pref::Net::PacketSize Description Limits the packet rate from server to client. Limits the packet rate from client to server. Limits the size of any one packet. Sample or Range [ 1 , 32 ] [ 8 , 32 ] [ 100 , 450 ]

Console Method Summaries


checkMaxRate connectLocal getGhostsActive resolveGhostID clearPaths getAddress getPacketLoss resolveObjectFromGhostIndex connect getGhostID getPing setSimulatedNetParams

87

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Methods
checkMaxRate() Purpose Use the checkMaxRate method to retrieve the current maximum packet rate for this connection. Returns Returns an integer value representing the maximum number of packets that can be transmitted by this connection per transmission period. Notes The period may not neccesarily be one second. To adjust packet rates, see the preference variables above. clearPaths() Purpose Use the clearPaths method to mark this connection as NOT having received any paths. Returns No return value. See Also transmitPaths connect( remoteAddress ) Purpose Use the connect method to request a connection to a remote server at the address remoteAddress. Syntax remoteAddress A string containing an address of the form: A.B.C.D:Port, where A .. B are standard IP numbers between 0 and 255 and Port can be between 1000 and 65536. Returns No return value. See Also connectLocal, getAddress

88

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

connectLocal() Purpose Use the connectLocal method to connect the current client-side connection to a local NetConnection, that is to create an internal connection from this client to the internal server. This is accomplished through the use of a back door mechanism and has an extremely high bandwidth. Returns No return value. See Also connect, getAddress getAddress() Purpose Use the getAddress method to get the address and port that this NetConnection is currently attached to. Returns Returns the address and port that this NetConnection is currently attached to, where the addres will be of the form: A.B.C.D:Port. A .. B are standard IP numbers between 0 and 255 and Port can be between 1000 and 65536. If the connection is local, the string local will be returned. If a this NetConnection is not currently connected the method will return a NULL string. See Also connect, connectLocal getGhostsActive() Purpose Use the getGhostsActive method to determine how many ghosts are active on a particular connection. Returns Returns an integer value between 0 and inf, specifying how many objects are being ghosted to a client on the other side of a specific connection. getPacketLoss() Purpose Use the getPacketLoss method to determine the current packetLoss count for this connection. Returns Returns an integer value between 0 and inf, indicating the number of packets that have been lost to date on this net connection. See Also getPing

89

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getPing() Purpose Use the getPing method to determine the round-trip travel time from this connection to the agent on the other end and back again. Returns Returns an integer value representing the total time in milliseconds it takes for a ping request to travel to the agent on the other end of a connection and back to this agent. See Also getPacketLoss setSimulatedNetParams( packetLoss , delay ) Purpose Use the setSimulatedNetParams method to force a connection to experience a certain degree of packet-loss and/or latency. This is a debug feature to allow us to see how a distributed game will behave in the face of poor connection quality. Syntax packetLoss A floating-point value between 0.0 (0%) and 1.0 (100%) dictating the percentage of packets to be artificially lost. delay An integer value specifying the number of milliseconds to insert into transmission latencies. Returns No return value. See Also getPacketLoss, getPing transmitPaths() Purpose Use the transmitPaths method to send Interior path information to a client on this connection. Returns No return value. Notes Only called on client connections from the server. See Also clearPaths

90

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.40. NetObject
Console Method Summaries
clearScopeToClient getGhostID scopeToClient

Console Methods
clearScopeToClient( client ) Purpose Use the clearScopeToClient method to undo the effects of a previous call to scopeToClient. Syntax client The ID of the client to stop forcing scoping this object for. Returns No return value. See Also scopeToClient scopeToClient( client ) Purpose Use the scopeToClient method to force this object to be SCOPE_ALWAYS on client. Syntax client The ID of the client to force this object to be SCOPE_ALWAYS for. Returns No return value. Notes When an object is SCOPE_ALWAYS it is always ghosted. Therefore, if you have an object that should always be ghosted to a client, use this method. See Also clearScopeToClient, setScopeAlways

91

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setScopeAlways() Purpose Use the setScopeAlways method to force an object to be SCOPE_ALWAYS for all clients. Returns No return value. Notes When an object is SCOPE_ALWAYS it is always ghosted. Therefore, if you have an object that should always be ghosted to all clients, use this method. See Also scopeToClient

A.2.41. ParticleData (PD)


This is the datablock used to represent individual particles. This datablock is subsequently used by particle emitter data to produce particle effects. Torque provides a variety of particle effects. Particles may be animated sequences of textures, a single fixed texture, a colorized or non-colorized texture.

Fields
Field Name animateTexture animTexName[0] ... animTexName[49] colors[0] colors[1] colors[2] colors[3] constantAcceleration dragCoefficient framesPerSec gravityCoefficient inheritedVelFactor lifetimeMS lifetimeVarianceMS sizes[0] sizes[1] sizes[2] sizes[3] spinRandomMax spinRandomMin spinSpeed textureName Use textures beyond Textures used when animating textures. ~/path/filename.png Note: animTexName[0] == textureName Key-framing color controls for particles. "1.0 0.5 0.5 1.0" Description Sample or Range [ false , true ]

Acceleration applied to particle per second over particle lifetime. Drag multiplier (total drag == particle velocity * dragCoefficient). Texture animation frames per second. Gravity multiplier. Velocity multiplier (inherited velocity == emitter velocity * inheritedVelFactor). Time this particle lives in milliseconds. ( Total lifetime == lifetimeMS +/- rand( lifetimeVarianceMS ) ). Random amount by which lifetime varies. Key-framing size controls for particles.

( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf , inf ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf , inf ) ( -inf , inf ) ( -inf.0 , inf.0 )

Maximum degrees for randomized angular spin about billboard's normal. Minimum degrees for randomized angular spin about billboard's normal. Degrees-per-second spin rate about billboard's normal. Texture to use for non-animated particles.

( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ~/path/filename.png

92

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name times[0] times[1] times[2] times[3] useInvAlpha windCoefficient

Description Key-framing time controls for particles.

Sample or Range [ 0.0, times[1] ] [ times[0], times[2] ] [ times[1], times[3] ] [ times[2], 1.0 ] [ false , true ] ( -inf.0 , inf.0 )

Invert alpha components in texture(s). Amount by which wind effects particle velocity.

A.2.42. ParticleEmitterData
Fields
Field Name Description Each particle is given an ejection direction at which to be emitted, and ejectionOffset specifies the distance in this direction from the particle emitter a particle will be emitted at. This field's value must be non-negative, otherwise a console warning is generated and the value is forced to 0.0. The actual ejectionOffset value used is only guaranteed to be within 0.01 of the value used, and values greater than 655 are not possible. Used along with periodVarianceMS to determine the frequency with which to emit particles. See the periodVarianceMS field for more details. Stored with only 10 bits of precision, so values above 1023 are not possible. If value is less than 1, a console warning is generated and the value is forced to 1. Used along with velocityVariance to determine the initial velocity at which a particle is emitted. See the velocityVariance field documentation for more details. This field's value must be non-negative, otherwise a console warning is generated and the value is forced to 0.0. The actual value used is only guaranteed to be within 0.01 of the value specified, and values larger than 655 are not valid. Used with lifetimeVarianceMS to determine the total life-time of each emitted particle. Measured in milliseconds. This field's value must be greater than zero, otherwise a console warning is generated and the value is forced to one. To save network transmission bandwidth, the value of lifetimeMS is shifted-right 5 bits, implying a loss of precision. Thus, the lifetime value used may be up to 31 milliseconds less than the value specified. After being shifted, the value is clamped to 10-bits of precision as well; thus, values of more than about 32000 milliseconds will not behave as expected. Used with lifetimeMS to determine the total life-time of each emitted particle. Measured in milliseconds. Each time a particle is created, a random number between -1*lifetimeVarianceMS and lifetimeVarianceMS is generated and added to lifetimeMS to determine the particle's total lifetime. This field's value must be no greater than that of lifetimeMS, otherwise a console warning is generated and the value is clamped. The lifetimeVarianceMS value is shifted and stored with 10bit precision in the exact manner that lifetimeMS's value is. Only applicable when orientParticles is True. Specifies whether emitted particles will be checked for velocity. If True, and the particle has no velocity, it will not be rendered. If True, and the particle has a velocity, the velocity magnitude will affect the particle's rendered size. False means that the particle's velocity will not affect the rendered size. Specifies whether emitted particles should be oriented as billboards (always face directly towards the camera), or if they should face the emission direction (see the field documentation for thetaMin, thetaMax, phiReferenceVel, and phiVariance for more information on emission direction). True means the particles should be oriented toward the emission direction, False means particles should be oriented as billboards. Sample or Range

ejectionOffset

( -inf.0 , inf.0 )

ejectionPeriodMS

( -inf , inf )

ejectionVelocity

( -inf.0 , inf.0 )

lifetimeMS

( -inf , inf )

lifetimeVarianceMS

( -inf , inf )

orientOnVelocity

[ false , true ]

orientParticles

[ false , true ]

93

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name overrideAdvance

Description Specifies whether a newly created particle's acceleration, color, and other settings should begin being updated immediately after it is created. If false, the new particle is updated immediately. If true, the particle's time advancement system is initially deferred. String containing a tab-delimited list of ParticleData datablock names. When a particle is emitted from the emitter, its datablock is randomly selected from the list of valid datablocks supplied in the particles field string. The string must be non-empty and at least one token must evaluate to a valid ParticleData datablock name. String must also be less than 256 characters long. If either of these conditions is not met, a console warning will be produced and the datablock's onAdd() method will fail. Used along with ejectionPeriodMS to determine the frequency with which to emit particles. Each time a particle is emitted, a random value between -1*periodVarianceMS and periodVarianceMS is generated and added to ejectionPeriodMS to determine the time at which to emit the next particle. So, with the default values ejectionPeriodMS = 100 and periodVarianceMS = 0, particles would be emitted exactly 10 times per second. This field's value is stored with only 10 bits of precision, so values above 1023 are not possible. Value must be less than the value of ejectionPeriodMS, otherwise a console warning is generated and the value is set to ejectionPeriodMS - 1. Each time a particle is emitted, it is given an ejection direction, see the thetaMin field documentation for more information. phiReferenceVel and phiVariance determine the particle's ejection angle about the z-axis. Particles are emitted starting at 0 degrees, and the emission angle is rotated over time, according to the velocity specified in phiReferenceVel, as well as random numbers generated from phiVariance. See the phiVariance field documentation. This field's value is measured in degrees per second and may range from 0.0 to 360.0; if it lies outside this range, a console warning is generated and the value is clamped. Used along with phiReferenceVel to determine the angle relative to the z-axis at which to eject a particle. Each time a particle is generated, a new ejection direction is determined for it. The ejection angle about the z-axis is defined by adding a random variable between 0.0 and phiVariance to the angle determined by phiRefVelocity. See the phiRefVelocity field documentation for more information. This field's value is measured in degrees and must lie in the range of 0.0 to 360.0; if it does not, a console warning is generated and the value is clamped. Each time a particle is emitted, it is given an ejection direction. See the thetaMin field documentation for more information. thetaMax defines the maximum ejection direction angle relative to the x-axis. This field's value is measured in degrees. The value may range from 0.0 to 180.0; if it lies outside this range, a console warning is generated and the value is clamped. Each time a particle is emitted, it is given an ejection direction. This direction is defined by the thetaMin, thetaMax, phiReferenceVel, and phiVariance fields. A random angle between thetaMin and thetaMax is generated and provides the ejection direction angle relative to the x-axis. This field's value is measured in degrees. The value may range from 0.0 to 180.0, and must be less than thetaMax. If one of these conditions is not met, a console warning is generated and the value is clamped accordingly. Specifies whether the particle's datablock colors array field should be over-ridden by the colors array provided by the ParticleEmitter object. True implies that the particle's datablock should be over-ridden. Specifies whether the particle's datablock sizes array field should be over-ridden by the sizes array provided by the ParticleEmitter object. True implies that the particle's datablock should be over-ridden.

Sample or Range [ false , true ]

particles

see type

periodVarianceMS

( -inf , inf )

( -inf.0 , inf.0 )

phiReferenceVel

phiVariance

( -inf.0 , inf.0 )

thetaMax

( -inf.0 , inf.0 )

thetaMin

( -inf.0 , inf.0 )

useEmitterColors

[ false , true ]

useEmitterSizes

[ false , true ]

94

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name

Description Used along with ejectionVelocity to determine the speed at which a particle will be emitted. Each particle gets a new velocity, which is calculated by adding a random number between -1*velocityVariance and velocityVariance to ejectionVelocity. This field's value must be non-negative and must not be greater than ejectionVelocity, otherwise a console warning is generated and the value is clamped appropriately. The actual value used is only guaranteed to be within 0.01 of the value specified, and values larger than 163 are not valid.

Sample or Range

velocityVariance

( -inf.0 , inf.0 )

A.2.43. ParticleEmitterNode
Fields
Field Name emitter velocity Description ParticleEmitterData datablock used by this emitter node Velocity at which to emit particles. Sample or Range ParticleEmitterData datablock [ 0.0 , inf.0 )

A.2.44. ParticleEmitterNodeData
Fields
Field Name timeMultiple Description A multiplier that will affect the behavior or the emitter node and the particles it emits. Sample or Range [ 0.0 , inf.0 )

A.2.45. Path
Fields
Field Name isLooping Description If this is true, the loop is closed, otherwise it is open. Sample or Range [ false , true ]

Console Method Summaries


getPathID

Console Methods
getPathID() Purpose Use the getPathID method to get the path ID (not the object ID) of this path. Returns Returns an integer value representing the path index for this path as stored by the path manager.

95

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.46. PathCamera
This object is a special version of the camera that can be placed on a path. The camera can then be made to travel along this path. This feature can be used for many purposes, including cut-scenes, in-game movies, demos, etc.

Console Method Summaries


popFront reset pushBack setPosition pushFront setState

Console Methods
popFront() Purpose Use the popFront method to remove a knot from the front of the camera's knot queue. Returns No return value. Notes Removes first node in path, does not affect node base or position. See Also pushBack, pushFront pushBack( transform , speed , type , path ) Purpose Use the pushBack method to add a new knot to the back of a path camera's path. Syntax transform speed type path Transform vector for new knot. Speed setting for knot. Knot type. (Normal, Position Only, Kink) Path type. (Linear, Spline)

Returns No return value. See Also pushFront

96

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

pushFront( transform , speed , type , path ) Purpose Use the pushFront method to add a new knot to the front of a path camera's path. Syntax transform speed type path Transform vector for new knot. Speed setting for knot. Knot type. (Normal, Position Only, Kink) Path type. (Linear, Spline)

Returns No return value. See Also pushBack reset( [ speed = 0 ] ) Purpose Use the reset method to move the camera back to the beginning of the path and optionally give it a new travel speed. Syntax speed A positive floating-point value corresponding to the rate of travel for the camera. Returns No return value. See Also setState setPosition( pos ) Purpose Use the setPosition method to set the position of the camera on a path as a percentage. Syntax pos A floating point value equal to the position on the current camera path. Between 0.0 and 1.0. Returns No return value. See Also setState, setTarget

97

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setState( state ) Purpose Use the setState method to set the camera's current movement state. Syntax state A string forward backward stop Returns No return value. See Also reset, setPosition setTarget( pos ) Purpose Use the setTarget method to set the next position target for this path camera. A path camera will travel along the path until it reaches the current target. This target is set as a percentage of the entire path. Syntax pos A floating point value represent the next target on this path camera's path. Between 0.0 and 1.0. Returns No return value. Notes Setting a target position behind the current path camera position will not make the camera travel in reverse unless the moving state is backward. See Also setPosition, setState containing either: moving from front of path to back of path. moving from back of path to front of path. - not moving.

A.2.47. PhysicalZone
Fields
Field Name appliedForce gravityMod polyhedron velocityMod Description Three-element floating point value representing forces in three axes to apply to objects entering pzone. Gravity in pzone. Multiplies against standard gravity. Floating point values describing outer bounds of pzone. Multiply velocity of objects entering zone by this value every tick. Sample or Range x y z Each value in range: [ -40000 , 40000 ] ( -inf.0 , inf.0 ) [ -40000 , 40000 ]

98

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Method Summaries


activate deactivate

Console Methods
activate() Purpose Use the activate method to enable this physicalZone. physicalZone attributes will be in effect. Returns No return value. See Also deactivate deactivate() Purpose Use the deactivate method to disable this physicalZone. physicalZone attributes will be in effect. Returns No return value. See Also activate Once disabled, none of the Once enabled, all of the

99

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.48. Player
Console Method Summaries
checkDismountPoint getDamageLocation clearControlObject getState getControlObject setActionThread

Console Methods
checkDismountPoint( oldPos , pos ) Purpose Use the checkDismountPoint method to see if dismounting a player from one position to another will cause a collision, or a cohabitation of space. Syntax oldPos Original position of player. pos Intended position of player. Returns Returns true if the new position does not result in any collisions, false otherwise. Notes This can be used for a variety of things besides dismounting checks. Basically, any time you want to check if a player can be moved into a new position, you may use this. clearControlObject() Purpose Use the clearControlObject method to undo the results of a call to player.setControlObject, re-establishing this player as the control object. Returns No return value. Notes It is possible to temporarily make another player receive move commands that would normally be sent to this player, while leaving this player in place. Unless moved, the camera will stay mounted to this player, while the other player receives movement inputs. See Also getControllingObject, setControlObject Description

100

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getControllingObject() Purpose Use the getControllingObject method to determine if this player object is receiving input commands from another player. Returns Returns 0 if this object is not being controlled by another player object, or it will return a non-zero positive integer specifying the ID of the player object that is controlling this player. Notes It is possible to temporarily make another player receive move commands that would normally be sent to this player, while leaving this player in place. Unless moved, the camera will stay mounted to this player, while the other player receives movement inputs. See Also clearControlObject, setControlObject Description Returns float getDamageLocation( damagePosition ) Purpose Use the getDamageLocation method to get the named damage location and modifier for a given damagePosition. The player object can differentiate 'hit' locations based on a predefined set of datablock settings. You may modify these settings, to vary the percentage of area given over to any body region. Syntax damagePosition A position for which to retrieve a body region on this player. Returns Returns a string containing two words (space separated strings), where the first is a location and the second is a modifier: Posible locations - legs - torso - head Head modifiers - front_left - front_right - back_left - back_right

101

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Legs/Torso modifiers - middle_back - right_back - left_middle - middle_middle - right_middle - left_front - middle_front - right_front Notes Positions do not need to be on or even near the player. The engine will calculate which body location and modifier best fit any position relative to the player. getState() Purpose Use the getState method to get the current state of this player. Returns Returns a string specifying the current state of the player: Possible states Dead Damage >= disabledLevel Mounted Mounted to another object. Move - Alive and able to move. Recover Recovering from a long fall or hard impact (unable to move). Notes The most important fact to note is that your player is considered dead at the disabledLevel, not the destroyedLevel. setActionThread( string sequenceName , hold , fps ) Purpose Use the setActionThread method to play a specific animation. This is similar to the playThread method that comes with all ShapeBase objects, but it is designed for full body animations and comes with a few different options. Optionally, a player may be told to play a thread and then to hold at the last frame of that animation until another overrides it. Syntax sequenceName A string containing the name of the animation to play. Returns No return value.

102

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setControlObject( obj ) Purpose Use the setControlObject method to have this player object 'control' another player object. Doing so will re-direct movement inputs from this player object to the controlled player object. This can be used when you want one player to act as a mount for another player. Syntax obj The ID of the surrogate player that should now receive movement inputs previously destined for this player. Returns Returns true on success, and false on failure. Notes It is possible to temporarily make another player receive move commands that would normally be sent to this player, while leaving this player in place. Unless moved, the camera will stay mounted to this player, while the other player receives movement inputs. See Also clearControlObject, getControllingObject

A.2.49. PlayerData
Fields
Field Name Rendering renderFirstPerson Pickups pickupRadius Looking maxFreelookAngle maxLookAngle minLookAngle Time Scaling maxTimeScale Step Height maxStepHeight Forces and Factors horizMaxSpeed horizResistFactor horizResistSpeed jumpDelay jumpEnergyDrain jumpForce Maximum horizontal velocity on ground, in air, or in water. Delta factor used to determine how much of 'horizResistspeed' is removed from current velocity. Velocity at which horizontal resistance kicks in. Forced delay between jumps (in ticks). Drain this many energy points for every jump. Force applied to player on jump. Should be less than 40000 * mass. ------Maximum height player will be able to step up. Heights larger than this will stop a walking player. Limits animation scaling. Animations will scale up to this amount when matching ground velocity. [ 0.0 , inf.0 ) -[ 0.0 , inf.0 ) Maximum free-look angle in radians. Maximum look angle in radians. Minimum look angle in radians. [ -3.14159 , 3.14159 ] [ -3.14159 , 3.14159 ] [ -3.14159 , 3.14159 ] Can be used to increase the size of the player box for item collisions ONLY. [ 0.0 , inf.0 ) If true, render the player mesh while in 1st POV. [ false , true ] Description Sample or Range

103

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name jumpSurfaceAngle maxJumpSpeed minJumpEnergy minRunEnergy recoverDelay recoverRunForceScale runEnergyDrain runForce runSurfaceAngle upMaxSpeed upResistFactor upResistSpeed Velocity maxBackwardSpeed maxForwardSpeed maxSideSpeed maxUnderwaterBackwardSpeed maxUnderwaterForwardSpeed maxUnderwaterSideSpeed Impacts groundImpactMinSpeed groundImpactShakeAmp groundImpactShakeDuration groundImpactShakeFalloff groundImpactShakeFreq minImpactSpeed

Description Cannot jump if surface angle equal to or greater to this many degrees. Cannot jump if running faster than this. Cannot jump if energy lower than this. Cannot run if energy lower than this. Number of ticks that player stays in 'recovery mode' after hard impact. Run force is multiplied by this amount during 'recovery'. Drain this much energy per tick while running. Accelerate player by this much per tick as a result of a move (command). Should be less than 40000 * mass. Cannot accelerate if surface angle equal to or greater to this many degrees. Maximum velocity allowed in the positive Z direction. Delta factor used to determine how much of 'upResistSpeed' is removed from current velocity. Velocity at which vertical resistance kicks in. Maximum backward velocity in m/s. Maximum forward velocity in m/s. Maximum sideway velocity in m/s. Maximum underwater backward velocity in m/s. Maximum underwater forward velocity in m/s. Maximum underwater sideway velocity in m/s. The velocity at which a collision with the ground is registered as an impact. Camera shake amplitude when a ground impact is registered. Camera shake duration when a ground impact is registered. a multiplier determining at what rate the shaking from an impact is reduced over time. Camera shake frequency when a ground impact is registered. The velocity at which a collision with the any object is registered as an impact.

Sample or Range ----[ 0 , inf ] [ 0.0 , 1.0 ] ------------[ 0.0 , inf.0 ) 1.0 1.0 10.0 [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) 4.0 4.0 4.0 [ 0.0 , inf.0 )

Hit Boxes boundingBox A vector containing the radius values for each dimension of the unscaled player's bounding box. (Will be multiplied by scale.) In conjunction with boxHeadBackPercentage, used to determine whether the front, middle, or back side of the player's torso or legs was hit. Note: this functionality is difficult to properly change; it is recommended that default values be used. In conjunction with boxHeadBackPercentage, used to determine whether the front, middle, or back side of the player's torso or legs was hit. Note: this functionality is difficult to properly change; it is recommended that default values be used. In conjunction with boxHeadRightPercentage, used to determine whether the left, middle, or right side of the player's torso or legs was hit. Note: this functionality is difficult to properly change; it is recommended that default values be used 1.6 1.6 2.3

boxHeadBackPercentage

boxHeadFrontPercentage

boxHeadLeftPercentage

104

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name boxHeadPercentage

Description The distance from the bottom of the player's bounding box, to the beginning of the area considered to bound the player's head. Measured as a raw percentage. In conjunction with boxHeadRightPercentage, used to determine whether the left, middle, or right side of the player's torso or legs was hit. Note: this functionality is difficult to properly change; it is recommended that default values be used Like boxHeadPercentage, but specifies where the torso area begins. DecalData datablock used for footprints. The offset from the player's center by which the decal should be displayed; should correlate with the distance from player's center, to the player's right foot center. -The ParticleEmitterData datablock will be used to emit particles at footstep locations. Number of dust particles to be generated for foot puffs. The radius of dust spread from foot steps. The AudioProfile used to produce sounds when emerging from water. The minimum velocity at which the exit splash sound will be played when emerging from water. The AudioProfile used to produce sounds for bubbles produced by footsteps. The AudioProfile used to produce sounds for hard footsteps. The AudioProfile used to produce sounds for soft footsteps. The AudioProfile used to produce sounds for shallow water footsteps. The AudioProfile used to produce sounds for soft footsteps. AudioProfileThe AudioProfile used to produce sounds for underwater footsteps. The AudioProfile used to produce sounds for wading footsteps. The minimum velocity at which the hard splash sound will be played when entering or leaving water. The AudioProfile used to produce sounds for hard impacts. The AudioProfile used to produce sounds for metal impacts. The AudioProfile used to produce sounds for snow impacts. The AudioProfile used to produce sounds for soft impacts. The AudioProfile used to produce sounds for soft impacts against water. The minimum velocity at which the hard water impact sound will be played when entering or leaving water. The AudioProfile used to produce sounds for medium impacts against water. The minimum velocity at which the medium water impact sound will be played when entering or leaving water. The AudioProfile used to produce sounds for underwater bubble while the player is moving. The AudioProfile used to produce sounds for underwater breathing. The length of time to continue emitting bubbles.

Sample or Range [ 0.0 , 1.0 ]

boxHeadRightPercentage

boxTorsoPercentage Footprints and -puffs decalData decalOffset dustEmitter footPuffEmitter footPuffNumParts footPuffRadius Sounds and Modifiers exitingWater exitSplashSoundVelocity FootBubblesSound FootHardSound FootMetalSound FootShallowSound FootSoftSound FootUnderWater Footwading hardSplashSoundVelocity impactHardSound impactMetal impactSnowSound impactSoftSound impactWaterEasy impactWaterHard impactWaterMedium mediumSplashSoundVelocity movingBubblesSound waterBreathSound Splashes and Bubbles bubbleEmitTime

[ 0.0 , 1.0 ]

Not Used

[ 0 , inf ) [ 0.0 , inf.0 ) ----------------------

105

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name footstepSplashHeight splash splashAngle splashEmitter[0] splashEmitter[1] splashFreqMod splashVelEpsilon splashVelocity

Description The maximum height at which to play the shallow footstep effect. The SplashData datablock used to emit splashes. The mimimum verticle angle at which a player must be traveling in order to generate a splash effect. Array of pointers to ParticleEmitterData datablocks which will be used to generate splash effect particles. The simulated frequency modulation of a splash generated by this player. Multiplied along with player speed and time elapsed when determining splash emission rate. The threshold speed at which we consider the player's movement to have stopped when updating splash effects. The minimum velocity a player needs in order to generate a splash effect.

Sample or Range -----

----

A.2.50. Precipitation
Fields
Field Name boxHeight boxWidth doCollision maxMass maxSpeed maxTurbulence minMass minSpeed numDrops rotateWithCamVel turbulenceSpeed useTurbulence Description Height of box (around camera) where precipitation occurs. Width of box (around camera) where precipitation occurs. Allow precipitation to collide with objects vs. fall through. Maximum mass per drop. Maximum speed per drop. Max turbulence to apply per drop. Minimum mass per drop. Minimum speed per drop. Approximate number of drops allowed to exist at any one time. Drops rotate to face camera. Velocity (of drops) at which turbulence kicks in. Enable turbulence effect. Sample or Range ( 0.0 , inf ) ( 0.0 , inf ) [ false , true ] [ minMass , inf ) [ minSpeed , inf ) [ 0.0 , inf ) [ 0.0 , maxMass ] [ 0.0 , maxSpeed ] ( 0 , inf ) [ false , true ] [ 0.0 , inf ) [ false , true ]

Console Method Summaries


modifyStorm setPercentage

106

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Methods
modifyStorm( percentage , time ) Purpose Use the modifyStorm method to adjust the percentage of raindrops falling over time period. Syntax percentage Percent of raindrops to render. [ 0.0 , 1.0 ] time Period of transition in seconds.( 0.0 , inf ) Returns No return value. See Also setPercentage setPercentage( percentage ) Purpose Use the setPercentage method to immediately adjust the percentage of raindrops falling. Syntax percentage Percent of raindrops to render. [ 0.0 , 1.0 ] Returns No return value. See Also modifyStorm

A.2.51. PrecipitationData
Fields
Field Name dropSize dropTexture soundProfile splashMS splashSize splashTexture useTrueBillboards Render size for drops. Texture file (4 x 4 bitmap array) to use for drops. Looping 2D audio profile to play with precipitation. Life of splashes in milliseconds. Size of splashes. Texture to use for splashes (4 x 4 bitmap array). Drops behave like true (non axis-aligned) billboards. Description Sample or Range ( 0.0 , inf ) texture Path Audio Profile Name ( 0 , inf ) ( 0.0 , inf ) texture path [ false , true ]

107

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.53. Projectile
Fields
Field Name initialPosition initialVelocity sourceObject sourceSlot Starting velocity of projectile. description description Description Position that projectile starts at. Sample or Range "1.0 2.0 3.0" "1.0 2.0 3.0" ( -inf , inf ) ( -inf , inf )

A.2.53. ProjectileData
Fields
Field Name armingDelay Description The number of ticks that must pass after the Projectile is created before it can explode. Valid range is 0 to Projectile::MaxLivingTicks (4095 by default). Used to simulate the Projectile's bounce elasticity, if it collides with something but does not explode. The bounce elasticity scales the velocity from a bounce, after friction is taken into account. On bounce, reduce projectile velocity by this factor and a multiple of the tangent to impact. Array of pointers to DecalData datablocks. A non-NULL decal object will be randomly chosen from the array when the Projectile collides with the terrain or an interior. ExplosionData datablock used when the Projectile object blows up out of water. The number of ticks that must pass after the Projectile is created before it will begin becoming transparent. Projectile's opacity will follow a linear degression starting fadeDelay number of ticks after it is creating and ending at lifetime number of ticks. Valid range is 0 to Projectile::MaxLivingTicks (4095 by default). If isBallistic is true, scales the effect of gravity on the Projectile. Valid range is 0.0 to 1.0. Specifies whether the Projectile object sheds light when not in water. If so, a point light object is used with the radius and color specified in the lightRadius and lightColor fields. Specifies whether the Projectile object sheds light when in water. If so, a point light object is used with the radius and color specified in the lightRadius and waterLightColor fields. Specifies whether the Projectile will be affected by gravity, and whether it can bounce before exploding. The number of ticks the Projectile should survive for. Also used along with fadeDelay to determine the transparency of the Projectile object at a given time. See the fadeDelay field documentation for more information. The valid range for lifetime values is 0 to Projectile::MaxLivingTicks (4095 by default). The projectile's point light color for use when not in water. The projectile's point light radius. Valid range is 1.0 to 20.0 Sample or Range ( -inf , inf )

bounceElasticity bounceFriction decals[0] ... decals[5] explosion

[ 0.0 , 0.999 ] ( -inf.0 , inf.0 )

DecalData datablock see type

fadeDelay

( -inf , inf )

gravityMod

( -inf.0 , inf.0 )

hasLight

[ false , true ]

hasWaterLight isBallistic

[ false , true ] [ false , true ]

lifetime

( -inf , inf )

lightColor lightRadius

"1.0 0.5 0.5" ( -inf.0 , inf.0 )

108

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name muzzleVelocity particleEmitter particleWaterEmitter projectileShapeName sound Splash

Description

Sample or Range ( -inf.0 , inf.0 ) see type see type ~/path/filename.dts see type see type

Note: this field currently has no tangible effect in the engine simulation itself, but it is useful in script, and its valid range is checked by the engine. Valid range is from 0.0 to 10,000.0
ParticleEmitter datablock used to generate particles for the projectile when it is out of water, and when entering or leaving water. ParticleEmitter datablock used to generate particles for the projectile when it is under water, and when it is entering or leaving water. The name of the shape file for the Projectile object. Must adhere to the semantics associated with the Filename datatype as defined in the engine. AudioProfile used to generate the Projectile object's sound. SplashData datablock used in the generation of splash effects when the Projectile is entering or leaving water.

velInheritFactor waterExplosion waterLightColor

Note: this field currently has no tangible effect in the engine simulation itself, but it is useful in script, and its valid range is checked by the engine. Valid range is from 0.0 to 1.0.
ExplosionData datablock used when the Projectile object blows up in the water. The projectile's point light color for use when in water.

( -inf.0 , inf.0 ) see type "1.0 0.5 0.5"

A.2.54. SceneObject
Console Method Summaries
getForwardVector getScale getWorldBoxCenter getObjectBox getTransform setScale getPosition getWorldBox setTransform

Console Methods
getForwardVector() Purpose Use the getForwardVector method to get the three-element floating-point vector representing the direction the object is facing. Returns Returns a three-element floating-point vector representing the direction the object is facing. Notes Forward for all SceneObjects is positive-Y. SceneObject's positive-Y axis is pointing. So, this vector represents the direction the

109

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getObjectBox() Purpose Use the getObjectBox method to get the six-element floating-point vector containing two three-space points representing the unscaled and unrotated bounding box for this object. Returns Returns a six-element floating-point vector containing two three-space points representing the bounds of this box: "minX minY minZ maxX maxY maxZ" Notes This box is unscaled and represents the bounding box of the exported, pre-scaled object. See Also getWorldBox getPosition() Purpose Use the getPosition method to get the current position of this object. Returns A three-element vector containing the XYZ world position of this SceneObject. See Also getTransform, setTransform getScale() Purpose Use the getScale method to get the scale of this SceneObject. Returns Returns a three-element vector containing the XYZ scale of this SceneObject. See Also setScale

110

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getTransform() Purpose Use the getTransform method to get the transform matrix for this SceneObject. Returns Returns a seven-element matrix/vector containing the following information: PosX PosY PoxZ RotX RotY RotZ theta , where theta is a rotation about the axis formed by RotX RotY RotZ . Notes Use the getWord(), getWords(), and setWord() string functions for parsing the transform vector. See Also setTransform getWorldBox() Purpose Use the getWorldBox method to get the six-element floating-point vector containing two three-space points representing the scaled and unrotated bounding box for this object. Returns Returns a six-element floating-point vector containing two three-space points representing the bounds of this box: "minX minY minZ maxX maxY maxZ" Notes This box is scaled such that it will contain all points on the current object, regardless of scaling and rotation. Thus, as an irregularly shaped object rotates, its world box will change. See Also getObjectBox getWorldBoxCenter() Purpose Use the getWorldBoxCenter method to get the centroid of this objects world box. Returns Returns a three-element position vector representing the centroid of this objects's world box. See Also getWorldBox

111

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setScale( scale ) Purpose Use the setScale method to set the XYZ scaling factor for this object. Syntax scale An XYZ vector containing the new scaling factor for this object. Returns No return value. See Also getScale setTransform( transform ) Purpose Use the setTransform method to apply a new transform to this object. Syntax transform A seven-element matrix/vector containing the following information: PosX PosY PoxZ RotX RotY RotZ theta , where theta is a rotation about the axis formed by RotX RotY RotZ . Returns No return value. Notes This will both translate and rotate the object. See Also getTransform

A.2.55. ScriptGroup
Fields
Field Name class superClass Description A new namespace to place after the object's name and before ScriptObject in the namespace chain. A new namespace to place after class and before ScriptObject in the namespace chain. Sample or Range Rectangle Polyhedron

112

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.56. ScriptObject
Fields
Field Name class superClass Description A new namespace to place after the object's name and before ScriptObject in the namespace chain. A new namespace to place after class and before ScriptObject in the namespace chain. Sample or Range Rectangle Polyhedron

A.2.57. ShapeBase
Globals
Variable Name SB::DFDec SB::WODec pref::environmentMaps Description Damage flash reduced by this amount per tick. Whiteout reduced by this amount per tick. Enables environmental mapping for all shapes. Sample or Range ( 0.0, 1.0 ] ( 0.0, 1.0 ] [ false , true ]

Console Functions
setShadowDetailLevel()

setShadowDetailLevel( level ) Purpose Use the setShadowDetailLevel function to modify the detail level of dynamically cast shadows. Syntax level A floating-point value between 0.0 and 1.0, where 0.0 is low-quality and 1.0 is high quality. Returns No return value.

113

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Method Summaries


applyDamage canCloak getControllingClient getDamageLevel getEnergyLevel getEyeTransform getImageLoaded getImageTrigger getMountedObjectCount getMountSlot getObjectMount getRepairRate getSlotTransform isCloaked isEnabled isImageMounted mountObject playThread setDamageFlash setDamageVector setImageAmmo setInvincibleMode setShapeName setVelocity stopAudio applyImpulse getAIRepairPoint getControllingObject getDamagePercent getEnergyPercent getEyeVector getImageSkinTag getMountedImage getMountedObjectNode getMuzzlePoint getPendingImage getShapeName getVelocity isDestroyed isHidden isMounted pauseThread setCameraFov setDamageLevel setEnergyLevel setImageLoaded setRechargeRate setSkinName setWhiteOut stopThread applyRepair getCameraFov getDamageFlash getDamageState getEyePoint getImageAmmo getImageState getMountedObject getMountNodeObject getMuzzleVector getRechargeRate getSkinName getWhiteOut isDisabled isImageFiring mountImage playAudio setCloaked setDamageState setHidden setImageTrigger setRepairRate setThreadDir startFade unmount

Console Methods
Cloaking
canCloak isCloaked setCloaked

canCloak() Purpose Use the canCloak method to determine if this shape is able to cloak. Returns Returns true if this shape is allowed to cloak, false otherwise. See Also isCloaked, setCloaked

114

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isCloaked() Purpose Use the isCloaked method to determine if this shape is currently cloaked. Returns Returns true if the shape is currently cloaked. See Also canCloak, setCloaked setCloaked( isCloaked ) Purpose Use the setCloaked method to cloak or uncloak the current shape. Syntax isCloaked A boolean value. will be uncloaked. Returns No return value. See Also canCloak, isCloaked If set to true, this shape will be cloaked, otherwise it

Hiding and Fading


isHidden setHidden startFade

isHidden() Purpose Use the isHidden method to see if this shape is currently hidden. Returns Returns true if the object is hidden, false otherwise. See Also setHidden Returns ID of this object's datablock. setHidden( isHidden ) Purpose Use the setHidden method to hide or unhide this shape. Syntax isHidden A boolean value. will be un-hidden. Returns No return value. If set to true, this shape will be hidden, otherwise it

115

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Notes When an object is hidden it is temporarily removed from the scene and therefore will not render or be collided with. See Also isHidden startFade( time , delay , fadeOut ) Purpose Use the startFade method to fade this shape in and out of view without removing it from the scene. Syntax time specifies the timeit takes (in milliseconds) for the fade to complete. delay specifes the time to wait (in milliseconds) before starting to fade. fadeOut If true, shape fades out, else shape fades in. Returns No return value. Notes Items have the ability to light their surroundings. When an Item with an active light is fading out, the light it emits is correspondingly reduced until it goes out. Likewise, when the item fades in, the light is turned-up till it reaches it's normal brightness. A faded out object is still in the scene and can still be collided with, so if you want to disable collisions for this shape after it fades out use setHidden to temporarily remove this shape from the scene. See Also setHidden

Re-Skinning
getSkinName setSkinName

getSkinName() Purpose Use the getSkinName method to determine which skin this shape is currently using. Returns Returns a string containing the name of the skin this shape is using, or if this shape is not set up to use multiple skins, it returns the NULL string. Notes Shapes must be skinned with specially named set of textures in order to enable reskinning. Please see GPGT volume 1 for more details. See Also setSkinName

116

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setSkinName( skinName ) Purpose Use the setSkinName method to change the current skin for this shape. Syntax skinName A string containing a valid skin name, as set up in the shapes set of skins. If this skin name does not match any of the textures used by this object, the object will not change skins. Returns No return value. Notes Shapes must be skinned with specially named set of textures in order to enable reskinning. Please see GPGT volume 1 for more details. See Also getSkinName

Damage
applyDamage getDamagePercent isDestroyed setInvincibleMode applyRepair getDamageState setDamageLevel setRepairRate getAIRepairPoint isEnabled setDamageState getDamageLevel isDisabled setDamageVector

applyDamage( damage ) Purpose Use the applyDamage method to apply the specified amount of damage to this shape. Syntax damage A floating-point value specifying a positive amount of damage to apply to this shape. Returns No return value. See Also applyRepair, getDamageLevel

117

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

applyRepair( repair ) Purpose Use the applyRepair method to apply repair amount or repair to shape. Syntax repair A positive number of repair points to apply to this shape. Returns No return value. Notes Before this will work, the self-repair must at least temporarily be disabled, by calling setRepairRate with an argument of 0. See Also applyDamage, getDamageLevel, setRepairRate getAIRepairPoint() Purpose Use the getAIRepairPoint method to get the the position of a specially named mesh node: "AIRepairNode". It is used as a helper when deciding where an AI should 'stand' to repair this shape. Returns Returns a three-element floating-point vector containing the position of a specially named mesh node: "AIRepairNode". It is used as a helper when deciding where an AI should 'stand' to repair this shape. Notes If this node is not present in the shape, the position of the shape's centroid will be returned instead. getDamageLevel() Purpose Use the getDamageLevel method to determine the current level of damage this shape has sustained. Returns Returns a floating point value between 0.0 and maxDamage (as specified in the shape's datablock). 0.0 is equal to 'no damage'. See Also applyDamage, applyRepair, getDamagePercent, getDamageState

118

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getDamagePercent() Purpose Use the getDamagePercent method to get a relative percentage of damage for this shape, where the equation for this percent is currentDamage / maxDamage. Returns Returns a floating point value between 0.0 (0%) and 1.0 (100%). See Also applyDamage, applyRepair, getDamageLevel, getDamageState getDamageState() Purpose Use the getDamageState method to determine this shape's current damage state. Returns Returns a string specifying the current damage state for this shape: Enabled Damage < Disabled Level Disabled Disabled Level <= Damage < Destroyed Level Destroyed DestroyedLevel <= Damage See Also applyDamage, applyRepair, getDamageLevel, getDamagePercent isEnabled() Purpose Use the isEnabled method to determine if this shape's damage is less than its disabled level. Returns Returns true as long as this shape's damage level is less than its disabled level. See Also getDamageLevel, getDamagePercent, getDamageState, isDisabled isDisabled() Purpose Use the isDisabled method to determine if this shape's damage is greater than or equal to its disabled level. Returns Returns true as long as this shape's damage level is greater than or equal to its disabled level. See Also getDamageLevel, getDamagePercent, getDamageState, isEnabled

119

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isDestroyed() Purpose Use the isDestroyed method to determine if this shape's damage is greater than or equal to its destroyed level. Returns Returns true as long as this shape's damage level is greater than or equal to its destroyed level. See Also getDamageLevel, getDamagePercent, getDamageState, isEnabled, isDisabled setDamageLevel( level ) Purpose Use the setDamageLevel method to set the shape's damage to a new level. Syntax level A floating-point value between 0.0 and inf.0, representing this shape's new damage level. Returns No return value. Notes If level is greater than this shape's maxDamage, the damage level will be capped at maxDamage. Setting this value will also update the shape's damage state. See Also getDamageLevel, getDamagePercent, getDamageState setDamageState( state ) Purpose Use the setDamageState method to change this shape's damage state. Syntax state A string containing one of the allowed damage states: enabled, disabled, or destroyed. Returns Returns true on success, false otherwise. Notes Setting this value will NOT affect the shape's damage level, furthermore the next time this shape's damage level is updated, the shape's damage state will change to match it. See Also setDamageLevel

120

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setDamageVector( damageOrigin ) Purpose Use the setDamageVector method to establish the direction from which to be applied damage will be coming. This will subsequently be used to correctly setup any explosion or debris scatter that occcurs as a result of the damage. Syntax damageOrigin An XYZ vector specifying the direction from which some to be applied damage originated. Returns No return value. Notes Generally, this should be done before applying damage, but it can be done afterward. However results may vary. See Also applyDamage setInvincibleMode( time , speed ) Purpose Use the setInvincibleMode method to temporarily make this shape invincible. i.e. Not able to take damage. While the player is invincible, the screen will flicker blue with a varying rate and a varying intensity. Syntax time A floating-point value specifying the time in seconds for this shape to remain invincible. speed A floating-point value between 0.0 and 1.0 controlling the rate at which the blue flickering effect occurs. Returns No return value. Notes The flickering effect is used to indicated to a player that his (or her) avatar is invincible. Furthermore, this flicker rate will change and the flicker will become increasingly translucent as the time elapses. If speed set to 1.0, the flickering is a bit obscene. Generally, lower values are nicer.

121

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setRepairRate( rate ) Purpose Use the setRepairRate method to allow this shape to auto-repair. Syntax rate A floating-point value specifying the number of points to heal per tick, where a tick is by default 1/32 of a second. Returns No return value. Notes If you wish to manually repair a shape using applyRepair, you will temporarily have to disable auto-repair by calling this method with an argument of 0. See Also applyRepair

Damage Flashes & Whiteouts


getDamageFlash getWhiteOut setDamageFlash setWhiteOut

getDamageFlash() Purpose Use the getDamageFlash method to get the current level of damage flash occuring, if any. Returns Returns a floating-point value between 0.0 and 1.0 specifying the current level of damage flash occuring. Notes A damage flash is a translucent red overlay that can be used to inform the player that their avatar has taken damage. When a damage flash is applied, it will fade slowly over time. This method merely lets us know how far along that fade has progressed. See Also setDamageFlash

122

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getWhiteOut( ) Purpose Use the getWhiteOut method to get the current level of whiteout occurring, if any. Returns Returns a floating-point value between 0.0 and 1.0 specifying the current level of whiteout occurring. Notes A whiteout is a translucent white overlay that can be used to temporarily blind the player. Usually this is done as a response to the player viewing a bright light of some sort, but it must be done manually if we want this effect. See Also setWhiteOut setDamageFlash( level ) Purpose Use the setDamageFlash method to set the current damage flash level. When this level is > 0.0 the screen is rendered with an an additive red overlay, which is limited to 76% regardless of level. The value of level is auto-decremented over a few seconds. Syntax level A floating-point value specifying the level of damage flash to apply. This value can be between 0.0 (0%) and 1.0 (100%). Returns No return value. Notes A damage flash is a translucent red overlay that can be used to inform the player that their avatar has taken damage. See Also getDamageFlash

123

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setWhiteOut( level ) Purpose Use the setWhiteOut method to set the current whiteout level. When this level is > 0.0 the screen is rendered with an additive white overlay. At 1.0, rendering is saturated and the screen is entirely white. i.e. it is whited out. The value of level is autodecremented over a few seconds. Syntax level A floating-point value specifying the level of whiteout to apply. This value can be between 0.0 (0%) and 1.0 (100%). Returns No return value. Notes A whiteout is a translucent white overlay that can be used to temporarily blind the player. See Also getWhiteOut

Energy
getRechargeRate setEnergyLevel getEnergyLevel getEnergyPercent setRechargeRate

getRechargeRate() Purpose Use the getRechargeRate method to get the current rechargeRate setting for this shape. Returns Returns a floating-point value between 0.0 and inf.0, equivalent to the number of energy points this shape will auto-recharge every tick, where a tick is by default 1/32 of a second. See Also setRechargeRate getEnergyLevel() Purpose Use the getEnergyLevel method to return the current energy level of this shape. Returns Returns a floating-point value between 0.0 and maxEnergy. See Also getEnergyPercent, setEnergyPercent

124

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getEnergyPercent() Purpose Use the getEnergyPercent method to return the current energy level as a percentage. Returns Returns a floating-point value between 0.0 (0%) and 1.0 (100%), where this percentage is calculated as: currentEnergy / maxEnergy. See Also getEnergyPercent, setEnergyLevel setRechargeRate( rate ) Purpose Use the setRechargeRate method to enable auto-recharging at rate points per tick. Syntax rate A floating-point value between 0.0 and inf.0 equivalent to the number of energy points to recharge this shape by each tick, where a tick is by default 1/32 of a second. Returns No return value. Notes Having auto-rechage enabled does not prevent manual application of recharge values, as is the case with auto-repair and manual repairs. See Also getRechargeRate setEnergyLevel( level ) Purpose Use the setEnergyLevel method to set the shape's current energy level to a value between 0.0 and maxEnergy. Syntax level A floating-point value between 0.0 and maxEnergy. Returns No return value. Notes If level is greater than maxEnergy, maxEnergy is used instead. See Also getEnergyLevel

125

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Eye Transform and Vectors


getEyePoint getEyeTransform getEyeVector

getEyePoint() Purpose Use the getEyePoint method to get the positions of this shape's 'eye' node. Returns If this shape has a node in it's mesh named 'eye', the position of that node will be returned as a three-element floating-point vector. If no 'eye' node is present in this shape's mesh, the position of the shape's centroid is returned instead. See Also getEyeTransform, getEyeVector getEyeTransform() Purpose Use the getEyeTransform method to get the transform of this shape's 'eye' node. Returns Returns a transform, "posX posY posZ rotX rotY rotZ rotTheta", representative of a shape's eye postion, orientation vector, and the rotation about that vector in radians. If not 'eye' node is present in this shape's mesh, the transform of the shape itself is returned. See Also getEyePoint, getEyeVector getEyeVector() Purpose Use the getEyeVector method to retrieve a vector representing the direction the shape's 'eye' node is facing. Returns Returns a vector representing the direction a shape is looking. If this shape's mesh contains no 'eye' node, the shape's forward vector will be returned instead. Notes Unless this shape is being observed through (i.e. the camera is hooked up to this shape and using its transforms for viewing), this will default to getForwardVector(). See Also getEyePoint, getEyeTransform

126

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Image/Weapons Specifics
getImageAmmo getImageTrigger isImageFiring getImageLoaded getMuzzlePoint setImageAmmo getImageSkinTag getMuzzleVector getImageLoaded getImageState getPendingImage setImageTrigger

getImageAmmo( slot ) Purpose Use the getImageAmmo method to determine if the image mounted at slot has ammo. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns true if there is an image mounted at slot and it has ammo, false otherwise. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also setImageAmmo getImageLoaded( slot ) Purpose Use the getImageLoaded method to determine if the image mounted at slot is loaded. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. Returns Returns true if there is an image mounted at slot and it is loaded, false otherwise. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also setImageLoaded

127

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getImageSkinTag( slot ) Purpose Use the getImageSkinTag method to get the skin tag for the image mounted at slot on this shape. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns an integer representing the tag number for the texture the image mounted at slot is using. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. Skin tags are selected when mounting an image to a shape. See Also mountImage getImageState( slot ) Purpose Use the getImageState method to determine state name that an image mounted in slot is at. i.e. If an image is mounted in slot, what state is its state-machine currently in? Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns a string representing the state name that the image mounted at slot is currently at. If there is not state machine defined for the image at slot, or if there no image at slot, or if slot is > 7, this method returns Error. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape.

128

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getImageTrigger( slot ) Purpose Use the getImageTrigger method to get the trigger value for the image mounted at slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns a boolean value specifying whether the trigger for the image at slot is active (true), or inactive (false). If no image is mounted at slot or if slot is > 7, this method returns 0. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also setImageTrigger getMuzzlePoint( slot ) Purpose Use the getMuzzlePoint method to get the position of the 'muzzlePoint' node for the image mounted at slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. Returns If an image is mounted at slot, and if it specifies the node 'muzzlePoint', this method will return the XYZ position of the node. If there is an image mounted at slot, and that image does not contain a 'muzzlePoint' node, this method returns the position of the image's centroid. In all other cases, this method returns 0 0 0. See Also getMuzzleVector

129

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getMuzzleVector( slot ) Purpose Use the getMuzzleVector method to get the direction the 'muzzlePoint' node for the image mounted at slot is pointing. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns If an image is mounted at slot, and if it specifies the node 'muzzlePoint', this method will return the XYZ pointing vector of the node. If there is an image mounted at slot, and that image does not contain a 'muzzlePoint' node, this method returns the equivalent of a forward vector for the weapon itself. In all other cases, this method returns 0 0 0. The returned vector can be modified by setting the image.correctMuzzleVector field. If this value is set to true, the vector will point from its origin to the position the player's eye is looking, otherwise, it will point in the true direction the weapon or weapon's muzzlePoint is facing, which may not be exactly where the player is looking. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also getMuzzlePoint getPendingImage( slot ) Purpose Use the getPendingImage method to determine if any images are pending for the specified slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns 0 if no images are pending or an integer representing the image datablock ID if an image is pending. Notes A pending image is an image that is waiting to mount a slot but is prevented from doing so by the presence of another 'blocking' image. Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape.

130

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isImageFiring( slot ) Purpose Use the isImageFiring method to determine if this image's state machine is currently in a state with the stateFire field set to true. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns true if this image's state machine is currently in a state with the stateFire field set to true, false otherwise. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also getImageState setImageAmmo( slot , hasAmmo ) Purpose Use the setImageAmmo method to give an image in slot ammo, or to remove ammo from an image in slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. hasAmmo A boolean value. If set to true, this image is set has having ammo, otherwise it is set as not having ammo. Returns Returns true on success, and false on failure. Notes Weapons support both 'ammo' and 'loaded' as distinct concepts. Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also setImageLoaded

131

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setImageLoaded( slot , loaded ) Purpose Use the setImageLoaded method to set an image in slot as loaded or unloaded. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. hasAmmo A boolean value. If set to true, this image is set has being loaded, otherwise it is set as not being loaded. Returns Returns true on success, and false on failure. Notes Weapons support both 'ammo' and 'loaded' as distinct concepts. Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also setImageAmmo setImageTrigger( slot , triggered ) Purpose Use the setImageTrigger method to set the trigger state for an image in slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. triggered A boolean value. If set to true, this image is triggered (firing), otherwise it is un-triggered (not firing). Returns Returns true on success, and false on failure. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape.

132

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Velocity
getVelocity() setVelocity()

getVelocity() Purpose Use the getVelocity method to get the current velocity of this shape. Returns Returns an XYZ vector equivalent to the magnitude and direction of this shape's movement. See Also applyImpulse, setVelocity setVelocity( velocity ) Purpose Use the setVelocity method to set this shape's current velocity. Syntax veclocity An XYZ vector equivalent to the new magnitude and direction of this shape's movement. Returns Returns true on success, false on failure. Notes It is sometimes nicer to use this method than applyImpluse because with this we can ignore a shape's mass while with applyImpluse mass directly affects the end velocity of the shape. See Also appyImpulse, getVelocity

133

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Impulses
applyImpulse

applyImpulse( position , impulseVector ) Purpose Use the applyImpulse method to apply an instantaneous acceleration to a shape at world position. Syntax position An XYZ vector specifying the position at which to apply the impulse. impulseVector An XYZ vector encoding the magnitude and direction of the impulse. Returns No return value. Notes To apply an impulse, the shape must have a non-zero positive mass, or applying an impulse will crash the engine. Also, applying an impulse whose magnitude is >= about 40 times the mass of a shape may cause the engine to lock up temporarily. See Also setVelocity

Camera Settings
getCameraFOV setCameraFOV

getCamerFOV( ) Purpose Use the getCamerFOV method to get the current field-of-view FOV. Returns Returns the current FOV setting, a value between 0.0 and 180.0 See Also setCameraFOV

134

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setCamerFOV( FOV ) Purpose Use the setCamerFOV method to set the current field-of-view (FOV) if this is a camera shape, or if a camera shape is using this shape to get it's FOV setting. Syntax FOV Field-of-view. Returns No return value. Notes Changing the FOV will make the view zoom in and out, depending on the setting. i.e. If we start at an default FOV of 90.0 and increase it to 270.0, the scene will zoom out. If we change it to 35.0 it will zoom in. See Also getCameraFOV A floating-point value between 0.0 and 180.0.

Animations
pauseThread playThread setThreadDir stopThread

pauseThread( thread ) Purpose Use the pauseThread method to pause a currently playing animation. Syntax thread An integer value between 0 and 3 specifying a previously started animation sequence. Returns Returns true if there was a previous thread playing, false otherwise. See Also playThread, stopThread

135

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

playThread( thread , sequenceName ) Purpose Use the playThread method to play a new sequence specified by sequenceName in the animation thread. Syntax thread An integer value between 0 and 3 specifying a thread/slot to start this animation in. sequenceName A valid animation name for the mesh this shape is using. Returns Returns true if the thread was successfully started, otherwise returns false. Notes Playing an new animation in a thread that already has an active animation will stop the active animation and reset affected nodes to their pre-animation positions. See Also pauseThread, setThreadDir, stopThread setThreadDir( thread , forward ) Purpose Use the setThreadDir method to set the direction a specific thread should be played in. Syntax thread An integer value between 0 and 3 specifying a previously started animation sequence. forward A boolean value. If set to true, the animation will play forward, otherwise it will play in reverse. Returns Returns true if the direction could be set for the specified thread, otherwise returns false. Notes A thread needs to have already been started for this to work, but after that this will work on any sequence whether it be already completed, or a cyclic animation that never ends. See Also pauseThread, playThread, stopThread

136

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

stopThread( thread

Purpose Use the stopThread method to stop a previously started sequence from playing. Syntax thread An integer value between 0 and 3 specifying a previously started animation sequence. Returns Returns true if the thread was successfully stopped, otherwise returns false. See Also pauseThread, playThread, setThreadDir

Sound
playAudio stopAudio

playAudio( thread

, audioProfile )

Purpose Use the playAudio method to play a sound specified by audioProfile in the slot specified by thread. Syntax thread An integer value between 0 and 3 specifying a slot to play the sound in. audioProfile An audioprofile datablock previously created using the datablocks keyword, not the new keyword. Returns Returns true if the sound was successfully started, otherwise returns false. Notes Be sure to only use audioProfiles made using the datablock keyword or the sounds will not play on remote clients. See Also stopAudio

137

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

stopAudio( thread ) Purpose Use the stopAudio method to stop a previously started audio thread. Syntax thread An integer value between 0 and 3 specifying a slot in which a sound was previously started. Returns Returns true if the sound was successfully stopped, otherwise returns false. See Also playAudio

Mounting
getMountedImage getMountNodeObject getObjectMount isImageMounted mountObject unmountObject getMountedObjectCount getMountSlot getPendingImage isMounted unmount getMountedObjectNode getMountedObject getSlotTransform mountImage unmountImage

getMountedImage( slot ) Purpose Use the getMountedImage method to get the datablock ID of the image mounted in slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns the datablock ID of the image mounted in slot, or zero if no image is mounted. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also isImageMounted, mountImage, unmountImage getMountedObjectCount() Purpose Use the getMountedObjectCount method to determine how many objects are currently mounted to this shape. Returns Returns an integer between 0 and 8 representing the number of objects that are mounted to this shape.

138

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Notes A shape may have at most eight objects mounted to it at any one time. This method does not count images that are mounted to the shape. See Also mountObject, unmountObject getMountedObjectNode( slot ) Purpose Use the getMountedObjectNode method to determine the mount node index of an object mounted in slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns a numeric value between 0 and 31 if an object is mounted is slot, otherwise returns -1. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also getMountNodeObject Return the mount node used by the object/image mounted at slot. getMountNodeObject( node ) Purpose Use the getMountNodeObject method to get the ID of the object mounted at mount node. Syntax node A node value between 0 and 31 representing a mount node in the mesh used by this shape. Returns Returns an integer value equal to the ID of the object mounted on node. mounted at node, this method returns -1. If not object is

Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also getMountedObjectNode

139

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getMountSlot( imageHandle ) Purpose Use the getMountSlot method to determine what slot the image datablock specified by imageHandle is mounted in. Syntax imageHandle The name of ID of a ShapeBaseImageData datablock. Returns Returns a value between 0 and 7 if imageHandle is mounted to this shape, or -1 if it is not. getMountedObject( slot ) Purpose Use the getMountedObject method to get the ID of the object mounted at mount slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns an integer value equal to the ID of object mounted is slot. mounted in this slot, the method returns -1. If not object is

Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also getMountedObjectNode getObjectMount() Purpose Use the getObjectMount method to get the ID of the object this object is mounted to. Returns Returns an integer value representing the ID of an object that this shape is mounted to, or 0 if this object is not mounted to another object. getPendingImage( slot ) Purpose Use the getPendingImage method to get the ID of an image that is pending for slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns an integer value representing the ID of a shapeBaseImageData object that is pending for slot. If no image is pending, returns 0.

140

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. getSlotTransform( slot ) Purpose Use the getSlotTransform method to get the transform the specified slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns a seven-element floating-point vector representing the transform of the node that an object tracked in slot is mounted to. i.e. Once we have a shape mounted to another shape, we can determine what slot the shape is tracked in and then get the transform for the node that is being used for the mount. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. isImageMounted( imageHandle ) Purpose Use the isImageMounted method to determine if the shapeBaseImageData specified by imageHandle is mounted on this shape. Syntax imageHandle The datablock ID of a shapeBaseImageData object to check for. Returns Returns true if the specified image is mounted to this shape, otherwise returns false. isMounted() Purpose Use the isMounted method to see if this object is mounted to another shape. Returns Returns true if this shape is mounted to another shape, otherwise returns false.

141

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mountImage( imageHandle, slot [ , loaded [ , skinName ] ] ) Purpose Use the mountImage method to mount the image specified by imageHandle to this image, using slot. Optionally, this image can be set to the 'loaded' state and given an alternate skinName. Syntax imageHandle The ID or name of a valid shapeBaseImageData datablock. slot An integer value between 0 and 7 representing a mount slot on this shape. loaded A boolean value. If set to true, the image the image's loaded state will be true. skinName A optional skin tag used to specify a specialized 'team' skin for the image. Returns Returns true on success, false otherwise. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. Don't forget. See Also mountObject mountObject( objectHandle , node ) Purpose Use the mountObject method to mount the shape specified by objectHandle to this shape at mount node. Syntax objectHandle The ID or name of the shape to mount to this shape. node The mount node on this shape's mesh upon which to mount the shape. Returns Returns true on a successful mount, otherwise returns false. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also mountImage Images specify the node that the mount to in their 'mountPoint' field.

142

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

unmount() Purpose Use the unmount method to force this object to be unmounted from a shape it is mounted to. Returns No return value. See Also unmountImage, unMountObject unmountImage( slot ) Purpose Use the unmountImage method to unmount an image mounted to this shape in slot. Syntax slot An integer value between 0 and 7 representing a mount slot on this shape. Returns Returns true if the image was unmounted successfully, otherwise returns false if no image was present in that slot. Images cannot 'resist' unmounting. Notes Do not confuse slots with mount nodes. A shape can have up to 32 mount nodes, but only has eight mount slots. Mount nodes are points on the mesh used by the shape while slots are used to track how many objects and images are mounted to a shape. See Also unmountObject unmountObject( objectHandle ) Purpose Use the unmountObject method to cause the object specified by objectHandle to unmount from this shape. Syntax objectHandle The ID or name of an object mounted to this shape. Returns Returns true if objectHandle is in fact mounted to this shape and is then unmounted by this method. Returns false if objectHandle is not mounted to this shape. See Also unmountImage

143

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

The Control Object


getControllingClient() getControllingObject()

getControllingClient() Purpose Use the getControllingClient method to get the ID of the client controlling this shape. Returns Returns the ID of the GameConnection (client) controlling this shape, or 0 if no client is controlling this shape. See Also getControllingObject getControllingObject() Purpose Use the getControllingObject method to get the ID of the object controlling this object. Shapes may controlled by other shapes by having the controlling shape pass commands sent to it from the client to a surrogate object instead. i.e. Object A can be controlled by the client and send movement inputs to object B, in effect controlling object B. Returns Returns the ID of the shape controlling this object, otherwise if no object is controlling this shape returns 0. The playe class has methods for setting up control of other shapes.

A.2.59. ShapeBaseData
Fields
Field Name Rendering cloakTexture emap shapeFile Physics density drag mass Damage debris debrisShapeName DebrisData datablock to use for post-destruction debris. Path to DTS file containing the model for this shape which is to be rendered post destruction. See lesson samples. See lesson samples. The density of this shape. Affects whether this shape will sink or float in water (relative to water density). Drag presented by this shape as it moves through the air. Mass for this shape. See lesson samples. 0 100 Path to texture to be used when shape is cloaked. A boolean value specifying whether to render environmental map or not. Path to DTS file containing the model for this shape. See lesson samples. false See lesson samples. Description Sample or Range

144

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name explosion isInvincible maxDamage renderWhenDestroyed underwaterExplosion Damage Reference Values destroyedLevel disabledlevel repairRate Energy inheritEnergyFromMount maxEnergy Energy Reference Values rechargeRate Camera Settings cameraDefaultFOV cameraMaxDist cameraMaxFOV cameraMinDist cameraMinFOV firstPersonOnly observeThroughObject useEyePoint Miscellaneous aiAvoidThis

Description ExplosionData datablock to use for an explosion when this shape is destroyed If this boolean value is true, this shape does not take damage. Maximum damage this shape can accrue. Boolean value specifying whether to render this shape or not once it's damageState is 'Destroyed'. ExplosionData datablock to use for an explosion when this shape is destroyed and submerged in water. These values do not change engine functions and are meant to be used by scripts. Damage level at which this shape will be considered destroyed. Damage level at which this shape will be considered disabled. Rate (in points per tick) at which this shape is repaired when current damage > 0. When mounted, use mount's energy. Maximum energy this object can have. These values do not change engine functions and are meant to be used by scripts. Rate (in points per tick) at which this shape is recharged when current energy < maxEnergy. Initial field of view for the camera. Maximum distance from camera to observing object. Maximum field of view for the camera. Minimum distance from camera to observing object. Minimum field of view for the camera. Render shape when in first person (only applies to shapes that are observed through. If true, camera will use this shape's camera parameters, when this is the controlled object Use the eye node specified by this shape instead. A boolean hit to be used by AI scripts to determine if an AI agent should 'avoid' this object. Scripts are responsible for dealing with this. Calculate a cyclic-redundancy-code for this shape.

Sample or Range See lesson samples. false 100 false See lesson samples.

100 50 0.03125 (== 1 point per second) false 50

0.03125 (== 1 point per second) [ 0.0 , 180 ] [ cameraMinDist , inf ) [ 0.0 , 180 ] [ 0.0 , cameraMaxDist ] [ 0.0 , 180 ] [ false , true ] [ false , true ] [ false , true ] [ false , true ]

computeCRC

[ false , true ]

Console Method Summaries


checkDeployPos getDeployTransform

145

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Methods
Deployment Helpers
getDeployTransform( position , normal ) Purpose Use the getDeployTransform method to retrieve the transform that would result from deploying an unscaled object using this datablock at position with the specified normal. Syntax position The postion to create the transform for. normal A vector representing the object's UP vector. This can be thought of as an object's tilt or leaning vector. If the object is to be placed at an angle, pass in a unit-vector that has the same orientation as a vector starting at position and aligning with the required angle. Returns Returns a deployment (placement) transform for an un-scaled and un-rotated datablock derived shape placed at the required location with an optional tilt. See Also checkDeployPos checkDeployPos( transform ) Purpose Use the checkDeployPos method to determine if placing an unscaled version of an object using this datablock will cause that shape to be embedded in a staticShape or an InteriorInstance. Syntax transforms A normal transform, or a transform calculated using getDeployTransform. Returns Returns false if the placement will result in the new shape being embedded in a staticShape or an InteriorInstance. Otherwise, it will return true. Notes This check doesn't examine the area for all possible object, just staticShapes and Interiors. To include more objects you will need to edit the engine and add them to the list of 'interesting' objects. See Also getDeployTransform

146

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.60. ShapeBaseImageData
ShapeBaseImageData has no associated class as other xyzData classes do. This special class is used to represent geometry that can only be mounted to an existing ShapeBase object. ShapeBaseImageData objects cannot otherwise be instantiated like other Shape objects.

Fields
Field Name Rendering emap firstPerson shapeFile Projectile projectile Cloaking cloakable Mounting eyeOffset eyeRotation mountPoint offset rotation Lighting lightColor lightRadius lightTime Three-element floating-point vector specifying RGB components of light. Floating-point value specifying radius for light emission. Integer value specifying time (in milliseconds) for light to pulse onoff-on. String specifying type of light does this image emits. r g b a In range [ 0.0 , 1.0 ] [0.0 , 20.0] [ 0 , inf ) NoLight ConstantLight PulsingLight --Rendering position offset (only affects 1st POV). Rendering rotation offset (only affects 1 POV). Named mount node to mount to on the receiving shape. Position offset from mount point. Rotation offset from mount point.
st

Description A boolean value specifying whether to render environmental map or not. If true, render this image in 1st POV. Path to DTS file containing the model for this shape. ProjectileData datablock A boolean value specifying whether this image cloaks when the shape it is mounted to is cloaked.

Sample or Range ------

--[ 0 , 31 ] ---

lightType Physics mass Weapons accuFire casing correctMuzzleVector

Floating-point value specifying mass for this image. Has no effect. DebrisData datablock to use for ejected casings. Boolean value specifying that the muzzle vector should be calculated from the players eyeVector, not from the muzzleVector of the image. Floating-point value specifying minimum energy required to 'fire' this weapon. Ejection vector for ejected casings. Floating-point value specifying variance in ejection vector direction. Floating-point value specifying shell ejection velocity. Boolean value specifying that this weapon uses energy.

[ false , true ] --[ 0.0 , 180.0 ] [ 0.0 , inf ] [ false , true ]

minEnergy shellExitDir shellExitVariance shellVelocity usesEnergy

147

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name State Machine stateAllowImageChange[] stateDirection[] stateEjectShell[] stateEmitter[] stateEmitterNode[] stateEmitterTime[] stateEnergyDrain[] stateFire[] stateIgnoreLoadedForReady[]

Description If false, will temporarily block other images from mounting while the state machine is executing the tasks in this state. Direction of animation. true is forward. Eject shell in this state. Play this particle emitter (at muzzle point or specified node). Integer value specifying node to attach emitter to. Default is to mount to named node 'muzzlepoint' Floating-point value specifying time to run emitter. Floating-point value specifying First state with this a true is the state entered by the client when it receives the 'fire' event. If set to true, and both ready and loaded transitions are true, the ready transition will be taken instead of the loaded transition. Set loaded state of shape to specified value. Name of this state. Play specified recoil animation. Method to execute on entering this state. Scoped to this image class name, then shapeBaseImageData, ... Play random flash animation. Play this animation. Play sound specified by this audio file.

Sample or Range [ false , true ] [ false , true ] [ false , true ]

[ false , true ] Ignore Loaded Empty NoRecoil LightRecoil MediumRecoil HeavyRecoil

stateLoadedFlag[] stateName stateRecoil[] stateScript[] stateSequenceRandomFlash[] stateSequence[] stateSound[]

[ false , true ]

stateSpinThread[]

Play this spin animation (blended).

Ignore Stop SpinUp SpinDown FullSpeed [ 0.0 , inf ) someStateName someStateName someStateName someStateName someStateName someStateName someStateName someStateName someStateName someStateName someStateName [ false , true ]

stateTimeoutValue[] stateTransitionOnNotLoaded[] stateTransitionOnTriggerDown[] stateTransitionOnTriggerUp[] stateTransitionOnAmmo[] stateTransitionOnLoaded[] stateTransitionOnNoAmmo[] stateTransitionOnNoTarget[] stateTransitionOnNotWet[] stateTransitionOnWet[] stateTransitionTarget[] stateTransitionTimeout[] stateWaitForTimeout[]

Time in seconds for this state to time out. Transition to this named state if the state of loaded is 'Empty'. Transition to this named state if the fire button is pressed. Transition to this named state if the fire button is released. Transition to this named state when the ammo the image has ammo. Transition to this named state when the loaded state of the image is 'Loaded'. Transition to this named state when the the image has no ammo. Transition to this named state when the image has no target. Transition to this named state when the image is not underwater. Transition to this named state when image is underwater. Transition to this named state when the image has a target. Transition to this named state when the current state transition timeout time has elapsed. If false, this state ignores timeout and transitions immediately if other tests are met.

148

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name Miscellanous computeCRC

Description Verify that the CRC of the client's image matches the server's CRC for the image on load by client.

Sample or Range [ false , true ]

A.2.61. SimDataBlock
This class is the root class for all datablock classes. It does not define any fields or console methods of its own.

A.2.62. SimGroup
Same fields as SimSet.

A.2.63. SimObject
Console Method Summaries
delete getGroup getType dump getId save getClassName getName schedule

Console Methods
delete() Purpose Use the delete method to delete this object. Returns No return value. Notes When an object is deleted, it automatically: Unregisters its ID and name (if it has one) with the engine. Removes itself from any SimGroup or SimSet it may be a member of. (eventually) returns the memory associated with itself and its non-dynamic members. Cancels all pending %obj.schedule() events. For objects in the GameBase, ScriptObject, or GUIControl hierarchies, an object will first: Call the onRemove() method for the object's namespace.

149

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

dump() Purpose Use the dump method to display the following information about this object: All All All All engine registered console methods (including parent methods) for this object. script registered console methods (including parent methods) for this object. Non-Dynamic Fields Dynamic Fields

Returns No return value. getClassName() Purpose Use the getClassName method to get the ConObject class name of this object. Returns Returns a string containing the ConObject registered name of this object's class. Some Possible Returns: SimObject WheeledVehicle Player ... See Also getType

getGroup() Purpose Use the getGroup method to determine if this object is contained in a SimGroup and if so, which one. Returns Returns the ID of the SimGroup this shape is in or zero if the shape is not contained in a SimGroup.

getId() Purpose Use the getId method to get the numeric ID of this shape. Returns Returns the unique numeric ID of this shape. See Also getName, setName

150

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getName() Purpose Use the getName method to get the name (if any) for this shape. Returns Returns a string containing the name of this shape, or the NULL string if this shape never given a name (either when created or subsequently). See Also getID, setName was

getType() Purpose Use the getType method to get the type for this object. This type is an integer value composed of bitmasks. For simplicity, these bitmasks are defined in the engine and exposed for our use as global variables. Returns Returns a bit mask containing one or more set bits. Notes To simplify the writing of scripts, a set of globals has been provided containing the bit setting for each class corresponding to a particular type. For a complete list of the bit masks, see the 'Shape Type Bitmasks' table. -$TypeMasks::GameBaseObjectType -- $TypeMasks::EnvironmentObjectType -- $TypeMasks::ExplosionObjectType -- $TypeMasks::ProjectileObjectType -- $TypeMasks::ShapeBaseObjectType --- $TypeMasks::CameraObjectType --- $TypeMasks::ItemObjectType --- $TypeMasks::MarkerObjectType --- $TypeMasks::PlayerObjectType --- $TypeMasks::StaticShapeObjectType --- $TypeMasks::VehicleObjectType -- $TypeMasks::TriggerObjectType - $TypeMasks::InteriorObjectType - $TypeMasks::StaticObjectType - $TypeMasks::TerrainObjectType - $TypeMasks::VehicleBlockerObjectType - $TypeMasks::WaterObjectType Two interesting general masks are: $TypeMasks::EnvironmentObjectType Matches sky, sun, lightning, particle emitter nodes. $TypeMasks::StaticObjectType Matches fxFoliageReplicator, fxLight, fxShapeReplicator, fxSunlight, interiorInstance, lightning, mirrorSubObject, missionMarker, staticShape, terrain, tsStatic See Also getClassName

151

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

$TypeMasks::StaticObjectType $TypeMasks::TerrainObjectType $TypeMasks::WaterObjectType $TypeMasks::MarkerObjectType $TypeMasks::ShapeBaseObjectType $TypeMasks::StaticShapeObjectType $TypeMasks::ItemObjectType $TypeMasks::VehicleBlockerObjectType $TypeMasks::ExplosionObjectType

$TypeMasks::EnvironmentObjectType $TypeMasks::InteriorObjectType $TypeMasks::TriggerObjectType $TypeMasks::GameBaseObjectType $TypeMasks::CameraObjectType $TypeMasks::PlayerObjectType $TypeMasks::VehicleObjectType $TypeMasks::ProjectileObjectType

Shape Type Bitmasks


schedule(time , command , <arg1 ... argN> ) Purpose Use the schedule method to schedule an action to be executed upon this object time milliseconds in the future. Syntax time Time in milliseconds till action is scheduled to occur. command Name of the command to execute. This command must be scoped to this object (i.e. It must exist in the namespace of the object), otherwise the schedule call will fail. arg1...argN These are optional arguments which will be passed to command. This version of schedule automatically passes the ID of %obj as arg0 to command. Returns Returns an integer schedule ID. Notes The major difference between this and the schedule console function is that if this object is deleted prior to the scheduled event, the event is automatically canceled. times should not be treated as exact since some 'simulation delay' is to be expected. The minimum resolution for a scheduled event is ~32 ms, or one tick. schedule does not validate the existence of command. i.e. If you pass an invalid console method name, the schedule() method will still return a schedule ID, but the subsequent event will fail silently. See Also See the schedule console function and its corresponding helper functions.

152

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setName( name ) Purpose Use the setName method to give this shape a new name. Syntax name A string containing the new name for this shape, or a NULL string to un-name this shape. Returns No return value. Notes Names can be compose of non-alphanumeric symbols, but be careful when doing this. See Also getID, getName

A.2.64. SimSet
This is a generic container class for SimObjects. It provides a basic set of console methods for storing and accessing SimObjects contained in the set. Any SimObject may be contained in multiple SimSets

Console Methods
add getObject remove bringToFront isMember clear listObjects getCount pushToBack

add( obj1 , ... ) Purpose Use the add method to add one or more objects to this SimSet. Syntax obj1 The ID or name of an object to add to the SimSet. ... - A comma separated list of as many names and object IDs as you wish, all of which will be added to this SimSet. Returns No return value. See Also clear, bringToFront, getCount, getObject, isMember, listObjects, pushToBack. Add new object to SimSet. Objects are validated on addition. i.e. If you pass an invalid ID an error will be printed and the add will fail. obj1 A object ID to add to the SimSet. ... - A list of comma separated IDs may be passed.

153

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

bringToFront( object ) Purpose Use the bringToFront method to move object to front of SimSet (queue). This is useful for sorting and later when this class is used as the base to the GUIControl class. Syntax object The ID or name of an object already in the set. Returns No return value. Notes Do not attempt to call this for objects that are not in the SimSet as you can crash the engine. See Also pushToBack

clear() Purpose Use the clear method to remove all entries from SimSet. Notes This does not cause the entries to be deleted. See Also add, remove

getCount() Purpose Use the getCount method to determine how many objects are currently being tracked by this SimSet. Returns Returns an integer value between 0 and inf equal to the number of objects currently being tracked by this SimSet. See Also add, clear, remove

154

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getObject( index ) Purpose Use the getObject method to get the ID of an object stored in this SimSet at position index. Syntax index The queue position of the object to look for/get. Returns Returns ID of object at index in SimSet. returned. If no object is found at that index, 0 is

isMember( object ) Purpose Use the isMember method to determine if object is being tracked by this SimSet. Syntax object An object name or ID to check for in this set. Returns Returns true if object is in this SimSet, otherwise returns false. Notes Unless you are absolutely sure about membership be sure to use this method prior to using bringToFront or pushToBack. See Also bringToFront, pushToBack

listObjects() Purpose Use the listObjects method to dump a list of all objects in this SimSet to the console. Returns No return value. Notes This is a helpful debug tool.

155

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

pushToBack( object ) Purpose Use the pushToBack method to push an object (already in the SimSet) to then back of the queue. Syntax object The ID or name of an object already in the set. Returns No return value. Notes Do not attempt to call this for objects that are not in the SimSet as you can crash the engine. See Also bringToFront

remove( obj1 , ... ) Purpose Use the remove method to remove one or more objects from this SimSet. Syntax obj1 The ID or name of an object already in the SimSet. ... - A comma separated list of objects already in this SimSet. Returns No return value. Notes Attempting to remove an object from this SimSet which is not in the SimSet will have no effect. See Also add, clear

156

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.65. Sky
Fields
Field Name cloudHeightPer0 cloudHeightPer1 cloudHeightPer2 cloudSpeed1 cloudSpeed2 cloudSpeed3 cloudText[0,2] fogColor fogDistance fogStorm1 fogStorm2 fogStorm3 fogVolume1 fogVolume2 fogVolume3 fogVolumeColor[1:3] materialList noRenderBans renderBottomTexture SkySolidColor useSkyTextures visibleDistance windEffectPrecipitation windVelocity Path to sky DML file. If set to true, the sky box is not allowed to be fogged out. If set to true, the bottom texture in the DML file will be rendered. The color for the untextured sky box. Enables sky box texture rendering. Maximum render distance. If set to true, wind will blow precipitation. Magnitude and direction of wind. Color of general fog. Affects fog density as a ratio of visibleDistance. Enables fog storm fading of layer 0. Enables fog storm fading of layer 1. Enables fog storm fading of layer 2. Layer 0 fog definition. Layer 1 fog definition. Layer 2 fog definition. -Description Relative height of layer 0 clouds. Relative height of layer 1 clouds. Relative height of layer 2 clouds. Cloud scrolling vs. windVelocity multiplier for layer 0 clouds. Cloud scrolling vs. windVelocity multiplier for layer 1 clouds. Cloud scrolling vs. windVelocity multiplier for layer 2 clouds. -Sample or Range [ 0.0 , cloudHeightPer1 ] [ cloudHeightPer0 , cloudHeightPer2 ] [ cloudHeightPer1 , 1.0 ] [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) Not used "1.0 0.5 0.5 1.0" [ 0.0 , inf.0 ) [ false , true ] [ false , true ] [ false , true ] "dist low-elev high-elev" "dist low-elev high-elev" "dist low-elev high-elev" Not used dml file path [ false , true ] [ false , true ] "r g b a" (float) [ false , true ] [ 0.0 , inf ) [ false , true ] 1.5 2.0 0.0

Globals
Variable Name pref::CloudOutline pref::CloudsOn pref::NumCloudLayers pref::SkyOn Enables cloud rendering. Limits number of cloud layers that are rendered. Enables the sky. Description Enable or disable cloud outlining. Sample or Range [ false , true ] [ false , true ] [0,3] [ false , true ]

Console Methods
getWindVelocity() stormCloudsShow() realFog() stormFog() setWindVelocity() stormFogShow() stormClouds()

157

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getWindVelocity() Purpose Use the getWindVelocity method to get the current wind velocity vector. Returns Returns a three-element floating point vector representing the direction and velicity of the wind. setWindVelocity( x , y , z ) Purpose Use the setWindVelocity method to modify the current wind velocity. Syntax x A floating-point value representing the X component of the wind velocity. y A floating-point value representing the Y component of the wind velocity. z A floating-point value representing the Z component of the wind velocity. Returns No return value. stormClouds( show , duration ) Purpose Use the stormClouds method to show or hide all defined cloud layers over duration seconds. Syntax show Boolean value specifying whether to show (true), or hide (false) clouds. duration Time in seconds within which to achieve results. Returns No return value. Notes duration cannot be zero Layers fade in top-to-bottom and fade out bottom-to-top See Also stormCloudsShow, stormFog, stormFogShow

158

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

stormCloudsShow( show ) Purpose Use the stormCloudsShow method to show or hide all defined cloud layers instantly. Syntax show Boolean value specifying whether to show (true), or hide (false) clouds. Returns No return value. See Also stormClouds, stormFog, stormFogShow stormFog( percent , duration ) Purpose Use the stormFog method to show or hide all stormFog enabled fog layers over duration seconds. Syntax percent Floating-point value specifying ending fade level for fog layers. duration Time in seconds within which to achieve results. Returns No return value. Notes duration cannot be zero Layers fade in bottom to top and fade out top-to-bottom stormFogn Must be checked for that layer to be affected by this method. percent Must be in range [ 0.0 , 1.0 ] See Also stormClouds, stormCloudsShow, stormFogShow stormFogShow( show ) Purpose Use the stormFogShow method to show or hide all stormFog enabled fog layers instantly. Syntax show Boolean value specifying whether to show (true), or hide (false) fog. Returns No return value. See Also stormClouds, stormCloudsShow, stormFog

159

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.66. SpawnSphere
Mission marker used to mark drop points. Can be used for many things. The fields in this object are only used by scripts, not the engine.

Fields
Field Name indoorWeight outdoorWeight radius sphereWeight Only used by scripts. Only used by scripts. Only used by scripts. Only used by scripts. Description Sample or Range any float any float any float any float

A.2.67. Splash
No fields or methods to discus.

A.2.68. SplashData
Fields
Field Name acceleration Description The acceleration of the splash, as used in the physical simulation. Affects the splash's velocity over time, along with the gravitational force acting in the system. Array of colors. Specifies what colors are to be used for splash rings at each time value specified in the times array field. First entry in the colors array corresponds to the initial ring color, and is interpolated with colors[1] until times[0] time has elapsed, at which time colors[1] is the draw color and it begins being interpolated with colors[2]. See the times and ringLifetime field documentation for more information. Sample or Range ( -inf.0 , inf.0 )

colors[0] colors[1] colors[2] colors[3] delayMS delayVariance ejectionAngle ejectionFreq emitter[0] emitter[1] emitter[2] explosion height lifetimeMS

"1.0 0.5 0.5"

Note: this field currently has no tangible effect in the engine's simulation. Note: this field currently has no tangible effect in the engine's simulation.
The angle at which new splash rings should be ejected, specified in degrees. The frequency with which new splash rings should be created. Array of pointers to ParticleEmitterData datablocks which specify the particle emissions to be used for the Splash object. ExplosionData datablock, which will be used to spawn an explosion for the splash.

--( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) see type see type -( -inf , inf )

This field currently has no tangible effect in the engine's simulation.


Used along with lifetimeVariance to determine the maximum time the Splash object persists in the world. Measured in whole milliseconds. Used along with lifetime to determine the maximum time the Splash object persists in the world. The Splash object's actual life-time is calculated by adding the lifetime field with a random integer from -1*lifetimeVariance to lifetimeVariance. A Splash object will be marked as dead once it's life-time expires, but a Splash object will not delete itself until its ring life-time has expired as well; see the documentation for the

lifetimeVariance

( -inf , inf )

160

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name

Description ringLifetime field for more information. Using lifetimeVariance, separate Splash objects sharing the same SplashData datablock can be given varied behavior. The number of segments used to generate each ring of the splash. Each segment has an associated splash ring texture u-coordinate, which is calculated by dividing the segment's position in the splash ring's segment list by the total number of the ring's segments, and then multiplying this result by the scalar value in the texWrap field. The life-time, in seconds, of splash rings generated by the Splash object. Also used during the rendering of splash rings to determine the opacity of the ring; the ring starts out fully transparent, and follows a linear progression to become fully opaque at the mid-point of its life-time, after which time a linear fall-off in opacity occurs until the end of the ring's lifetime, at which point it is once again fully transparent. a Splash object will not delete itself until all ring's it has generated have expired.

Sample or Range

numSegments

( -inf , inf )

ringLifetime

( -inf.0 , inf.0 )

scale soundProfile startRadius

Note: this field currently has no tangible effect in the engine's simulation. Note: this field currently has no tangible effect in the engine's simulation.
The inital radius with which to eject splash rings, affected by velocity over time. Scalar value used to translate the v-coordinate value of texture reads from the splash rings texture during their rendering. texFactor is not applied directly to scale v-coordinate reads, as the texWrap field is. Rather, texFactor is multiplied with the non-integer portion of the elapsed time (elapsedTime - int(elapsedTime)) to determine the v-coordinate texture position to be read for the splash ring.escription The texture file to be used for the splash rings. Scalar value used along with numSegments to determine the u-coordinate value of texture reads from the splash rings texture during their rendering. See the numSegments field documentation for more information. Array of time values. Each entry is used to help determine the color used in rendering splash rings, as described in the colors field documentation.

--( -inf.0 , inf.0 )

texFactor

( -inf.0 , inf.0 )

texture texWrap

~/path/filename.png ( -inf.0 , inf.0 )

times[0] times[1] times[2] times[3]

( -inf.0 , inf.0 )

velocity

The velocity of the splash, as used in physical simulation. Affects the radius of the splash over time, and the velocity of splash rings. Velocity changes over time in accordance with the value specified with the acceleration field, and the gravity affecting the physical simulation.

( -inf.0 , inf.0 )

width

Note: this field currently has no tangible effect in the engine's simulation.

--

161

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.69. StaticShape
A concrete class derived from ShapeBase used to reprsent world objects.

Console Methods
getPoweredState() setPoweredState()

getPoweredState() Purpose Use the getPoweredState method to get the shape's current 'powered' state. Syntax state Is the shape powered? [ false , true ] Returns No return value. Notes This feature does not modify any engine behaviors and is purely for scripted use. See Also setPoweredState setPoweredState( isPowered ) Purpose Use the setPoweredState method to set shape's current 'powered' state. Syntax state Is the shape powered? [ false , true ] Returns No return value. Notes This feature does not modify any engine behaviors and is purely for scripted use. See Also getPoweredState

162

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.70. StaticShapeData
Datablock associated with StaticShape object class.

Fields
Field Name dynamicType noIndividualDamage Description An integer value which, if specified, is added to the value returned by getType(). Boolean value used as a hint to scripts, to NOT apply damage to this shape. Sample or Range See dynamicType below --

A.2.71. Sun
Mission object representing mission lighting parameters.

Fields
Field Name ambient azimuth color elevation Azimuth of sun. Color / Intensity of direct light. Inclination (elevation) of sun. Description Color / Intensity of ambient light. Sample or Range "r g b i" (float) [ 0, 360 ] "r g b i" (float) [ 0, 360 ]

A.2.72. TCPObject
Console Method Summaries
connect disconnect listen

Console Methods
connect( addr ) Purpose Use the connect method to request a connection to a remote agent at the address addr. Syntax addr A string containing an address of the form: A.B.C.D:Port, where A .. B are standard IP numbers between 0 and 255 and Port can be between 1000 and 65536. Returns No return value. See Also disconnect

163

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

disconnect() Purpose Use the disconnect method to close a previously opened connection without destroying the requesting TCPOpbject. Returns No return value. Notes This will close any open connection, but not destroy this object. be used to open a new connection. See Also connect listen( port ) Purpose Use the listen method to allow this TCPObject to accept connections on the specified port. Syntax port A value between 1000 and 65536. Returns No return value. send( ... ) Purpose Use the send method to send any number of parameters, as strings, one at a time to the agent at the other end of the connection. Syntax ... Any number of arguments, as strings. The arguments are not concatenated. Returns No return value. Each string is sent separately. i.e. Thus, the object can

164

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.73. TerrainBlock
Mission object representing terrain. Default terrain in Torque measures 2 x 2 km and repeats forever. See EGTGE Volume I Mission Objects chapter for usage.

Fields
Field Name bumpOffset bumpScale bumpTexture detailTexture emptySquares squareSize terrainFile zeroBumpScale tile Description Offset between bumpmap textures. Scale of bumpmap texture. Path to texture to use for bump map textures. Path to texture to use for terrain detailing. List of empty squares in terrain. Vertical and horizontal distance between terrain verticies in meters. Default is 8 meters. Affects water rendering. Path to file used for terrain. Value controlling bumpmap rendering distance. Enables and disables tiling of terrain. Sample or Range [ 0.0 , inf ) [ 1 , inf ) ---1 , 2 , 4 , 8 (default) , 16 , 32 , 64 , 128 -[ 1 , inf ) [ false , true ]

Globals
Variable Name $farDistance $pref::Terrain::dynamicLights $pref::Terrain::enableDetails $pref::Terrain::enableEmbossBumps $pref::Terrain::screenError Enable/Disable detailTexture rendering. Enable/Disable bump map rendering. Terrain screen error metrics. The higher this value is, the smoother terrain slopes will become (within limits). Lowering this value can increase render speed on old systems. This value modifies the texturing LOD. Higher numbers equal LOWER LOD. For best results leave this at 0. Description Distance to far render plane (where rendering stops). Sample or Range Modify in sky object. ---[ 0 , inf )

$pref::Terrain::texDetail $pref::Terrain::textureCacheSize $screenSize $T2::dynamicTextureCount $T2::staticTextureCount

[ 0 , 10 ] ---

Grand total newly rendered textures count for the terrain. Grand total rendered textures count for the terrain.

---

Console Methods
getHeightfieldScript() setTextureScript() getTextureScript() save() setHeightfieldScript()

These methods are used by the editors and should not be used for general scripting purposes. Please see EditorGui.cs if you want to see these method in use.

165

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.74. Trigger
Mission object representing a re-active zone in the game. This object senses the entry, exit, and presence of objects within it's bounds.

Fields
Field Name polyhedron Description List of (relative) coordinates representing bounds of area. Sample or Range --

Console Methods
getNumObjects() getObject()

getNumObjects() Purpose Use the getNumObjects method to determine how many GameBase objects are within the bounds of this trigger. Returns Returns an integer value specifying the number of objects that are currently within the boundaries of this trigger. See Also getObject getObject( index ) Purpose Use the getObject method to retrieve the ID of an object, at index, within this trigger's object list. Syntax index An integer value between 0 and numObjects, where numObjects can be retrieved with getNumObjects. Returns Returns an object ID, or 0 if no object is found at the specified index. See Also getNumObjects

166

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.75. TriggerData
Datablock associated with the trigger mission object.

Fields
Field Name tickPeriodMS Description Period in milliseconds describing time to next trigger tick. Trigger checks for current contents on each tick. Does not affect sensing of onEnter/onLeave. Sample or Range [ 0 , inf )

A.2.76. TSShapeConstructor
Fields
Field Name baseShape sequence[0:126] Description The DTS file used by this shape constructor. Up to 127 sequences that can be associated with this shape constructor. Sample or Range ~/path/filename.dts ~/path/filename.dsq

A.2.77. TSStatic
Fields
Field Name position rotation scale shapeName Description The object's placement position, not necessarily the current position. The object's placement rotation, not necessarily the current rotation. The object's placement scale, not necessarily the current scale. DTS file for this shape Sample or Range "1.0 2.0 3.0" "1.0 2.0 3.0" "1.0 2.0 3.0" ~/path/filename.dts

A.2.78. Vehicle
Fields
Field Name disableMove Description Used to disable a vehicle's ability to move Sample or Range [ false , true ]

167

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.79. VehicleData
Fields
Field Name bodyFriction Description The vehicle's rigid body friction coefficient. Used in the physical simulation during contacts and collisions. Higher friction values dampen contact and collision forces. The vehicle's rigid body restitution. Used in the physical simulation during collisions. The scalar restitution value affects the strength of the body's rebound resulting from collisions with objects. Higher restitution values yield more powerful rebound reactions. Scalar rate at which the third person camera offset decays, per tick. Scalar amount by which the third person camera lags the vehicle, relative to the vehicle's linear velocity. The vertical offset of the vehicle's camera. Specifies whether the camera's rotation matrix, and the render eye transform are multiplied during camera updates. Sample or Range ( -inf.0 , inf.0 )

bodyRestitution

( -inf.0 , inf.0 )

cameraDecay cameraLag cameraOffset cameraRoll collDamageMultiplier collDamageThresholdVel

( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) [ false , true ] ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 )

Note: this field currently has no tangible effect in the engine's simulation.. Note: this field currently has no tangible effect in the engine's simulation.
The minimum collision velocity required to trigger a full collision. Collisions with velocities less than the collisionTol value will be treated as collision contacts or constraints. The minimum collision velocity required to trigger a collision contact. Collisions with velocities less than the contactTol value will be treated as collision constraints. Array of pointers to ParticleEmitterData datablocks which will be used to emit particles for damage effects (smoke). Offset point at which to display damage effects. Array of floats specifying damage level thresholds. Each entry is specified as a decimal percentage of maxDamage (defined in ShapeBaseData). Each damage level is used to determine what damage effect to play. Array of pointers to ParticleEmitterData datablocks which will be used to emit particles at vehicle/terrain contact point. Height of dust effects. The AudioProfile will be used to produce sounds when emerging from water. The minimum velocity at which the exit splash sound will be played when emerging from water. The AudioProfile used to produce sounds for hard impacts. Minimum speed at which the vehicle must be traveling for the hard impact sound to be played. The minimum velocity at which the hard splash sound will be played when impacting water. The AudioProfile will be used to produce sounds when a soft impact with water occurs.

collisionTol

( -inf.0 , inf.0 )

contactTol damageEmitter[0] damageEmitter[1] damageEmitter[2] damageEmitterOffset[0] damageEmitterOffset[1] damageLevelTolerance[0] damageLevelTolerance[1] dustEmitter dustHeight exitingWater exitSplashSoundVelocity hardImpactSound hardImpactSpeed hardSplashSoundVelocity impactWaterEasy

( -inf.0 , inf.0 )

see type "1.0 2.0 3.0"

( -inf.0 , inf.0 )

see type ( -inf.0 , inf.0 ) see type ( -inf.0 , inf.0 ) see type ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) see type

168

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name impactWaterHard impactWaterMedium integration jetEnergyDrain jetForce massCenter maxDrag maxSteeringAngle mediumSplashSoundVelocity

Description The AudioProfile will be used to produce sounds when a hard impact with water occurs. The AudioProfile will be used to produce sounds when a medium impact with water occurs. The number of discrete steps with which to process physics data per tick. Energy drained per tick by use of the vehicle's jet, if it has one. Force generated by the vehicle's jet, if it has one. This field is only used by derived classes. The vehicle's rigid body center of mass. Intended to clamp the maximum drag available. Note: this field currently has no tangible effect in the engine's simulation. Maximum attainable steering angle, measured in radians. Steering angles are clamped to this maximum value. The minimum velocity at which the medium splash sound will be played when impacting water. The minimum drag acting on the vehicle at all times. At present, this field is only used by FlyingVehicleData and helps determine it's maxSpeed and movement force. Minimum speed at which the vehicle must be traveling for the OnImpact script function to be called. Minimum energy required in order to use the vehicle's jet, if it has one.

Sample or Range see type see type ( -inf , inf ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) "1.0 2.0 3.0" -( -inf.0 , inf.0 ) ( -inf.0 , inf.0 )

minDrag minImpactSpeed minJetEnergy minRollSpeed numDmgEmitterAreas softImpactSound softImpactSpeed softSplashSoundVelocity splashEmitter[0] splashEmitter[1] splashFreqMod splashVelEpsilon triggerDustHeight waterWakeSound

( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) -( -inf.0 , inf.0 ) see type ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) see type

Note: this field currently has no tangible effect in the engine's simulation.
The number of areas on the vehicle that can display damage effects. The AudioProfile used to produce sounds for soft impacts. Minimum speed at which the vehicle must be traveling for the soft impact sound to be played. The minimum velocity at which the soft splash sound will be played when impacting water. Array of pointers to ParticleEmitterData datablocks which will generate splash effects. The simulated frequency modulation of a splash generated by this vehicle. Multiplied along with vehicle speed and time elapsed when determining splash emission rate. The threshold speed at which we consider the vehicle's movement to have stopped when updating splash effects. Maximum height from the ground at which the vehicle will generate dust. The AudioProfile will be used to produce sounds when a water wake is displayed.

( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) ( -inf.0 , inf.0 ) see type

169

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.80. WaterBlock
Fields
Field Name density Description The default water density is one. Meanwhile, the default character density is 10. This means the character will sink upon entering the water. Therefore, if you want the character to be more buoyant, you can adjust either or both parameters. Controls the slope between MinAlpha and MaxAlpha. In older versions of the engine, Melvin implemented this as a sigmoid function, but since version 1.2, it has been implemented using the (more involved) gammacorrection function. This allows you to adjust distortion such that the effect is the same between a large water block and a small water block. There are not set rules really. Youll just have to experiment. If this vale is not zero, distortion is enabled. Generally, the magnitude of this value should be less than one or the distortion behavesstrangely. Both positive and negative values are legal. As you might guess, this period of the distort function. It is inversely proportional to the distortions rate of change. In other words, larger values mean slower distortions and smaller values mean faster distortions. A value of zero (0) is illegal and will cause the texture rendering to fail gracefully. description If environmental mapping (see .Reflections and Specular Masks below) is enabled, this texture is rendered when looking down onto the water from above. This represents an environmental reflection on the waters surface. As with envMapOverTexture, this represents an environmental reflection, but this is the texture you will see if looking up from beneath the water. This parameter (in degrees) determines the direction of the translation. If this value is non-zero, water flow will be enabled. The higher the value, the more quickly textures will translate. These are leftover values from Tribes 2. You may use them for your own purposes. As might be intuited, this parameter determine the maximum alpha to use while rendering shoreTexture. This directly affects the multi-texturing equation involving the surfaceTexture and shoreTexture. As might be intuited, this parameter determine the minimum alpha to use while rendering shoreTexture. This directly affects the multi-texturing equation involving the surfaceTexture and shoreTexture. The object's placement position, not necessarily the current position. Setting this value true, tells the engine to (attempt to) clip the edges of water that protrude from beneath terrain features. Results will vary when using this feature. The object's placement rotation, not necessarily the current rotation. The object's placement scale, not necessarily the current scale. Sample or Range [ 0.0 , inf.0 )

DepthGradient

[ 0.0 , inf.0 )

DistortGridScale

[ 0.0 , inf.0 )

DistortMag

[ 0.0 , inf.0 )

DistortTime

[ 0.0 , inf.0 )

envMapIntensity envMapOverTexture envMapUnderTexture FlowAngle FlowRate

[ 0.0 , inf.0 ) ~/path/filename.png ~/path/filename.png [ 0.0 , 360.0 ) [ 0.0 , inf.0 ) Water, OceanWater, RiverWater, StagnantWater, Lava, HotLava, CrustyLava, Quicksand [ 0.0 , 1.0 ]

liquidType

MaxAlpha

MinAlpha position removeWetEdges rotation scale

[ 0.0 , 1.0 ] "1.0 2.0 3.0" [ false , true ] "1.0 2.0 3.0" "1.0 2.0 3.0"

170

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name

Description Shore rendering is determined by a ray-cast at distinct points across the surface of the water block. The result of this ray-cast returns the distance between the top of the water and the terrain directly below that point on the surface. If this value is greater than or equal to ShoreDepth, the engine is instructed to render the shoreTexture. If you choose to set this value to zero, the shoreTexture will not render at all. Well talk more about shorelines in a moment, but Torque has the ability to render shorelines differently. When it renders the shoreline, it blends this texture with surfaceTexture, giving a nice visual effect. This can be used to change both the color of the resultant highlight and its intensity. This parameter takes a 4-tuple floating-point vector r g b a. This texture is used to make the surface of the water look as if it is reflecting light. Again, this should be some kind of caustic grayscale. The engine does take into account the position and elevation of the sun when rendering the specular effect. This determines how large an area is shiny. Lower values cause more of the specular map to be rendered, versus larger values that will tend to show just a spot of highlighting. These two textures are only used when liquidType is one of the Lava types (Lava, HotLava, or CrustyLava). These two textures are rendered perpendicular to the viewing plane. Additionally they are animated. A suggestion I was given, which Ill pass along, is to use two high quality (say 512x512 instead of the normal 256x256) grayscale caustics for these. This affects how opaque the combination of surfaceTexture and shoreTexture is. That is it. A value of zero is not transparent, just very translucent. A value of one is quite opaque. Youll have to adjust this meet your needs. When FlowRate is non-zero, the flow-rate of the oriented surfaceTexture is controlled by this value as follows: > 1 - Non-oriented surfaceTexture flows more slowly than oriented surfaceTexture.

Sample or Range

ShoreDepth

[ 0.0 , inf.0 )

ShoreTexture specularColor

~/path/filename.png "1.0 0.5 0.5"

specularMaskTex

~/path/filename.png

specularPower

[ 0 , inf )

submergeTexture[0] submergeTexture[1]

~/path/filename.png

surfaceOpacity

[ 0.0 , 1.0 ]

SurfaceParallax

1 - Non-oriented surfaceTexture and oriented surfaceTexture flow at same rate. < 1 - Oriented surfaceTexture flows more slowly than non-oriented surfaceTexture. Oriented surfaceTexture counter-flows. 0 - Oriented surfaceTexture remains stationary.

[ 0.0 , inf.0 )

surfaceTexture TessShore TessSurface tile UseDepthMask

This texture is used to define the base water layer(s). This texture is rendered in two layers, with one layer re-oriented at a 45-degree angle (about Z of course). This makes the water more interesting. Controls shore 'detail level'. Controls surface 'detail level' Enable/disable tiling. If this value is false, only the envMapOverTexture will be rendered on the top of the water. All other surface textures will be disabled.

~/path/filename.png [ 0 , inf ) [ 0 , inf ) [ false , true ] [ false , true ]

171

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name

Description In addition to choosing whether a character will float or sink in water, we can indirectly adjust how quickly this occurs by changing the viscosity of the water. A thicker fluid like, say honey, has a high viscosity, whereas plain water will have a low viscosity. By increasing this value, you create an effect where the player will require more time to float or sink. description

Sample or Range

viscosity

( -inf.0 , inf.0 )

waveMagnitude

( -inf.0 , inf.0 )

A.2.81. WheeledVehicle
Class used to represent wheeled vehicles.

Console Methods
getwheelCount setWheelTire setWheelPowered setWheelSpring setWheelSteering

getWheelCount() Purpose Use the getWheelCount method to determine how many wheels (hubs) this wheeledVehicle has in it's mesh. Returns Returns a value between 0 and 8, specifying the number of hubs the mesh has of the name: hub0, hub1, ... hub7. Notes When creating a wheeledVehicle mesh, always create hubs using the name hubX, where X is a value between 0 and 7. Furthermore, always create these hubs in order and in pairs (opposite each other). For example, If you are creating a four-wheeled vehicle, the hubs should be: hub0 hub1 hub2 hub3 front left tire front right tire rear left tire rear right tire

See Also setWheelPowered, setWheelSpring, setWheelSteering, setWheelTire

172

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setWheelPowered( wheelNum , isPowered ) Purpose Use the setWheelPowered method to set the powered state for the specified wheel (hub). Syntax wheelNum A value between 0 and 7, specifying a specific wheel/hub. isPowered A boolean value. If set to true, this wheel will contribute to the powered motion of the vehicle, otherwise it will only affect nonpowered motion such as rolling and steering. Returns Returns true if wheelNum was successfully set to powered or un-powered. false if wheelNum does not specify a valid hub. Notes By default, all wheels/hubs are powered. See Also getWheelCount setWheelSpring( wheelNum , springDB ) Purpose Use the setWheelSpring method to assign a WheeledVehicleSpring datablock to the specified hub. Syntax wheelNum A value between 0 and 7, specifying a specific wheel/hub. springDB The name of ID of a previously specified WheeledVehicleSpring datablock. Returns Returns true if wheelNum was successfully assigned a new spring datablock, otherwise returns false. Notes You may change the springs on a wheeled tire at any time, allowing you to enhance vehicles or make them subject to damage. Each tire can have its own customized spring datablock. See Also getWheelCount Will return

173

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setWheelSteering( wheelNum, steerAngle ) Purpose Use the setWheelSteering method to set that maximum steering angle (away from center) as steerAngle (radians) for wheel wheelNum. This limits the turning angle for the vehicle. Syntax wheelNum A value between 0 and 7, specifying a specific wheel/hub. steerAngle An angular value between 0.0 and 1.57 radians specifying maximum turning angle this wheel can take. Returns Returns true if wheelNum was successfully set the requested steerAngle, otherwise returns false. See Also getwheelCount setWheelTire( wheelNum , tireDB ) Purpose Use the setWheelTire method to assign a previously specified WheeledVehicleTire datablock to the requested hub. Syntax wheelNum A value between 0 and 7, specifying a specific wheel/hub. tireDB The name of ID of a previously specified WheeledVehicleTire datablock. Returns Returns true if wheelNum was successfully assigned a new WheeledVehicleTire datablock. Notes Tires may be changed at any time during the game. See Also getWheelCount

174

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.2.82. WheeledVehicleData
Datablock associated with wheeledvehicle object class.

Fields
Field Name engineBrake engineSound engineTorque jetSound maxWheelSpeed squealSound tireEmitter wheelImpactSound brakeTorque Description Floating-point value specifying how much the engine brakes when the engine is not engaged (i.e. when the forward key is pressed). AudioProfile specifying sound to play when engine is engaged. Floating-point value specifying the power of the engine. AudioProfile specifying sound to play when jets are engaged. Floating-point value specifying maximum rotational velocity for tires. AudioProfile specifying sound to play when tires break friction. ParticleEmitterData datablock for tire emitters. AudioProfile specifying sound to play when tires impact 'ground'. Floating-point value specifying the power of the brakes. Sample or Range ----------

A.2.83. WheeledVehicleSpring
Datablock used to describe vehicle suspension(s).

Fields
Field Name antiSwayForce damping force length Description Force which acts to dampen lateral sway introduced when wheels opposite each other are extended at different lengths. Dampening force which counter-acts the spring's force. The force of the spring. Spring forces act straight up and are applied at the spring's root position, which is defined in the vehicle's shape. The length of suspension travel from the root position. Sample or Range [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 ) [ 0.0 , inf.0 )

A.2.84. WheeledVehicleTire
Summary of object.

Fields
Field Name kineticFriction Description Used in the physical simulation to represent the tire's surface friction when it is slipping (has no traction). Measures the dampening force applied against lateral forces generated by the tire. See the lateralForce field documentation for more information on vehicle physics as they relate to wheel forces. Sample or Range [ 0.0 , inf.0 )

lateralDamping

[ 0.0 , inf.0 )

175

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name

Description Used in the physical simulation to represent the tire's lateral force. Lateral force can in simple terms be considered left/right steering force. WheeledVehicles are acted upon by forces generated by their tires and the lateralForce measures the magnitude of the force exerted on the vehicle when the tires are deformed along the x-axis. With real wheeled vehicles, tires are constantly being deformed and it is the interplay of deformation forces which determines how a vehicle moves. In Torque's simulation of vehicle physics, tire deformation obviously can't be handled with absolute realism, but the interplay of a vehicle's velocity, its engine's torque and braking forces, and its wheels' friction, lateral deformation, lateralDamping, lateralRelaxation, longitudinal deformation, longitudinalDamping, and longitudinalRelaxation forces, along with its wheels' angular velocity are combined to create a robust real-time physical simulation. For this field, the larger the value supplied for the lateralForce, the larger the effect steering moves can have. In Torque tire forces are applied at a vehicle's wheel hubs. Measures the relaxing force applied against lateral forces generated by the tire. The lateralRelaxation force measures how strongly the tire effectively un-deforms. See the lateralForce field documentation for more information on vehicle physics as they relate to wheel forces. Measures the relaxing force applied against longitudinal forces generated by the tire. The longitudinalRelaxation force measures how strongly the tire effectively un-deforms. See the longitudinalForce field documentation for more information on longitudinal tire forces, and the laterForce field documentation for more information on general vehicle physics as they relate to wheel forces. Measures the dampening force applied against longitudinal forces generated by the tire. See the longitudinalForce field documentation for more information on longitudinal tire forces, and the laterForce field documentation for more information on general vehicle physics as they relate to wheel forces. Used in the physical simulation to represent the tire's longitudinal force. Longitudinal force can in simple terms be considered forward/backward movement force. WheeledVehicles are acted upon by forces generated by their tires and the longitudinalForce measures the magnitude of the force exerted on the vehicle when the tires are deformed along the y-axis. See the lateralForce field documentation for more information on wheeled vehicle physics. For this field, the larger the value supplied for the longitudinalForce, the larger the effect acceleration/deceleration moves can have. The mass of the entire wheel. Used in the physics simulation, see the documentation for the WheeledVehicleData datablock for more information. The wheel's mass does not need to be specified in script. The tire's radius. The radius is determined from the bounding box of the shape provided in the shapefile field, and does not need to be specified in script. The tire should be built with it's hub axis along the object's Y-axis.

Sample or Range

lateralForce

[ 0.0 , inf.0 )

lateralRelaxation

[ 0.0 , inf.0 )

logitudinalRelaxation (yes, an engine typo)

[ 0.0 , inf.0 )

longitudinalDamping

[ 0.0 , inf.0 )

longitudinalForce

[ 0.0 , inf.0 )

mass

[ 0.0 , inf.0 )

radius

[ 0.0 , inf.0 )

176

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name restitution

Description

Sample or Range --

Note: this field currently has no tangible effect in the engine's simulation.
The path and file name of a shape file to be used for the wheel. Must adhere to the semantics associated with the Filename datatype, as defined in the engine. Used in the physical simulation to represent the tire's surface friction when it is not slipping (has traction).

shapeFile staticFriction

~/path/filename.dts [ 0.0 , inf.0 )

177

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3 Console Functions Quick Reference


A.3.1. OpenAL
The following functions are for the most part wrappers on OpenAL functions. Many of these functions use enumerated values. The following table includes the string equivalents to the OpenAL enums as well as information on flags associated with them:
ALEnum AL_CONE_INNER_ANGLE AL_CONE_OUTER_ANGLE AL_CONE_OUTER_GAIN AL_DIRECTION AL_EXTENSIONS AL_GAIN_LINEAR AL_GAIN AL_LOOPING AL_MAX_DISTANCE AL_ORIENTATION AL_PITCH AL_POSITION AL_REFERENCE_DISTANCE AL_RENDERER AL_VELOCITY AL_VENDOR AL_VERSION OpenAL Enum (C++) AL_CONE_INNER_ANGLE AL_CONE_OUTER_ANGLE AL_CONE_OUTER_GAIN AL_DIRECTION AL_EXTENSIONS AL_GAIN_LINEAR AL_GAIN AL_LOOPING AL_MAX_DISTANCE AL_ORIENTATION AL_PITCH AL_POSITION AL_REFERENCE_DISTANCE AL_RENDERER AL_VELOCITY AL_VENDOR AL_VERSION Flags (Source|Get|Set|Int) (Source|Get|Set|Int) (Source|Get|Set|Float) (Source|Get|Set|Float3) (Context|Get) (Source|Listener|Get|Set|Float) (Source|Listener|Get|Set|Float) (Source|Get|Set|Int) (Source|Get|Set|Float) (Listener|Set|Float6) (Source|Get|Set|Float) (Source|Listener|Get|Set|Float3) (Source|Get|Set|Float) (Context|Get) (Source|Listener|Get|Set|Float3) (Context|Get) (Context|Get)

alxGetChannelVolume( channelID ) Purpose Use the alxGetChannelVolume function to get the volume setting for a specified channel. Syntax channelID An integer value, equal to or greater than 0, corresponding to a valid audio channel. Returns Returns volume [ 0.0, 1.0 ] for channel specified by channelID. See Also alxSetChannelVolume

178

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

alxGetListenerf( ALEnum ) alGetListener3f( ALEnum ) alGetListeneri( ALEnum ) Purpose Use the al*GetListener* function to get the current value of a listener parameter, as specified by ALEnum. Syntax ALEnum A string containing an OpenAL enumerated type name. values for legal values. See (above) table of ALEnum

Returns Returns a float (alxGetListenerf), a vector of three floats (alGetListener3f), or an integer value respectively (alGetListeneri). Notes Depending on the ALEnum you need to acquire, be sure to use the correct version (i.e. correct return type) of al*GetListener*. See Also alxGetSource*

alxGetSourcef( handle , ALEnum ) alxGetSourcei( handle , ALEnum ) alxGetSource3f( handle , ALEnum ) Purpose Use the alxGetSource* function to get the current value of a source parameter, as specified by ALEnum. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. ALEnum A string containing an OpenAL enumerated type name. See (above) table of ALEnum values for legal values. Returns Returns current value of parameter specified by ALEnum for source identified by handle. Notes Depending on the ALEnum you need to acquire, be sure to use the correct version (i.e. correct return type) of alxGetSource*. See Also alxSource*, al*GetListener*

179

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

alxGetStreamDuration( handle ) Purpose Use the alxGetStreamDuration function to determine the length of a previously set up sound in seconds. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. Returns Returns -1 for invalid handle, and 0.0 to N.M for valid handle indicating length of scheduled sound in seconds. See Also alxGetStreamPosition

alxGetStreamPosition( handle ) Purpose Use the alxGetStreamPosition function to get the current play position for a playing sound. Note, this value is a percentage equivalent to the percent of the sound that as already played. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. Returns Returns -1 for invalid handle, and 0.0 to 1.0 for valid handle indicating what percentage of the sound file has been played. See Also alxGetStreamDuration

alGetString( ALEnum ) Purpose Use the alGetString function to get the string equivalent to the specified OpenAL enumerated value. Syntax ALEnum A string containing an OpenAL enumerated type name. values for legal values. Returns Returns a string corresponding to the passed ALEnum. See (above) table of ALEnum

180

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

alGetWaveLen( fileName ) Purpose Use the alGetWaveLen function to get the play-length of a specified sound file in milliseconds. Syntax fileName A full path to legally formatted sound file. Returns Returns play-length of the WAV file specified by filename in milliseconds. See Also alxGetStreamDuration, alxGetStreamPosition alListener3f( ALEnum , x , y , z) alxListenerf( AlEnum , value ) Purpose Use the al*Listener* function to set a listener parameter(s) as specified by the OpenAL enumerated type ALEnum. Syntax ALEnum A string containing an OpenAL enumerated type name. See (above) table of ALEnum values for legal values. x,y,z XYZ floating-point coordinates. value An ALEnum type specific value corresponding to the new value for this enumerated parameters. Returns No return value. See Also al*GetListener*, alxSource*

alxIsPlaying( handle ) Purpose Use the alxIsPlaying function to determine if the sound associated with a previously setup sound handle is playing or not. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. Returns Returns 1 if specified handle is being played, 0 otherwise. See Also alxPlay, alxStop, alxStopAll

181

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

alxPlay( handle ) alxPlay( profile ) alxPlay( profile , x , y , z ) Purpose Use the alxPlay function to start playing a sound specified by either a previously set up sound (handle), or a previously defined audio profile. For 3D sounds, you must specify a XYZ coordinate for the source to play at. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. profile The ID (a non-negative integer) corresponding to a previously set up audio profile. x,y,z XYZ floating-point coordinates. Returns Returns handle to playing sound, or 0 on failure. See Also alxIsPlaying, alxStop, alxStopAll

alxSetChannelVolume( channelD , volume ) Purpose Use the alxSetChannelVolume function to set a volume [ 0.0, 1.0 ] for the channel specified by channelID. Syntax channelID An integer value, equal to or greater than 0, corresponding to a valid audio channel. volume A value between 0.0 and 1.0 specifying the new volume for the specified channel. Returns Returns true on success and false on failure. See Also alxGetChannelVolume

182

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

alxSourcef( handle , ALEnum , value ) alxSourcei( handle , ALEenum , value ) alxSource3f( handle , ALEnum , x , y , z ) Purpose Use the alxSource* function to set a source parameter(s) as specified by the OpenAL enumerated type ALEnum. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. ALEnum A string containing an OpenAL enumerated type name. See (above) table of ALEnum values for legal values. value An ALEnum type specific value corresponding to the new value for this enumerated parameters. x,y,z XYZ floating-point coordinates. Returns No return value. See Also alxGetSource*, al*Listener*

alxStop( handle ) Purpose Use the alxStop function to stop a currently playing sound as specified by handle. Syntax handle The ID (a non-negative integer) corresponding to a previously set up sound source. Returns No return value. See Also alxIsPlaying, alxPlay, alxStopAll

alxStopAll() Purpose Use the alxStopAll function to stop all currently playing sounds associated with registered handles. Returns No return. See Also alxIsPlaying, alxPlay, alxStop

183

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

OpenALInitDriver() Purpose Use the OpenALInitDriver function to initialize the OpenAL driver. Returns Returns true on successful initialization, false otherwise. Notes This must be done before all other OpenAL operations. See Also OpenALShutdownDriver

OpenALShutdownDriver() Purpose Use the OpenALShutdownDriver function to stop/shut down the OpenAL driver. Returns No return value. Notes After this is called, you must restart the driver with OpenALInitDriver to execute any new sound operations. See Also OpenALInitDriver

A.3.2. Debugging
This category of console functions includes functions used to debug or to otherwise examine the scripting environment, the 3D world, engine performance, et cetera.

General
debug() Purpose Use the debug function to cause the engine to issue a debug break and to break into an active debugger. Returns No return value. Notes For this to work, the engine must have been compiled with either TORQUE_DEBUG, or

INTERNAL_RELEASE defined.

184

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

dumpConsoleClasses() Purpose Use the dumpConsoleClasses function to prints all registered classes and the console methods associated with them to the console. Returns No return value. Notes This will dump all classes and methods that were registered from within the engine, AND from the console via scripts. See Also dumpConsoleFunctions dumpConsoleFunctions() Purpose Use the dumpConsoleFunctions function to prints all registered functions to the console. Returns No return value. Notes This will dump all funtions that were registered from within the engine, AND from the console via scripts. See Also dumpConsoleMethods

setEchoFileLoads( enable ) Purpose Use the setEchoFileLoads function to enable/disable echoing of file loads (to console). Syntax enable A boolean value. If this value is true, extra information will be dumped to the console when files are loaded. Notes This does not completely disable message, but rather adds additional methods when echoing is set to true. File loads will always echo a compile statement if compiling is required, and an exec statement at all times.

Interiors
setInteriorRenderMode( mode ) Purpose Use the setInteriorRenderMode function to enable various interior rendering modes used for mesh debugging. Syntax

185

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mode A numeric value between 0 and 16. table below. Returns

Please see the 'Interior Render Modes'

No return value.
Notes For this to work, the engine must have been compiled with TORQUE_DEBUG defined.

See Also

GLEnableOutline
Render Mode 0 1 2 3 4 5 6 7 8 9 10 Mode Name Normal Render Lines Detail Ambiguous Orphan Light Map Textures Only Portal Zones Outside Visible Collision Fans Strips Normal. Render interior brush outlines only. Render interior brushes with flat coloration. White colored blocks indicate brushes that do not change with LOD changes. Red colored blocks will change. Shows ambiguous polygons. (Good models have none.) Shows orphaned polygons. (Good models have none.) Shows lightmaps on flat (white) shaded model. Shows textures without lightmaps. Colorizes portalized zones to make them distinct and easily identifiable. Marks insides of interiors as white and outsides as red. Tip: An interiors with no portals is marked as all RED. Displays calculated (by exporter) collision fans with axes showing face directions. Shows surfaces divided into colorized triangle strips. Each strip has a distinct color from adjacent strips. Tip: Large triangles generally give best performance, but strips can be too large in some instance. Renders all faces with NULL texture applied as RED. Tip: Excluding portals, no red surfaces should be visible without taking the camera into walls, or you will have a hole/gap in your surface. All textures large textures will be rendered with a colorized shading: 12 Large Textures Blue Width or Height Equal to 256 pixels Green Width or Height Equal to 512 pixels Red Width or Height Equal to greater than pixels Renders HULL surfaces with distinct flat colors. Renders specialized Vechicle HULL surfaces with distinct flat colors. -- Currently Unavailable -Renders entire interior at current LOD coloration. (See LOD colors below). Meaning

11

NULL Surfaces

13 14 15 16

Hull Surfaces Vechile Hull Surfaces Vertex Colors Detail Levels

186

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Level Of Deail (LOD) 0 1 2 3 4 ...

Debug Color White Blue Greeen Red Yellow Please see source code.

Journalling
playJournal( namedFile , doBreak ) Purpose Use the playJournal function to play back a journal from namedFile and to optionally break (into an active debugger) after loading the Journal. This allow us to debug engine bugs by reproducing them consistently repeatedly. Syntax namedFile A full path to a valid journal file. Usually, journal names end with the extension .jrn. doBreak A boolean value. If true, the engine will load the journal and then assert a break (to break into an active debugger). If not true, the engine will play back the journal with no break. Returns No return value. Notes The journaling system is a vital tool for debugging complex or hard to reproduce engine and script bugs. See Also saveJournal playJournal( playJournal( "~/myJournal.jrn" "~/myJournal.jrn" , true ); // Break after loading journal.

); // Just play journal

187

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

saveJournal( namedFile ) Purpose Use the saveJournal function to save a new journal of the current game. Syntax namedFile A full path specifying the file to save this journal to. Usually, journal names end with the extension .jrn. Returns No return value. See Also playJournal saveJournal( "~/myJournal.jrn" );

Logging
inputLog( fileName ) Purpose Use the inputLog function to enables/disable logging of DirectInput events to a log file specified by fileName. Syntax fileName A valid path to a file in which to store a log of DirectInput events. Returns For this to work, the engine must have been compiled with LOG_INPUT defined. Notes Once started, input logging cannot be stopped, so only apply this in debug scenarios. See Also setLogMode inputLog( "DirectInput.log");

188

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setLogMode( mode ) Purpose Use the setLogMode function to set the logging level based on bits that are set in the mode argument. Syntax mode A bitmask enabling various types of logging. Returns No return value. Notes This is a general debug method and should be used in all but release cases and perhaps even then. See Also intputLog SetLogMode( 6 ); // Dump console output before this to file and leave file open. See 'Logging Modes' table below.

mode bitmasks 1

Description Open file and append. Close file on each log write. This allows us to edit the file in a separate editor without having to quit and without getting a file lock conflict. Open file and leave it open. This is more efficient for lots of logging, but we may not be able to view the file till we exit the game. (Note: *NIX users can just tail the file: 'tail -fn 100 filename') Dump anything that has been printed to the console so far. This is needed because the console doesn't get turned on right away, and some output would otherwise be missed.

Memory
dumpUnflaggedAllocs( fileName ) Purpose Use the dumpUnflaggedAllocs function to dump all allocations that were made subsequent to a call to flagCurrentAllocs. This function, in association with flagCurrentAllocs, is used for detecting memory leaks and analyzing memory usage in general. Syntax filename A valid path and filename in which to dump the current memory allocation information. Returns No return value. Notes For this to work, the engine must have been compiled with DEBUG_GUARD defined. See Also flagCurrentAllocs

189

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

flagCurrentAllocs() Purpose Use the flagCurrentAllocs function to mark all current memory allocations in preparation for a subsequent call or calls to dumpUnflaggedAllocs. This function, in association with dumpUnflaggedAllocs, is used for detecting memory leaks and analyzing memory usage in general. Returns No return value. Notes

For this to work, the engine must have been compiled with DEBUG_GUARD defined.
See Also dumpUnflaggedAllocs freeMemoryDump() Purpose Use the freeMemoryDump function to dump the current 'memory free' statistics to the console. Returns No return value.

This does not print how much memory is free, but rather an analysis of 'free chunks' of memory.

Notes

Metrics
GLEnableLogging( enable ) Purpose Use the GLEnableLogging function to enable/disable the gathering of OpenGL metrics. Syntax enable A boolean value. If set to true, the engine will gather various OpenGL metrics and dump them to a file named gl_log.txt. If set to false, logging is stopped, the last writes to the log are flushed, and the file is closed. Returns No return value. Notes For this to work, the engine must have been compiled with either TORQUE_DEBUG or

INTERNAL_RELEASE defined. Always be sure to do a disable after an enable to flush the last log data to the log file.
See Also GLEnableMetrics, metrics

190

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GLEnableMetrics( enable ) Purpose Use the GLEnableMetrics function to enable or disable logging of OpenGL texture and video metrics. Syntax enable A boolean value. When this is set to true, texture and video (triangles and primitives) logging is enabled and dumped as part of calls to certain metrics. Returns No return value.

For this to work, the engine must have been compiled with either TORQUE_DEBUG or INTERNAL_RELEASE defined. Use the metrics function to get at this information. Also, once this feature is enabled, the following globals will be available for inspection/examination: OpenGL::triCount0 OpenGL::triCount1 OpenGL::triCount2 OpenGL::triCount3 OpenGL::primCount0 OpenGL::primCount1 OpenGL::primCount2 OpenGL::primCount3 Terrain triangles DIF triangles DTS triangles Uncategorized triangles Terrain primitives DIF primitives DTS primitives Uncategorized primitives

Notes

See Also GLEnableLogging, metrics metrics( metric ) Purpose Use the metrics function to enable a display of specified metric information in upper left corner of screen. Syntax metric The class of metrics to display. Please see the 'metric' table below for specific metrics. Returns No return value. Notes For this to work, the engine must have been compiled with TORQUE_DEBUG defined. See Also GLEnableMetrics

191

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

metric fps metrics + OH: Open Handles OLH: Open Looping Handles AS: Active Streams NAS: Null Active Streams LAS: Active Looping Streams LS: Total Looping Streams ILS: Inactive Looping Streams CLS: Culled Looping Streams

Measures

audio

debug

fps metrics + NTL: Texels Loaded. TRP: Percentage of resident memory (on card) used by textures. NP: Number of primitives being rendered. NT: Number of textures in use. NO: Number of objects being rendered. fps metrics + NTL: Texels Loaded. TRP: Percentage of resident memory (on card) used by textures. INP: Number of primitives being rendered, for interiors only. INT: Number of textures in use, for interiors only. INO: Number of objects being rendered, for interiors only. FPS: Frames Per Second mspf: Milliseconds per frame fps metrics + Sim Time: Sim time to date. Time since engine started. Mod: Ticks since engine started. fps metrics + L0: Numer of terrain blocks rendering at level zero. FMC: Full mipmap count. DTC: Dynamic texture count. UNU: Unused texture count. STC: Static texture count. DTSU: Dynamic texture space used. STSU: Static texture space used. FRB: Terrain blocks not rendered due to full fogging. fps metrics + NTL: Number of texels loaded. TRP: Percentage of resident memory (on card) used by textures. TCM: Texture cache misses. fps metrics + TC: Total triangle count. PC: Total primitive count. T_T: Terrain triangle count. T_P: Terrain primitive count. I_T: Interiors triangle count. I_P: Interiors primitive count. TS_T: Shape (DTS) triangle count. TS_P: Shape (DTS) primitive count. ?_T: Uncategorized triangle count. ?_P: Uncategorized primitive count. fps metrics + R: Integration retry count. C: Search count. P: Polygon count for vehicles. V: Vertex count for vehicles.

interior

fps

time

terrain

texture Requires: GLEnableMetrics(true);

video Requires: GLEnableMetrics(true);

vehicle

192

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

metric water fps metrics + Tri#: Water triangle count. Pnt#: Water point (vertex) count. Hz#: Water haze point count.

Measures

193

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Networking
dbgSetParameters ( port , password ) Purpose Use the dbgSetParameters function to set the debug connection password for a specific port. Syntax port The IP port to set the password on. password The password for this port. Set this to a NULL string to clear the password for the port. Returns No return value. dbgSetParameters( 1130 , edochi ); dNetSetLogging( enable ) Purpose Use the dNetSetLogging function to enable (or disable) network packet logging to the console. Syntax enable A boolean value. If set to true, network packet logging is enabled, otherwise it is disabled. Returns No return value. dnetSetLogging(1);

194

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Profiling
profilerDump() Purpose Use the profilerDump function to dump engine profile statistics to the console. Returns No return value. Used to dump NetStringTable statistics to the console profilerDump(); profilerDumpToFile( filename ) Purpose Use the profilerDumpToFile function to dump engine profile statistics to a file. Syntax filename A string value specifying a full or partial path to a file for writing the profiler statistics to. Returns No return value. profilerEnable( enable ) Purpose Use the profileEnable function to enable (or disable) engine profiling. Syntax enable A boolean value. If set to true and the engine was compiled with DEBUG specified, engine profiling is enabled, otherwise it is disabled. Returns No return value. Note TGE has predefined profiling areas surrounded by markers, but you may need to define additional markers (in C++) around areas you wish to profile, by using the PROFILE_START( markerName ); and PROFILE_END(); MACROS. Please read GPGTGE Volume 2 to learn more about this. profilerEnable(false);

195

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

profilerMarkerEnable( markerName , enable

Purpose Use the profilerMarkerEnable function to enable (or disable) specific profiling markers. Syntax markerName The name of a marker (as specified using the C++ MACRO PROFILER_START) to be enabled/disabled. enable A boolean value. If set to true, the specified marker will be enabled for profiling, otherwise it will be disabled. Returns No return value. profilerMarkerEnable( mark , true );

profilerReset( ) Purpose Use the profilerReset function to reset the profile gathering mechanism. This clears the current counters, but all markers retain their current enable/disable status. Returns No return value.

Tracing
backtrace() Purpose Use the backtrace function to print the current callstack to the console. This is used to trace functions called from withing functions and can help discover what functions were called (and not yet exited) before the current point in your scripts. Returns No return value. trace( enable ) Purpose Use the trace function to enable will print a message every time a and it will print a message every last value of last statement) for Syntax enable A boolean value. disabled. Returns No return value. (or disable) function call tracing. If enabled, tracing function is entered, showing what arguments it received, time a function is exited, showing the return value (or that function.

If set to true, tracing is enabled, otherwise it is

196

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3.3. String Manipulation


Bad Word Filtering
Torque includes a bad word filtering feature that can be used process strings and to replace any words matching words on the list of known bad works. This list is build manually. Bad words are replaced with a user-defined set of random characters. Please note, a small but common list of bad words is already included in the engine. Please refer to the engine source code for this list as a would-be tasteless the list of words here.
addBadWord( aBadWord ) Purpose Use the addBadWord function to add new 'bad words' to the current 'bad word' list. Once a word is added to the list, it may not be removed. Also, adding a word more than once has no additional effect. Syntax aBadWord A word to be considered as foul language and to be looked for when the containsBadWords and/or filterString functions are called. Returns No return value. Notes Several bad words are already added by the engine. full listing. See Also containsBadWords, filterString Please refer to the source code for a

containsBadWords( string ) Purpose Use the containsBadWords function to check string for any previously specified 'bad words'. Syntax string Any string to be checked for bad words. Returns Returns true if any words on the bad word list are found in the string. otherwise. Returns false

Notes This function will catch whole words and variants as long as the variant contains the full spelling of the bad word. For example, if 'wack' where defined as a bad word, this function would return true if the word 'wacko' were included in a sentence. See Also addBadWord, filterString Returns true if string contains any known bad words.

197

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

string filterString( baseString [ , replacementChars ] ) Purpose Use the filterString function to parse baseString for words on the 'bad word' list and to then replace those words with random characters. Syntax baseString The original string, possibly containing bad words. replacementChars This optional argument can be used to supply a single character or a list of chracters to use for the replacement of characters the bad words. If no replacement characters are specified, the engine will choose randomly from lower-case alpha-characters (a..z). Returns Returns a 'cleaned' string. This string will contain all the original words, except that any words which were deemed 'bad' will have had each character in the word replaced with a random replacement character. Notes This function will catch whole words and variants as long as the variant contains the full spelling of the bad word. For example, if 'wack' where defined as a bad word, this function would return true if the word 'wacko' were included in a sentence. See Also addBadWord, containsBadWords

Bad Word Filtering Sample


function badWordTest() { addBadWord( "poop" ); %testPhrase = "This is the poop!"; %filteredPhrase = filterString( %testPhrase , "@#$*" ); echo("Contains bad words? ==> " , containsBadWords( %testPhrase ) ); echo("Before filtering: ", %testPhrase ); echo("After } badWordTest(); Contains bad words? ==> 1 Before filtering: This is the poop! After filtering: This is the $@@*! filtering: ", %filteredPhrase );

198

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Comparison
strcmp( string1 , string2 ) Purpose Use the strcmp function to do a lexicographic case sensitive string comparison between string1 and string2. Syntax string1 String to be compared to string2. string2 String to be compared to string1. Returns Returns a numeric value:

- 1 string1 is less than string2, including case. 0 string1 is equal to string2, including case. 1 string1 is greater than string2, including case.

See Also see stricmp, strstr

stricmp( string1 , string2 ) Purpose Use the stricmp function to do a lexicographic case in-sensitive string comparison between string1 and string2. Syntax string1 String to be compared to string2. string2 String to be compared to string1. Returns Returns a numeric value:

- 1 string1 is less than string2, ignoring case. 0 string1 is equal to string2, ignoring case. 1 string1 is greater than string2, ignoring case.

See Also see strcmp, strstr

199

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Comparison Sample
function strcmptest() { echo("Lexicographic comparisons are not the same as arithmetic comparisons..."); echo("100 - 10 == 90, but strcmp( \"100\" , \"10\" ) == " , strcmp( "100" , "10" ) ); echo("\n", "Don't forget about case-sensitivity..."); echo("strcmp( \"ABC\" , \"abc\" ) == " , strcmp( "ABC" , "abc" ) , "\n\n, but \n" ); echo("stricmp( \"ABC\" , \"abc\" ) == " , stricmp( "ABC" , "abc" ) );

strcmptest(); Lexicographic comparisons are not the same as arithmetic comparisons... 100 - 10 == 90, but strcmp( "100" , "10" ) == 1 Don't forget about case-sensitivity... strcmp( "ABC" , "abc" ) == -1 , but stricmp( "ABC" , "abc" ) == 0

Conversion
strlwr( sourceString ) Purpose Use the strlwr function to convert all alpha characters in sourceString to lower-case equivalents. Syntax sourceString The string to be modified. Returns Returns a copy of sourceString in which all upper-case characters have been converted to lower-case letters. See Also strupr echo( strlwr( "ABCD123" ) ); // Prints abcd123

200

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

strupr( sourceString ) Purpose Use the strupr function to convert all alpha characters in sourceString to upper-case equivalents. Syntax sourceString The string to be modified. Returns Returns a copy of sourceString in which all lower-case characters have been converted to upper-case letters. See Also strlwr echo( strlwr( "abcd123" ) ); // Prints ABCD123

Fields ( Newline or Tab Separated String)


A field is a sub-string withing a larger string, where each field is delimted by a NEWLINE character or a TAB character. A newline can be represented as either "\n" or the keyword NL, and a tab can be represented by hitting the TAB key, or by the keyword TAB.
getField( sourceString , index ) Purpose Use the getField function to get the field at index in sourceString. Syntax sourceString A string containing one or more fields. Returns Returns field at index in sourceString, or null string if no field exists at that index. See Also getFields, setField

// Prints Torque echo( getField( "GPGTGE + " NL "AND" TAB "Torque" TAB "Rocks" , 2 ) );

201

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getFieldCount( sourceString ) Purpose Use the getFieldCount function to get the number of fields in sourceString. Syntax sourceString A string containing one or more fields. Returns Returns number of fields in sourceString or 0 if no fields are present. %fields = "You" TAB "must" TAB "buy" TAB "GPGTGE"; echo( getFieldsCount( %fields ) ); // Prints 4 getFields( sourceString , index [ , endindex ] ) Purpose Use the getFields function to retrieve a set of fields from a sourceString. Syntax sourceString A string containing one or more fields. index The index of the first field to retrieve. endindex The index of the final field to retrieve. Returns Returns all fields (separated by current delimiter) from sourceString, starting at index and ending at endIndex or end of string, whichever comes first. If no endIndex is specified, all remaining fields are returned. See Also getField, setField // Prints Torque^Rocks (^ is printed TAB in console) echo( getFields( "GPGTGE + " NL "AND" TAB "Torque" TAB "Rocks" , 2 , 3 ) ); // Also prints Torque^Rocks echo( getFields( "GPGTGE + " NL "AND" TAB "Torque" TAB "Rocks" , 2 ) );

202

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

removeField( sourceString , index ) Purpose Use the removeField function to remove a single indexed field from a sourceString. Syntax sourceString A string containing one or more fields. index The index of the field to remove. Returns Returns sourceString minus the removed field. If the index is greater than the number of fields in sourceString, the original string is returned. See Also setField %fields = "Torque" TAB "So" TAB "Totally" TAB "Rocks!"; // %fields will contain three fields: "Torque", "Totally", and "Rocks!". %fields = removeField( %fields, 1 );

setField( sourceString , index , replace ) Purpose Use the setField function to replace an existing field with a new field(s), or to add field(s) to a string.. Syntax sourceString A string containing one or more fields. index The index of the field to remove. replace The new field(s) to replace the field at index with. Returns There are multiple return cases: - In the first case, a simple one-to-one replacement, the field at index in sourceString will be replaced with the value in replace, and the new string will be returned. - In the first case, a multi-to-one replacement, the field at index in sourceString will be replaced with the value in replace, which can be two or more fields, and the new string will be returned. - In the thrid and final case, new records, empty or filled, can be appended to the end of sourceString. If index is beyond the end of the sourceString, that is, the index is greater than the total count of fields in sourceString, the requisite number of empty (null-string) fields will be appended to the end of sourceString and the value in replace will be appended to the end of this new string. This entire resultant string will be returned. See Also getField, getFields, removeField

203

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

// %fields will contain two fields, "Torque" and "Rocks!", %fields = setField( "Torque" TAB "Rock" , 1 , "Rocks!" );

Metrics
strlen( string ) Purpose Use the strlen function to determine how many characters there are in string. Syntax string The string to count characters for. Returns Returns the number of characters in string, or 0 if string is invalid or a NULL string. // Prints 10 echo( strlen( "0123456789" ) );

Records (Newline Separated String)


A record is a sub-string withing a larger string, where each record is delimted by a NEWLINE character. A newline can be represented as either "\n" or the keyword NL.
getRecord( sourceString , index ) Purpose Use the getRecord function to get the record at index in sourceString. Syntax sourceString A string containing one or more records. Returns Returns record at index in sourceString, or NULL string if no record exists at that index. See Also getRecords, setRecord // Prints Torque echo( getRecord( "GPGTGE + " NL "AND" TAB "Torque" TAB "Rocks" , 2 ) );

204

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getRecordCount( sourceString ) Purpose Use the getRecordCount function to get the number of records in sourceString. Syntax sourceString A string containing one or more records. Returns Returns number of records in sourceString or 0 if no records are present. %records = "You" TAB "must" TAB "buy" TAB "GPGTGE"; echo( getRecordsCount( %records ) ); // Prints 4 getRecords( sourceString , index [ , endindex ] ) Purpose Use the getRecords function to retrieve a set of records from a sourceString. Syntax sourceString A string containing one or more records. index The index of the first record to retrieve. endindex The index of the final record to retrieve. Returns Returns all records (separated by current delimiter) from sourceString, starting at index and ending at endIndex or end of string, whichever comes first. If no endIndex is specified, all remaining records are returned. See Also getRecord, setRecord // Prints Torque^Rocks (^ is printed TAB in console) echo( getRecords( "GPGTGE + " NL "AND" TAB "Torque" TAB "Rocks" , 2 , 3 ) ); // Also prints Torque^Rocks echo( getRecords( "GPGTGE + " NL "AND" TAB "Torque" TAB "Rocks" , 2 ) );

205

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

removeRecord( sourceString , index ) Purpose Use the removeRecord function to remove a single indexed record from a sourceString. Syntax sourceString A string containing one or more records. index The index of the record to remove. Returns Returns sourceString minus the removed record. If the index is greater than the number of records in sourceString, the original string is returned. See Also setRecord %records = "Torque" TAB "So" TAB "Totally" TAB "Rocks!"; // %records will contain three records: "Torque", "Totally", and "Rocks!". %records = removeRecord( %records, 1 );

setRecord( sourceString , index , replace ) Purpose Use the setRecord function to replace an existing record with a new record(s), or to add record(s) to a string.. Syntax sourceString A string containing one or more records. index The index of the record to remove. replace The new record(s) to replace the record at index with. Returns There are multiple return cases: - In the first case, a simple one-to-one replacement, the record at index in sourceString will be replaced with the value in replace, and the new string will be returned. - In the first case, a multi-to-one replacement, the record at index in sourceString will be replaced with the value in replace, which can be two or more records, and the new string will be returned. - In the thrid and final case, new records, empty or filled, can be appended to the end of sourceString. If index is beyond the end of the sourceString, that is, the index is greater than the total count of records in sourceString, the requisite number of empty (null-string) records will be appended to the end of sourceString and the value in replace will be appended to the end of this new string. This entire resultant string will be returned. See Also getRecord, getRecords, removeRecord

206

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

// %records will contain two records, "Torque" and "Rocks!", %Records = setRecord( "Torque" TAB "Rock" , 1 , "Rocks!" );

Replacing
strreplace( sourceString , from , to ) Purpose Use the strreplace function to replace every instance of from in sourceString with to. Syntax sourceString The string to do replacement operations on. from The old value to be replaced. to The new value to replace old values with. Returns Returns a new version of sourceString in which every instance of the value in from was replaced with the value in to. Notes This function is case-sensitive and only does exact matching. // Prints Torque rocks! echo( strreplace("Torque is cool!" , "is cool" , "rocks" ) );

Searching
getSubStr( sourceString , start , count )

Purpose Use the getSubStr function to get a sub-string of sourceString, starting at character index start and ending at character index start + count, or the end-of-string, which ever comes first. Syntax sourceString The string from which to extract a sub-string. start The character index at which the extraction starts. count The length of the sub-string to extract. Returns Returns a string made up of the character at start in sourceString and ending at the end of the original sourceString, or start + count, whichever comes first. Notes If start + count is greater than the length of sourceString, the extraction will return a string shorter than count. See Also strchr

207

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

// Prints 2345 echo( getSubStr( "0123456789" , 2 , 4 ) ); strchr( sourceString , char ) Purpose Use the strchr function to extract a sub-string of sourceString, where the sub-string is equal to the first occurence of char in sourceString followed by the remainder of sourceString. Syntax sourceString The string from which to extract a sub-string. char The character to search for in sourceString. Returns Returns a string composed of first instance of char in sourceString, and all of the characters after it. If char is not found, a NULL string is returned. See Also getSubStr // Prints 3456789 echo( strchr( "0123456789" , "3" ) );

strpos( sourceString , searchString [ , offset ] ) Purpose Use the strPos function to locate the first instance of searchString in sourceString, starting at character 0, or at an optional offset. Syntax sourceString The string in which to search for searchString. searchString The string for which to search for in sourceString. offset An optional non-negative integer value representing the character offset within sourceString at which to begin the search. Returns Returns a numeric character index representing the postion in sourceString at which searchString was found, or -1 to indicate that no instance of searchString was found. See Also strstr // Prints 2 echo( strpos( "This is a test" , "is" ) ); // Prints 5 echo( strpos( "This is a test" , "is" , 3 ) );

208

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

strstr( sourceString , searchString ) Purpose Use the strstr function to locate the first instance of searchString in sourceString. Syntax sourceString The string in which to search for searchString. searchString The string for which to search for in sourceString. Returns Returns a numeric character index representing the position in sourceString at which searchString was found, or -1 to indicate that no instance of searchString was found. See Also strpos // Prints -1 echo( strstr( "This is a test" , "IS" ) ); // Prints 2 echo( strstr( "This is a test" , "is" ) );

Stripping and Trimming


stripChars( sourceString , chars ) Purpose Use the stripChars function to remove chars from sourceString. Syntax sourceString The string to be modified. chars The character or characters to search for and remove. Returns Returns a copy of sourceString, from which all instances of chars have been removed. This may be the original sourceString, if chars was not found. See Also stripMLControlChars, stripTrailingSpaces // Prints WowThisIsCool echo( stripChars("WowxThisy*IsCoolz!" , "*xyz" ) ); stripMLControlChars( sourceString ) Purpose Use the stripMLControlChars function to remove all Torque Markup-Language (ML) symbols from sourceString. Syntax sourceString The string to be modified.

209

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Returns Returns a copy of sourceString with all the ML symbols removed, or the original string if no ML symbols were present. Caution This may not remove <br> correctly, so check before you trust this function. See Also stripChars, stripTrailingSpaces

stripTrailingSpaces( sourceString ) Purpose Use the stripTrailingSpaces function to remove all spaces and underscores from sourceString. Syntax sourceString The string to be modified. Returns Returns modified string, or original string if no trailing spaces found found. Notes This function DOES NOT remove TAB characters, only a space " ", or underline "_" are considered spaces. See Also stripChars, stripMLControlChars, ltrim, rtrim, trim // Prints This is a test. echo( stripTrailingSpaces( "This is a

" ) SPC "test." );

210

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

ltrim( sourceString ) Purpose Use the ltrim function to strip the leading white space from sourceString. Syntax sourceString The string to be trimmed. Returns Returns sourceString with all the leading white spaces removed. Notes White space is any character in this set: spaces, TABs, and NULL strings. See Also stripChars, stripMLControlChars, stripTrailingSpaces, rtrim, trim

// Prints This is a test. echo( ltrim( " " TAB " " SPC "This is a test." ) );

rtrim( sourceString ) Purpose Use the rtrim function to strip the trailing white space from sourceString. Syntax sourceString The string to be trimmed. Returns Returns sourceString with all the trailing white spaces removed. Notes White space is any character in this set: spaces, TABs, and NULL strings. See Also stripChars, stripMLControlChars, stripTrailingSpaces, ltrim, trim // Prints This is a test. echo( rtrim( "This is a

" ) SPC "test." );

211

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

trim( sourceString ) Purpose Use the trim function to strip the leading and trailing white space from sourceString. Syntax sourceString The string to be trimmed. Returns Returns sourceString with all the leading and trailing white spaces removed. Notes White space is any character in this set: spaces, TABs, and NULL strings. See Also stripChars, stripMLControlChars, stripTrailingSpaces, ltrim, rtrim // Prints This is a test. echo( trim( " " TAB " " SPC "This is a

" ) SPC "test." );

Tokens
nextToken( tokenList , tokenVar , delimeter ) Purpose Use the nextToken function to get the first token found in tokenList, where tokens are separated by the character(s) specified in delimeter. The token itself is stored in a variable whose name is specified in tokenVar. This function provides complex power in a simple package. Please read the notes below, they are very important. Syntax tokenList tokenVar delimeter character, The string containing token(s). The 'name' of the variable to store the token in. The character(s) to use as a delimeter. A delimeter may be a single or a sequence of characters. If there are

Returns Returns a copy of tokenList, less the first token and the first delimiter. no more tokens, a NULL string is returned.

Notes This function is scope-smart. That is, when we specify the name of the variable to store a token in by passing a value in tokenVar, we do not include either a local symbol (%), or a global symbol ($). We just pass in an un-adorned name, let's say "George". Then, depending on where this function is called, "George" will become a local (%George), or a global ($George) variable. If this function is called within a function or method definition, "George" will be local (%George). If this function is called from the filescope (executed as part of a file and not within the scope of a function or method), "George" will become a global ($George). There is one additional special case. If you attempt to use this from the console command line, the token will vaporize and no variable will be created.

212

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

function testTokens( ) { %myTokens = "A,E,I,O,U"; while( "" !$= %myTokens ) { %myTokens = nextToken( %myTokens , "theToken" , "," ); echo( %theToken ); } }

testTokens(); A E I O I

Words (Space separated strings)


A word is a sub-string withing a larger string, where each word is delimted by a SPACE character. A space can be represented as either " " or the keyword SPC.
firstWord( sourceString ) Purpose Use the firstWord function to retrieve the first word found in sourceString. Syntax sourceString A string containing one or more words. Returns Returns the first word found in sourceString, or a NULL string, if no words are found. See Also restWords // a target in range was found so select it if (%scanTarg) { %targetObject = firstWord(%scanTarg); %client.setSelectedObj(%targetObject); }

213

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getWord( sourceString ,index ) Purpose Use the getWord function to get the word at index in sourceString. Syntax sourceString A string containing one or more words. Returns Returns word at index in sourceString, or null string if no word exists at that index. See Also getWords, setWord function TerrainEditor::offsetBrush(%this, %x, %y) { %curPos = %this.getBrushPos(); %this.setBrushPos(getWord(%curPos, 0) + %x, getWord(%curPos, 1) + %y); }

getWordCount( sourceString ) Purpose Use the getWordCount function to get the number of words in sourceString. Syntax sourceString A string containing one or more words. Returns Returns number of words in sourceString or 0 if no words are present. if ( %pos != -1 ) { %wordCount = getWordCount( %action ); %mods = %wordCount > 1 ? getWords( %action, 0, %wordCount - 2 ) @ " " : ""; %object = getWord( %action, %wordCount - 1 ); switch$ ( %object ) { case "upov": %object = "POV1 up"; case "dpov": %object = "POV1 down"; case "lpov": %object = "POV1 left"; case "rpov": %object = "POV1 right"; case "upov2": %object = "POV2 up"; case "dpov2": %object = "POV2 down"; case "lpov2": %object = "POV2 left"; case "rpov2": %object = "POV2 right"; default: %object = "??"; } return( %mods @ %object ); } else { error( "Unsupported Joystick input object passed to getDisplayMapName!" ); }

214

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getWords( sourceString ,index [,endindex]) Purpose Use the getWords function to retrieve a set of words from a sourceString. Syntax sourceString A string containing one or more words. index The index of the first word to retrieve. endindex The index of the final word to retrieve. Returns Returns all words (separated by current delimiter) from sourceString, starting at index and ending at endIndex or end of string, whichever comes first. If no endIndex is specified, all remaining words are returned. See Also getWord, setWord %pos = getWords(%obj.getTransform(), 0, 2);

removeWord( sourceString ,index) Purpose Use the removeWord function to remove a single indexed word from a sourceString. Syntax sourceString A string containing one or more words. index The index of the word to remove. Returns Returns sourceString minus the removed word. If the index is greater than the number of words in sourceString, the original string is returned. See Also setWord %cam = removeWord(%cam, 0, getWord(%pos, 0));

215

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

restWords( sourceString ) Purpose Use the restWords function to retrieve all words after the first word in sourceString. Syntax sourceString A string containing one or more words. Returns Returns a string containing all the words after the first word found in sourceString, or a NULL string if no words remain after the first word (or if no words at all remain). See Also firstWord function Heightfield::showTab(%id) { Heightfield::hideTab(); %data = restWords(Heightfield_operation.getRowTextById(%id)); %tab = getField(%data,1); echo("Tab data: " @ %data @ " tab: " @ %tab); %tab.setVisible(true); }

setWord( sourceString ,index,replace) Purpose Use the setWord function to replace an existing word with a new word(s), or to add word(s) to a string.. Syntax sourceString A string containing one or more words. index The index of the word to remove. replace The new word(s) to replace the word at index with. Returns There are multiple return cases: - In the first case, a simple one-to-one replacement, the word at index in sourceString will be replaced with the value in replace, and the new string will be returned. - In the first case, a multi-to-one replacement, the word at index in sourceString will be replaced with the value in replace, which can be two or more words, and the new string will be returned. - In the third and final case, new records, empty or filled, can be appended to the end of sourceString. If index is beyond the end of the sourceString, that is, the index is greater than the total count of words in sourceString, the requisite number of empty (null-string) words will be appended to the end of sourceString and the value in replace will be appended to the end of this new string. This entire resultant string will be returned. See Also getWord, getWords, removeWord

216

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

function WorldEditor::dropCameraToSelection(%this) { if(%this.getSelectionSize() == 0) return; %pos = %this.getSelectionCentroid(); %cam = LocalClientConnection.camera.getTransform(); // set the pnt %cam = setWord(%cam, 0, getWord(%pos, 0)); %cam = setWord(%cam, 1, getWord(%pos, 1)); %cam = setWord(%cam, 2, getWord(%pos, 2)); LocalClientConnection.camera.setTransform(%cam);

A.3.4. NETWORKING
Tags (NetStringTable)
addTaggedString( string ) Purpose Use the addTaggedString function to tag a new string and add it to the NetStringTable. Syntax string The string to tagged and placed in the NetStringTable. Tagging ignores case, so tagging the same string (excluding case differences) will be ignored as a duplicated tag. Returns Returns a string (containing a numeric value) equivalent to the string ID for the newly tagged string. buildTaggedString( format , <arg1, ...arg9> )

Purpose Use the buildTaggedString function to build a tagged string using the specified format. Syntax enable A boolean value. If set to true, network packet logging is enabled, otherwise it is disabled. Returns No return value. detag( tagID ) Purpose Use the detag function to convert a tag to a string. This can only be used in the proper context, i.e. to parse values passed to a client command or to a server command. See 'Remote Procedure Call Samples' below. Syntax tagID A numeric tag ID corresponding to a previously tagged string.

217

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Returns Returns the string associated with the tag ID. See Also commandToClient(), commandToServer(). dumpNetStringTable() Purpose Use the dumpNetStringTable function to dump a list of all the currently registered NetStringTable entries, including the times each has been referenced, the total entry count, and the current 'highest' reference string. Returns No return value. Notes For this to work, the engine must have been compiled with TORQUE_DEBUG defined.

getTag( taggedString ) Purpose Use the getTag function to retrieve the tag ID associated with a previously tagged string. Syntax taggedString A previously tagged string. Returns Returns the tag ID of the string. If the string was not previously tagged, it gets tagged and the new tag ID is returned. getTaggedString( tag ) Purpose Use the getTaggedString function to convert a tag to a string. This is not the same a detag() which can only be used within the context of a function that receives a tag. This function can be used any time and anywhere to convert a tag to a string. Syntax tag A numeric tag ID. Returns Returns the string corresponding to the tag ID.

218

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

removeTaggedString( tag ) Purpose Use the removeTaggedSTring function to remove a previously tagged string from the NetStringTable. Syntax tag A number tag ID. Returns No return value.

Telnet
gotoWebPage( address ) Purpose Use the gotoWebPage function to open a browser and go to the specified URL address. Syntax address A complete and valid URL. Returns No return value.

219

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setNetPort( port ) Purpose Use the setNetPort function to set the netport that the server will listen on. Syntax port A numeric port ID. Returns Returns true if the set worked, false otherwise. telnetSetParameters( port, consolePass, listenPass [ , remoteEcho ] ) Purpose Use the telnetSetParameters function to setup and accept telnet requests on a specific port. Syntax port consolePass listenPass remoteEcho A numeric port ID. Password for read/write access to the console. Password for read access to the console. A boolean value to enable echoing to the client, false by default.

Returns No return value.

220

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Client-Server Communications
commandToClient( client, func [ , arg1, ... , argn ] ) Purpose Use the commandToClient function to issue a remote procedure call on a client. Syntax client The numeric ID of a client gameConnection. func The suffix of the remote procedure name to be executed on the client. arg1 .. argn Optional arguments to be passed to the remote procedure. Returns No return value. Notes All arguments (excluding client) may be in tagged or non-tagged format. Procedure Call Samples' below. See 'Remote

commandToServer( func [ , arg1, ... , argn ] ) Purpose Use the commandToServer function to issue a remote procedure call the server. Syntax func The suffix of the remote procedure name to be executed on the client. arg1 .. argn Optional arguments to be passed to the remote procedure. Returns No return value. Notes All arguments may be in tagged or non-tagged format. below. See 'Remote Procedure Call Samples'

221

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Game Server
allowConnections( enable ) Purpose Use the allowConnections to enable (or disable) remote connections to the local game server. Syntax enable A boolean value enabling, or disabling connections to the local server. Returns No return value.

cancelServerQuery() Purpose Use the cancelServerQuery function to cancel a previous query*() call. Returns No return value. See Also queryLANServers(), queryMasterServer(), querySingleServer() getServerCount() Purpose Use the getServerCount function to determine the number of game servers found on the last queryLANServers() or queryMasterServer() call. Returns Returns a numeric value equal to the number of game servers found on the last queryLANServers() or queryMasterServer() call. Returns 0 if the function was not called, or none were found. Notes This value is important because it allows us to properly index when calling setServerInfo(). See Also queryLANServers, queryMasterServer, setServerInfo()

222

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

queryLANServers( port , flags , gametype , missiontype , minplayers , maxplayers , maxbots , regionmask , maxping , mincpu , filterflags ) Purpose Use the queryLANServers function to establish whether any game servers of the required specification(s) are available on the local area network (LAN). Syntax port Look for any game servers advertising at this port. Set to 0 if you don't care what port the game server is using. flags - Look for any game servers with these special flags set. Set to 0 for no flags. gametype - Look for any game servers playing a game type that matches this string. Set to the NULL string to look for any game type. missiontype - Look for any game servers playing a mission type that matches this string. Set to the NULL string to look for any mission type. minplayers - Look for any game servers with this number of players or more. Set to 0 for no lower limit. maxplayers - Look for any game servers with this number of players or fewer. Set to 0 for no upper limit. maxbots - Look for any game servers with this number of AI controlled players or fewer. Set to 0 for no limit. regionmask - Look for any master servers, on our master server list, in this region. Set to 0 to examine all regions. maxping - Look for any game servers with a PING rate equal to or lower than this. Set to 0 for no upper PING limit. mincpu - Look for any game servers with a CPU (clock speed) equal or greater than this. Set to 0 for no CPU (clock speed) limit. filterflags - Look for any game servers with this game version number or higher. Set to 0 to find all versions. Returns No return value. See Also getServerCount, queryMasterServer, setServerInfo, stopServerQuery

queryMasterServer( flags , gametype , missiontype , minplayers , maxplayers , maxbots , regionmask , maxping , mincpu , filterflags ) Purpose Use the queryMasterServer function to query all master servers in the master server list and to establish if they are aware of any game servers that meet the specified requirements, as established by the arguments passed to this function. Syntax flags - Look for any game servers with these special flags set. Set to 0 for no flags. gametype - Look for any game servers playing a game type that matches this string. Set to the NULL string to look for any game type. missiontype - Look for any game servers playing a mission type that matches this string. Set to the NULL string to look for any mission type. minplayers - Look for any game servers with this number of players or more. Set to 0 for no lower limit. maxplayers - Look for any game servers with this number of players or fewer. Set to 0 for no upper limit.

223

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

maxbots - Look for any game servers with this number of AI controlled players or fewer. Set to 0 for no limit. regionmask - Look for any master servers, on our master server list, in this region. Set to 0 to examine all regions. maxping - Look for any game servers with a PING rate equal to or lower than this. Set to 0 for no upper PING limit. mincpu - Look for any game servers with a CPU (clock speed) equal or greater than this. Set to 0 for no CPU (clock speed) limit. filterflags - Look for any game servers with this game version number or higher. Set to 0 to find all versions. Returns No return value. Notes In order for this function to do anything, a list of master servers must have been previously specified. This list may contain one or more server addresses. A call to this function will search all servers in the list. To specify a list, simply create a set of array entries like this: $pref::Master[0] = "2:192.168.123.15:28002"; $pref::Master[1] = "2:192.168.123.2:28002"; ... The format of these values is ==> Region Number : IP Address : Port Number These values should be specified in either the client's or the server's preferences file (prefs.cs). You may specifiy it elsewhere, however be sure that it is specified prior to this function being called and before any other functions that rely on it. See Also getServerCount, queryLANServers, setServerInfo, startHeartbeat, stopServerQuery

querySingleServer( address [ , flags ] ) Purpose Use the querySingleServer function to re-query a previously queried lan server, OR a game server found with queryLANServers or with queryMasterServer and selected with setServerInfo. This will refresh the information stored by TGE about this server. It will not however modify the values of the $ServerInfo::* global variables. Syntax address The IP address and Port to re-query, i.e. "192.168.123.2:28000". flags No longer used. Returns No return value. See Also getServerCount, queryLANServers, queryMasterServer, setServerInfo, stopServerQuery

224

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setServerInfo( index ) Purpose Use the setServerInfo function to set the values of the $ServerInfo::* global variables with information for a server found with queryLANServers or with queryMasterServer. Syntax index The index of the server to get information about. Returns Will return true if the information was successfully set, false otherwise. See Also getServerCount, queryLANServers, queryMasterServer, querySingleServer

startHeartbeat() Purpose Use the startHeartbeat function to start advertising this game serer to any master servers on the master server list. Returns No return value. Notes In order for this function to do anything, a list of master servers must have been previously specified. This list may contain one or more server addresses. Once this function is called, the game server will re-advertise itself to all the master servers on its master server lits every two minutes. To specify a list, simply create a set of array entries like this: $pref::Master[0] = "2:192.168.123.15:28002"; $pref::Master[1] = "2:192.168.123.2:28002"; ... The format of these values is ==> Region Number : IP Address : Port Number These values should be specified in either the client's or the server's preferences file (prefs.cs). You may specifiy it elsewhere, however be sure that it is specified prior to this function being called and before any other functions that rely on it. See Also queryMasterServer, stopHeartbeat

225

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

stopHeartbeat() Purpose Use the startHeartbeat function to stop advertising this game serer to any master servers on the master server list. Returns No return value. See Also queryMasterServer, startHeartbeat stopServerQuery() Purpose Use the stopServerQuery function to cancel any outstanding server queries. Returns No return value. See Also queryLANServers, queryMasterServer, querySingleServer

Statistics/Metrics
getMaxFrameAllocation() Purpose Use the getMaxFrameAllocation function to determine the largest amount of memory that has ever been allocated in the process of rendering a single frame. Returns Returns a non-negative integer representing the largest number of bytes temporarily allocated during the rendering of a single frame. Notes For this to work, the engine must have been compiled with TORQUE_DEBUG defined. During

frame rendering, the engine will dynamically allocate and deallocate memory for various tasks. This function allows us to get a peek at the largest amount of allocation that has occured to date. If this number is very large, we may have a bug or be facing a serious performance issue of one form or another. We want this number to be small.

226

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3.5. CONSOLE
call( funcName [ , args ... ] ) Purpose Use the call function to dynamically build and call a function. Syntax funcName A string containing the unadorned name of a function to be executed. args ... - Any arguments that should be passed to the function. Returns Returns a string containing the results from the function that is built and called. See Also eval // Prints Hello World call( "echo" , "Hello" , " ", "World" );

cls( ) Purpose Use the cls function to clear the console output. Returns No return value. collapseEscape( text ) Purpose Use the collapseEscape function to replace all escape sequences ('\\xx') with a collapsed version ('\xx'). Syntax text A string, possibly containing escape sequences. Returns Returns a copy of text with all escape sequences converted to an encoding. See Also expandEscape echo( "edo\\t" ); // Prints edo\t echo( collapseEscape( "edo\\t" ) ); \\ Prints edo^

227

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

compile( fileName ) Purpose Use the compile function to pre-compile a script file without executing the contents. Syntax fileName A path to the script to compile. Returns Returns 1 if the script compiled without errors and 0 if the file did not compile correctly or if the path is wrong. Also, ff the path is invalid, an error will print to the console. See Also exec compile("./main.cs"); deleteVariables( wildCard ) Purpose Use the deleteVariables function to delete any global variable matching the wildCard statement. Syntax wildCard A string identifying what variable(s) to delete. All characters used to create a global are allowed and the special symbol "*", meaning 0 or more instances of any character. Returns No return value. $edo = "cool"; echo( $edo ); // Prints cool deleteVariables( "$ed*" ); // Delete all globals starting with $ed echo( $edo ); // Prints "" because $edo was deleted

228

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

echo( text [ , ... ] ) Purpose Use the echo function to print messages to the console. Syntax text Any valid text string. ... - Any additional valid text string(s). Returns No return value. See Also error, warn // Prints This is a test echo( "This is a test" ); enableWinConsole( enable ) Purpose Use the enableWinConsole function to tell TGE to create an external console window, either as a separate DOS window or as a new window under OSX/Linux/*NIX. Syntax enable A boolean. Returns No return value. Notes Subsequent calls to this function do nothing. Only one external console is allowed. If this value is set to true, a new console window will be created.

enableWinConsole( true ); // Open an external console. error( text [ , ... ] ) Purpose Use the error function to print error print in red. messages to the console. These messages usually

Syntax text Any valid text string. ... - Any additional valid text string(s). Returns No return value. See Also echo, warn // Prints This is a test error( "This is a test" );

229

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

eval( script ) Purpose Use the eval function to execute any valid script statement. Syntax script A string containing a valid script statement. This may be a single line statement or multiple lines concatenated together with new-line characters. Returns Returns the result of executing the script statement. Notes If you choose to eval a multi-line statement, be sure that there are no comments or (\\) comment blocks (\**\) embedded in the script string. See Also call exec( fileName [ , nocalls [ , journalScript ] ] ) Purpose Use the exec function to compile and execute a normal script, or a special journal script. Syntax fileName A string containing a path to the script to be compiled and executed. nocalls A boolean value. If this value is set to true, then all function calls encountered while executing the script file will be skipped and not called. This allows us to re-define function definitions found in a script file, without re-executing other worker scripts in the same file. journalScript A boolean value. If this value is set tot true, and if a journal is being played, the engine will attempt to read this script from the journal stream. If no journal is playing, this field is ignored. Returns Returns true if the file compiled and executed w/o errors, false otherwise. Notes If $Pref::ignoreDSOs is set to true, the system will use .cs before a .dso file if both are found. See Also compile exec( "./main.cs" );

230

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

expandEscape( text ) Purpose Use the collapseEscape function to replace all escape sequences ('\xx') with an expanded version ('\\xx'). Syntax text A string, possibly containing escape sequences. Returns Returns a copy of text with all escape sequences expanded. See Also collapseEscape echo( "edo\t" ); // Prints edo^ echo( expandEscape( "edo\t" ) ); \\ Prints edo\t

export( wildCard [ , fileName [ , append ] ] ) Purpose Use the export function to save all global variables matching the specified name pattern in wildCard to a file, either appending to that file or over-writing it. Syntax wildCard A string identifying what variable(s) to export. All characters used to create a global are allowed and the special symbol "*", meaning 0 or more instances of any character. fileName A string containing a path to a file in which to save the globals and their definitions. append A boolean value. If this value is true, the file will be appended to if it exists, otherwise it will be created/over-written. Returns No return value. echo("Exporting server prefs"); export("$Pref::Server::*", "./server/prefs.cs", False); quit() Purpose Use the quit function to stop the engine and quit to the command line. Returns No return value. quit();

231

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

warn( text [ , ... ] ) Purpose Use the warn function to print warning messages to the console. yellow or orange. Syntax text Any valid text string. ... - Any additional valid text string(s). Returns No return value. See Also warn, error // Prints This is a test warn( "This is a test" ); These messages usually

A.3.6. DEVICE IO
activateDirectInput() Purpose Use the activateDirectInput function to activate polling of direct input devices (keyboard, mouse, joystick, et cetera). Returns No return value. See Also deactivateDirectInput activateDirectInput(); activateKeyboard() Purpose Use the activateKeyboard function to enable directInput polling of the keyboard. Returns Returns a true if polling was successfully enabled. See Also deactivateKeyboard if(activateKeyboard()) echo(Keyboard has been activated);

232

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

deactivateDirectInput() Purpose Use the deactivateDirectInput function to de-activate polling of direct input devices (keyboard, mouse, joystick, et cetera). Returns No return value. See Also activateDirectInput deactivateDirectInput(); deactivateKeyboard() Purpose Use the deactivateKeyboard function to disable directInput polling of the keyboard. Returns No return value. See Also activateKeyboard deactivateKeyboard(); disableJoystick() Purpose Use the disableJoystick function to disable joystick input. Returns No return value. See Also enableJoystick, getJoystickAxes, isJoystickEnabled disableMouse() Purpose Use the disableMouse function to disable mouse input. Returns No return value. See Also enableMouse disableMouse();

233

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

echoInputState() Purpose Use the echoInputState function to dump the input state of the mouse, keyboard, and joystick to the console. Returns No return value. See Also activateDirectInput, deactivateDirectInput, activateKeyboard, deactivateKeyboard, disableJoystick, enableJoystick, enableMouse, disableMouse echoInputState(); DirectInput is enabled but inactive. - Keyboard is enabled and inactive. - Mouse is disabled and inactive. - Joystick is disabled and inactive. enableJoystick() Purpose Use the enableJoystick function to enable joystick input if it is present. Returns Will return true if the joystick is present and was successfully enabled, false otherwise. See Also disableJoystick, getJoystickAxes, isJoystickDetected enableJoystick(); enableMouse() Purpose Use the enableMouse function to enable mouse input. Returns Returns true if a mouse is present and it was enabled, false otherwise. See Also disableMouse enableMouse();

234

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getJoystickAxes( instance ) Purpose Use the getJoystickAxes function to get the current axes position (x and y ) of any intance of a joystick. Syntax instance A non-negative number value selecting a specific joystick instance attached to this computer. Returns Returns a string containing the "x y" position of the joystick. See Also disableJoystick, enableJoystick, isJoystickDetected Used to get the current axes of the joystick pointed to by instance, where instance is a numeric value specifying the joystick number. %joyAxes = getJoystickAxes( 3 );

isJoystickDetected() Purpose Use the isJoystickDetected function to determine if one or more joysticks are connected to the system. Returns Returns true if one or more joysticks are attached and detected, false otherwise. Notes This doesn't tell us how many joysticks there are, just that there are joysticks. our job to find out how many and to attach them. See Also disableJoystick, enableJoystick, getJoystickAxes if( !isJoystickDetected() ) echo( "No Joystick was detected" ); It is

235

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

lockMouse( isLocked ) Purpose Use the lockMouse function to un/lock the mouse. Syntax isLocked A boolean value Returns No return value. { function cursorOff() if ( $cursorControlled ) lockMouse(true); Canvas.cursorOff();

A.3.7. FILE I/O


TGE provides a file manager maintains a list of all files in the game directory and all sub-directories. The followings functions are used to access this list. In order manipulate (read/write) the contents of a file, see the console object 'fileObject', listed in the 'ConObjects Quick Reference' or refer to the 'TGE I/O' chapter of EGTGE.

modpath
When a Torque game starts up a call, or calls are made to setModPaths(). This function is passed a game directories, separated by semi-colons (;). The file manager will examine each of these game paths and their sub-directories, building up a list of known files. This information is used later for relative pathing and file searching.

Pathing
Torque supports both direct pathing and relative pathing. Direct paths start with a slash (/). For example, "/starter.fps/main.cs" points to the file "main.cs" under the game directory "starter.fps", where "starter.fps" is in the root directory (the directory where the executable is started from. Relative pathing can be accomplished in three basic ways. In the first method, if an unadorned name is used as the first part of a directory ("starter.fps/test.cs"), the engine will assume that this first name is the name game directory, but then if the unadorned name does not match the game directory, the file match/search will fail. In general, you should not use unadorned names. In the second method, if a tilde (~) is used as the first part of a directory ("~/test.cs"), the engine will assume that the tilde should be replaced by the root-child this file lives in. For example, the root child of "starter.fps/client/doit.cs" is "starter.fps". Therefore, if we use this path "~/somefile.cs" in a command within the file "doit.cs", the path will be expanded to be "starter.fps/somefile.cs". In the third and final relative pathing method, if a dot (.) is used as the first part of a directory ("./test.cs"), then the dot is expanded to be the root- path that the current file resides in. For example, the root-path of "starter.fps/client/doit.cs" is "starter.fps/client". Therefore, if we use this path "./somefile.cs" in a command within the file "doit.cs", the path will be expanded to be "starter.fps/client/somefile.cs". 236

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Wildcards
Torque supports a single wildcard, the asterisk (*). This wild card can be interpreted to mean "zero or more instances of any chracter(s)".
expandFilename( filename ) Purpose Use the expandFilename function to convert a relative path name to a full path name. Syntax filename A string containing the relative or full path and file name of an existing or new file. Returns Returns a string containing the expanded path to the specified file. expandFilename("~/data/sound/testing.wav");

fileBase( filename ) Purpose Use the fileBase function to get the name of a file from a relative or full path, not including the file extension. Syntax filename A string containing the relative or full path and file name of an existing or new file. Returns Returns an unadorned file name without a path or file extension. See Also fileExt, fileName, filePath fileBase(egt/main.cs); // will return main fileExt( filename ) Purpose Use the fileExt function to get the extension of a file from a relative or full path, not including the file extension. Syntax filename A string containing the relative or full path and file name of an existing or new file. Returns Returns a file extension, including the dot (.). Notes If asterisks are present in an extension, as passed in filename, they will not be expanded in the return value.

237

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

See Also fileBase, fileName, filePath Returns the file extension (suffix) for the file specified by filename. fileExt(script.cs); // will return .cs

fileName( filename ) Purpose Use the fileName function to get the filename and extension of a file from a relative or full path, not including the file extension. Syntax filename A string containing the relative or full path and file name of an existing or new file. Returns Returns a string containing the full name of a file less any path before the name. Notes If asterisks are present in an file name, as passed in filename, they will not be expanded in the return value. See Also fileBase, fileExt, filePath fileName(egt/main.cs); // will return main.cs filePath( filename ) Purpose Use the fileBase function to get all parts of a path up to, but not including the last slash (/). Syntax filename A string containing the relative or full path and file name of an existing or new file. Returns Returns a string containing the relative or full path portion of filename. Notes If asterisks are present in any part of the path, as passed in filename, they will not be expanded in the return value. See Also fileBase, fileExt, fileName filePath(common/ui/defaultProfiles.cs); // Will return common/ui

238

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

findFirstFile ( pattern ) Purpose Use the findFirstFile function to find the first file matching pattern. Syntax pattern A full or partial path, followed by a full or partial filename, or any combination of these two elements. Returns Returns a full path to the first file name matching pattern. Returns a NULL string, when no matches are found. Notes This function will search all directories in the modpath, as created by setModPaths. Each time this function is called it will reset an internal variable tracking the current position in the file list. So, multiple routines calling this in an overlapping fashion will clobber each other. See Also findNextFile, getFileCount, getModPaths, setModPaths findFirstFile(*.cs); findNextFile ( pattern ) Purpose Use the findNextFile function to find the next file matching pattern. Syntax pattern A full or partial path, followed by a full or partial filename, or any combination of these two elements. Returns Returns a full path to the next file name matching pattern. Returns a NULL string, when no matches are found. Notes This function will search all directories in the modpath, as created by a call to setModPaths. Also, this function requires that findNextFile be called at least with the same pattern, some time prior to calling this function. See Also findFirstFile, getFileCount, getModPaths, setModPaths findNextFile( *.cs );

239

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getFileCount ( pattern ) Purpose Use the getFileCount function to determine how many files exist in modpaths that match the specified pattern. Syntax pattern A full or partial path, followed by a full or partial filename, or any combination of these two elements. Returns Returns a zero if no matches are found, or a positive integer value specifying how many matches there were. See Also findFirstFile, findNextFile getFileCount(*.cs); getFileCRC( filename ) Purpose Use the getFileCRC function to calculate the Cyclic-Redundancy-Check (CRC) value for a file as specified by the partial or full path in filename. Syntax filename A string containing the relative or full path and file name of an existing or new file. Returns Returns a non-zero positive integer value corresponding to this file's CRC. Notes CRC values are useful for checking to see if two same named files are actually the same. If a client file of the same path and name as a file on the server has a different CRC from the server version, then the files are NOT the same, otherwise they highly likely to be the same. Although it is theoretically possible for two non-matching files to have matching CRCs, the odds are very much against it. getFileCRC(/fps/client/scripts/script/cs);

240

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isFile( filename ) Purpose Use the isFile function to determine whether the value in filename is in fact an existing file. Syntax filename A string containing the relative or full path and file name of an existing file. Returns Returns true if the file exists, false otherwise. See Also isWriteableFileName isFile(/fps/client/scripts/script.cs); isWriteableFileName( filename ) Purpose Use the isWriteableFileName function to determine whether the value in filename is in fact an existing file and it can be written to. Syntax filename A string containing the relative or full path and file name of an existing file. Returns Returns true if the file exists and can be written to, false otherwise. See Also isFile isWriteableFileName(/fps/client/scripts/script.cs);

241

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3.8. PACKAGES
activatePackage( packageName ) Purpose Use the activatePackage function to activate a package definition and to re-define all functions named within this package with the definitions provided in the package body. Syntax packagename The name or ID of an existing package. Returns No return value. Notes This pushes the newly activated package onto the top of the package stack. See Also deactivatePackage, isPackage activatePackage(Show);

deactivatePackage( packageName ) Purpose Use the deactivatePackage function to deactivate a package definition and to pop any definitions from this package off the package stack. Syntax packagename The name or ID of an existing package. Returns No return value. Notes This also causes any subsequently stacked packages to be popped. i.e. If any packages were activated after the one specified in packageName, they too will be deactivated and popped. See Also activatePackage, isPackage deactivePackage(Show);

242

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isPackage( packageName ) Purpose Use the isPackage function to check if the name or ID specified in packageName is a valid package. Syntax packagename The name or ID of an existing package. Returns Returns true if packageName is a valid package, false otherwise. See Also activatePackage, deactivatePackage isPackage(Show);

A.3.9. OBJECTS
An object is in the class instance created by the engine or by scripts and available for access in the console.
isObject( handle ) Purpose Use the isObject function to check if the name or ID specified in handle is a valid object. Syntax handle A name or ID of a possible object. Returns Returns true if handle refers to a valid object, false otherwise. isObject(%player);

nameToID( objectName ) Purpose Use the nameToID function to convert an object name into an object ID. Syntax objectName A string containing the name of an object. Returns Returns a positive non-zero value if the name corresponds to an object, or a -1 if it does not. Notes This function is a helper for those odd cases where a string will not covert properly, but generally this can be replaced with a statement like: ("someName")

243

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

nameToId(%player);

strToPlayerName( playerName ); Purpose Use the strToPlayerName function to convert a string into a valid player name. It will remove various characters to include: comma (,), dot (.), back-slash (\), and tick ('). Syntax playerName A string containing a candidate player name. Returns Returns a cleaned version of playerName that is suitable for networking and use by the engine in various instances. %name = stripTrailingSpaces( strToPlayerName( %name ) );

A.3.10. EVENT SCHEDULING


cancel( eventID ) Purpose Use the cancel function to cancel a previously scheduled event as specified by eventID. Syntax eventID The numeric ID of a previously scheduled event. Returns No return value. See Also getEventTimeLeft, getScheduleDuration, getTimeSinceStart, isEventPending, schedule, obj.schedule

getEventTimeLeft( eventID ) Purpose Use the getEventTimeLeft function to determine how much time remains until the event specified by eventID occurs. Syntax eventID The numeric ID of a previously scheduled event. Returns Returns a non-zero integer value equal to the milliseconds until the event specified by eventID will occur. However, if eventID is invalid, or the event has passed, this function will return zero. See Also cancel, getScheduleDuration, getTimeSinceStart, isEventPending, schedule, obj.schedule

244

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getScheduleDuration ( eventID ) Purpose Use the getScheduleDuration function to determine how long the event associated with eventID was scheduled for. Syntax eventID The numeric ID of a previously scheduled event. Returns Returns a non-zero integer value equal to the milliseconds used in the schedule call that created this event. However, if eventID is invalid, this function will return zero. See Also cancel, getEventTimeLeft, getTimeSinceStart, isEventPending, schedule, obj.schedule Returns the time in milliseconds of the event denoted by eventID as it was originally scheduled. Returns 0 if eventID is past or invalid. getTimeSinceStart( eventID ) Purpose Use the getTimeSinceStart function to determine how much time has passed since the event specified by eventID was scheduled. Syntax eventID The numeric ID of a previously scheduled event. Returns Returns a non-zero integer value equal to the milliseconds that have passed since this event was scheduled. However, if eventID is invalid, or the event has passed, this function will return zero. See Also cancel, getEventTimeLeft, getScheduleDuration, isEventPending, schedule, obj.schedule

isEventPending( eventID ) Purpose Use the isEventPending function to see if the event associated with eventID is still pending. Syntax eventID The numeric ID of a previously scheduled event. Returns Returns true if this event is still outstanding and false if it has passed or eventID is invalid.

245

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Notes When an event passes, the eventID is removed from the event queue, becoming invalid, so there is no discnerable difference between a completed event and a bad event ID. See Also cancel, getEventTimeLeft, getScheduleDuration, getTimeSinceStart, schedule, obj.schedule $Game::Schedule = schedule($Game::EndGamePause * 1000, 0, "onCyclePauseEnd"); if( isEventPending($Game::Schedule) ) schedule( t , objID || 0 , echo(got a pending event);

functionName, arg0, ... , argN )

Purpose Use the schedule function to schedule functionName to be executed with optional arguments at time t (specified in milliseconds) in the future. This function may be associated with an object ID or not. If it is associated with an object ID and the object is deleted prior to this event occurring, the event is automatically canceled. Syntax t objID functionName arg0, ... , argN The time to wait (in milliseconds) before executing functionName. An optional ID to associate this event with. An unadorned (flat) function name. Any number of optional arguments to be passed to functionName.

Returns Returns a non-zero integer representing the event ID for the scheduled event. See Also cancel, getEventTimeLeft, getScheduleDuration, getTimeSinceStart, isEventPending, obj.schedule $Game::Schedule = schedule($Game::EndGamePause * 1000, 0, "onCyclePauseEnd");

objID.schedule( t , methodName, arg0, ... , argN )


Purpose Use the objID.schedule method to arguments at time t (specified in event is automatically associated prior to this event occuring, the Syntax t objID methodName arg0, ... , argN The time to wait (in milliseconds) before executing methodName. An ID to associate this event with. An unadorned (flat) function name. Any number of optional arguments to be passed to methodName. schedule methodName to be executed with optional milliseconds) in the future on the object objID. This with the object objID, and if that object is deleted event is automatically cancelled.

Returns Returns a non-zero integer representing the event ID for the scheduled event.

246

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Note This is one of those rare cases where a method is described in the function appendix, but it really needed to be defined here for clarity. See Also cancel, getEventTimeLeft, getScheduleDuration, getTimeSinceStart, isEventPending, schedule $Game::Schedule = schedule($Game::EndGamePause * 1000, 0, "onCyclePauseEnd");

A.3.11. DATABLOCKS
deleteDataBlocks() Purpose Use the deleteDataBlocks function to cause a server to delete all datablocks that have thus far been loaded and defined. Returns No return value. deleteDataBlocks();

A.3.12. VIDEO / TEXTURING


addMaterialMapping( materialName, map0 [ , ... , map97 ]) Purpose Use the addMaterialMapping function to create a new material map instance. These maps are used by terrain and interiors for creating proper footstep sounds and dust puffs when an avatar treads upon a terrain block or interior surface using the associated texture. Syntax materialName The unadorned texture name this map is associated with. For example, "sand.jpg" would have a materialName of "sand". map0 At least one (required) map. See map chart below for format of these maps. ... , map97 Up to 96 additional maps. Returns No return value. Notes There are fewer mapping types than the maximum number of maps this function will accept. addMaterialMapping( "sand" , "sound: 0" , "color: 0.46 0.36 0.26 0.4 0.0" );

247

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Map Type Sound Type Detail Texture

Format "sound: #" "detail: texture"

Purpose Map this surface to the numeric sound type #: 0 Soft, 1 Hard, 2 Metal, 3 Snow. A texture name to be used for a detail texture on surfaces using this material map. This material map has an environment map and it should use texture for the mapping and give it a reflective factor of reflect, where reflect is in the range [0.25, 1.0]. When a player treads on this surface and has smoke puffs enabled, the smoke will have the color specified in R, G, and B, where these values are floating point values in the range [0.0, 1.0]. StartA is a floating point value in the range [0.0, 1.0] specifying the puff's starting alpha. EndA is of the same format and specifies the puff's ending Alpha.

Environment Map

"environment: texture reflect"

Smoke Puff Color

"color: R G B StartA EndA"

clearTextureHolds() Purpose Use the clearTextureHolds function to free and release any held textures, returning the size of the held textures free. Returns Returns the space freed. Notes As long as a texture is not currently in use, it will be released. See Also dumpTextureStats, flushTextureCache, purgeResources clearTextureHolds();

248

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

dumpTextureStats() Purpose Use the dumpTextureStats function to dump information about each texture currently in use to the console. This information will be printed in this format: aaa type: (refCount, holding) textureSpace (filename) Output Syntax type: refCount holding textureSpace filename Type of this texture. See 'Texture Types' list below. Number of references to this texture. Is this texture being held? "yes" or "no" Bytes used by this texture. Full path to this texture.

Returns No return value. Notes For this to work, the engine must have been compiled with TORQUE_DEBUG defined. See Also clearTextureHolds, flushTextureCache dumpTextureStats();

flushTextureCache() Purpose Use the flushTextureCache function to flush the texture cache. Returns No return value. See Also clearTextureHolds, dumpTextureStats, purgeResources flushTextureCache();

249

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getDesktopResolution() Purpose Use the getDesktopResolution function to determine the current resolution of the desktop (not the application). Returns Returns a string containing the current desktop resolution, including the width height and the current bits per pixel. Notes To get the current resolution of a windowed display of the torque game engine, simply examine the global variable '$pref::Video::resolution'. See Also getDisplayDeviceList, getResolutionList, nextResolution, prevResolution, setDisplayDevice, setRes, setScreenMode, switchBitDepth %res = getDesktopResolution(): getDisplayDeviceList() Purpose Use the getDisplayDeviceList function to get a list of valid display devices. Returns Returns a tab separated list of valid display devices. See Also getDesktopResolution, getResolutionList, setRes, setScreenMode, switchBitDepth echo(Display Device(s) : @ getDisplayDeviceList() );

250

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getResolutionList( devicename ) Purpose Use the getResolutionList function to get a semicolon separated list of legal resolutions for a specified device. Syntax deviceName A string containing a supported display device. Returns Returns a tab separated list of valid display resolutions for devicename. Notes Resolutions are always in the form: width height bpp, where width and height are in pixels and bpp is bits-per-pixel. See Also getDesktopResolution, getDisplayDeviceList, setRes, setScreenMode, switchBitDepth echo("Possible resolutions : @ getResolutionList( "OpenGL" ) ); getVideoDriverInfo() Purpose Use the getVideoDriverInfo function to dump information on the video driver to the console. Returns No return value. echo(Device driver info : @ getVideroDriverInfo() );

isDeviceFullScreenOnly( devicename ) Purpose Use the isDeviceFullScreenOnly function to determine if the device specified in devicename is for full screen display only, or whether it supports windowed mode too. Syntax deviceName A string containing a supported display device. Returns Returns true if the device can only display full scree, false otherwise. See Also getResolutionList

251

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

isFullScreen() Purpose Use the isFullScreen function to determine if the current application is displayed in full-screen mode. Returns Returns true if the engine is currently displaying full-screen, otherwise returns false. nextResolution() Purpose Use the nextResolution function to switch to the next valid (higher) resolution for the current display device. Returns Returns true if switch was successful, false otherwise. See Also getDesktopResolution, prevResolution, getResolutionList, setRes, setScreenMode, switchBitDepth

png2jpg( pngFilename [ , quality ] ) Purpose Use the png2jpg function to save a PNG file specified by pngFilename as a similarly named JPEG file with the optionally specified quality. Syntax pngFilename The path and file name of the PNG file to convert. quality An optional quality between 0 and 100. The default quality is 90. Returns Returns -1 if the file could not be opened, 0 on other failures, and 1 if the conversion worked. prevResolution() Purpose Use the prevResolution function to switch to the previous valid (lower) resolution for the current display device. Returns Returns true if switch was successful, false otherwise. See Also getDesktopResolution, nextResolution, getResolutionList, setRes, setScreenMode, switchBitDepth

252

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

screenShot( filename , format ) Purpose Use the screenShot function to capture a screen shot and store it in the file specified by filename. Syntax filename Path to file in which to save screenshot. format The format to save the file in, PNG or JPG. Returns No return value. See Also panoramaScreenShot screenshot("capture0.png", "PNG" );

setDefaultFov( defaultFOV ) Purpose Use the setDefaultFov function to set the default field-of-view (FOV). Syntax defaultFOV A FOV value between 0.0 and 180.0. Returns No return value. See Also SetFOV

253

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setDisplayDevice( deviceName [, width [ , height [, bpp [, fullScreen ]]]] ) Purpose Use the setDisplayDevice function to select a display device and to set the initial width, height and bits-per-pixel (bpp) setting, as well as whether the application is windowed or in fullScreen. Syntax deviceName width height bpp fullScreen A supported display device name. Resolution width in pixels. Resolution height in pixels. Pixel resolution in bits-per-pixel (16 or 32). A boolean value. If set to true, the application displays in fullscreen mode, otherwise it will attempt to display in windowed mode.

Returns Returns true on success, false otherwise. Notes If no resolution information is specified, the first legal resolution on this device's resolution list will be used. Furthermore, for each optional argument if the subsequent arguments are not specified, the first matching case will be used. Lastly, if the application is not told to display in full screen, but the device only supports windowed, the application will be forced into windowed mode. See Also getDesktopResolution, getDisplayDeviceList, getResolutionList, nextResolution, prevResolution, setRes, setScreenMode, switchBitDepth

setFov( FOV ) Purpose Use the setFov function to set the current field-of-view (FOV). Syntax FOV A FOV value between 0.0 and 180.0. Returns No return value. See Also setDefaultFov

254

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setOpenGLAnisotropy( 0.0 .. max.f ) Purpose Use the setOpenGLAnisotropy function to enable or disable anisotropic filtering. Syntax 0.0 .. max.f - A value between 0.0 and the maximum anisotropic level supported by the current machine. Selecting a value higher than max.f results in max.f. Returns No return value. Notes Anisotropic filtering is somewhat 'expensive' filtering technique that uses more texels than your average filtering technique (bilinear or trilinear) for determining the color of a pixel for cases where more than one texel may be responsible for that pixels color.

setOpenGLInteriorMipReduction( reductionVal ) Purpose Use the setOpenGLInteriorMipReduction function to set the texture quality for interiors. Syntax reductionVal An integer value between 0 and 5, with 0 being the lowest quality and 5 being the highest quality. Returns No return value. See Also setOpenGLMipReduction ,setOpenGLSkyMipReduction setOpenGLMipReduction( reductionVal ) Purpose Use the setOpenGLMipReduction function to control shape texture detail Syntax reductionVal An integer value between 0 and 5, with 0 being the lowest quality and 5 being the highest quality. Returns No return value. See Also setOpenGLInteriorMipReduction, setOpenGLSkyMipReduction

255

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setOpenGLSkyMipReduction( reductionVal ) Purpose Use the setOpenGLSkyMipReduction function to control texture detail for the skybox and clouds. Syntax reductionVal An integer value between 0 and 5, with 0 being the lowest quality and 5 being the highest quality. Returns No return value. See Also setOpenGLInteriorMipReduction, setOpenGLMipReduction setOpenGLTextureCompressionHint ( hint ) Purpose Use the setOpenGLTextureCompressionHint function to select the OpenGL texture compression method. Syntax hint "GL_DONT_CARE", "GL_FASTEST", or "GL_NICEST". text for information on what these mean.) (Please refer to an OpenGL

Returns No return value. setOpenGLTextureCompressionHint(GL_NICEST);

setRes( width , height , bpp ) Purpose Use the setRes function to set the screen to the specified width, height, and bits-perpixel (bpp). Syntax width Resolution width in pixels. height - Resolution height in pixels. bpp Pixel resolution in bits-per-pixel (16 or 32). Returns Returns true if successful, otherwise false. See Also getDesktopResolution, getDisplayDeviceList, getResolutionList, nextResolution, prevResolution, setDisplayDevice, setScreenMode, switchBitDepth

256

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setScreenMode( width , height , bpp , fullScreen ) Purpose Use the setScreenMode function to set the screen to the specified width, height, and bits-per-pixel (bpp). Additionally, if fullScreen is set to true the engine will attempt to display the application in full-screen mode, otherwise it will attempt to used windowed mode. Syntax width height bpp fullScreen Resolution width in pixels. Resolution height in pixels. Pixel resolution in bits-per-pixel (16 or 32). A boolean value. If set to true, the application displays in fullscreen mode, otherwise it will attempt to display in windowed mode.

Returns Returns true if successful, otherwise false. See Also getDesktopResolution, getDisplayDeviceList, getResolutionList, nextResolution, prevResolution, setDisplayDevice, setRes, switchBitDepth

setScreenMode( 1024 , 768 , 32 , false ); // 1024x768 32bpp windowed mode setVerticalSync( enable ) Purpose Use the setVerticalSync function to force the framerate to sync up with the vertical refresh rate. Syntax enable A boolean value. If set to true, the engine will only swap front and back buffers on or before a vertical refresh pass. Returns Returns true on success, false otherwise. Notes This is used to reduce excessive swapping/rendering. There is generally no purpose in rendering any faster than the monitor will support. Those extra 'ergs' can be used for something else.

257

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

switchBitDepth() Purpose Use the switchBitDepth function to toggle the bits-per-pixel (bpp) pixel resolution between 16 and 32. Returns Returns true on success, false otherwise. See Also getDesktopResolution, getDisplayDeviceList, getResolutionList, nextResolution, prevResolution, setDisplayDevice, setRes, toggleFullScreen() Purpose Use the toggleFullScreen function to switch from full-screen mode to windowed, or vice versa. Returns Returns true on success, false otherwise.

videoSetGammaCorrection( gamma ) Purpose Use the videoSetGammaCorrection function to adjust the gamma for the video card. Syntax gamma A floating-point value between 0.0 and 1.0. Returns No return value. Notes The card will revert to it's default gamma setting as long as the application closes normally. videoSetGammaCorrection($pref::OpenGL::gammaCorrection);

258

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Texture Types
Type 0 1 2 3 4 5 6 7 8 9 10 11 Regular bitmap Same as 1, but the data will be kept after creation Same as 1 except data will not be loaded to OpenGL and cannot be "bound" Internal ONLY. Same as 1, but has "small textures" Terrain texture. Sky Texture Interior Texture Bump Texture Inverted Bump Texture Detail Texture Zero Border Texture Description

259

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3.13. SPECIAL
calcExplosionCoverage( source, targetObject , coverageMask ) Purpose Use the calcExplosionCoverage function to calculate the total exposure for targetObject to an explosion located at source position and blocked by all objects having a tight as specified by coverageMask. In other words, this function will calculate how much of an explosive force is applied to an object if intervening objects block portions of the explosion. Syntax source A position vector. targetObject The name of ID of the object to check coverage for. coverageMask A bitmask containing object type masks for all objects that can block this explosion. 0 For none, and -1 for all. For specific types, please see the "Object Types Table" below. Returns Returns a value between 0.0 and 1.0, where 0.0 is no coverage and therefore no damage, and when 1.0 is full coverage.

%coverage = calcExplosionCoverage(%position, %targetObject, $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType); if (%coverage == 0) continue;

$TypeMasks::StaticObjectType $TypeMasks::TerrainObjectType $TypeMasks::WaterObjectType $TypeMasks::MarkerObjectType $TypeMasks::ShapeBaseObjectType $TypeMasks::StaticShapeObjectType $TypeMasks::ItemObjectType $TypeMasks::VehicleBlockerObjectType $TypeMasks::ExplosionObjectType

$TypeMasks::EnvironmentObjectType $TypeMasks::InteriorObjectType $TypeMasks::TriggerObjectType $TypeMasks::GameBaseObjectType $TypeMasks::CameraObjectType $TypeMasks::PlayerObjectType $TypeMasks::VehicleObjectType $TypeMasks::ProjectileObjectType

Object Types Table

260

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getControlObjectAltitude() Purpose Use the getControlObjectAltitude function to determine how high above the terrain the control object. Returns Returns a floating-point value equal to the distance above the terrain for the current control object. If the object is below the terrain, a zero is returned. See Also getControlObjectSpeed, getTerrainHeight getControlObjectAltitude();

getControlObjectSpeed() Purpose Use the getControlObjectSpeed function to determine how fast the control object is currently moving. Returns Returns a floating-point value equal to the magnitude of the current control objects velocity in the game world in meters-per-second. See Also getControlObjectAltitude, getTerrainHeight %player.getControlObjectSpeed();

getClipboard() Purpose Use the getClipboard function to get the contents of the GUI clipboard. Returns Returns a string equal to the current contents of the copy the clipboard, or a NULL strain if the copy clipboard is empty. See Also setClipboard

261

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getModPaths() Purpose Use the getModPaths function to get the current mod path information. Returns Returns a string equivalent to the complete current mod path, that is all pads that are visible to the file manager. See Also setModPaths getTerrainHeight( position ) Purpose Use the getTerrainHeight function to determine the height of the terrain at an XY position in the game world. Syntax position An XY position vector in the game world. Returns Returns the terrain height at XY position. See Also getControlObjectAltitude, getControlObjectSpeed %TerHeight = getTerrainHeight(%pos); isPointInside( position ) Purpose Use the isPointInside function to determine if an XYZ point is in side an interior. Syntax position An XYZ position vector in the game world. Returns Returns true if the XYZ position is inside an interior, otherwise returns false. Notes This will only work if the interior at position is using portals, otherwise inside and outside are equivalent. isPointInside(143 34 567);

262

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

pathOnMissionLoadDone() Purpose Use the pathOnMissionLoadDone function to load all path information from the currently loaded interiors. Returns No return value. pathOnMissionLoadDone(); setModPaths( path ) Purpose Use the setModPaths function to set the current mod path to the value specified in path. Syntax path A string containing a semi-colon (;) separated list of game and mod paths. Returns No return value. See Also getModPaths // Set the mod path which dictates which directories will be visible // to the scripts and the resource engine. $modPath = pushback($userMods, $baseMods, ";"); setModPaths($modPath);

setClipboard( string ) Purpose Use the setClipboard function to Set value on clipboard to string. Syntax string The new value to place in the GUI clipboard. Returns Returns true if successful, false otherwise. See Also getClipoard

263

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setZoomSpeed( delay ) Purpose Use the setZoomSpeed function to set the current zoom speed to the value specified in delay, where delay is the time it takes to transition across 90-degrees of field-of-view specified in milliseconds. The maximum delay is 2000 ms. Syntax delay An integer value between 0 and 2000, equal to the time it takes the camera across 90-degrees of FOV. setZoomSpeed( $pref::Player::zoomSpeed );

A.3.14. RESOURCE MANAGEMENT


dumpResourceStats() Purpose Use the dumpResourceStats function to dump a listing of the currently in-use resources to the console. This will include such things as sound files, font files, etc. Returns No return value. Notes For this to work, the engine must have been compiled with TORQUE_DEBUG defined. See Also purgeResources dumpResourceStats();

purgeResources() Purpose Use the purgeResources function to purge all game resources. Returns No return value. See Also clearTextureHolds, dumpResourceStats, dumpTextureStats, flushTextureCache purgeResources();

264

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3.15. SCENE
lightScene( [ completeCallback [ , force ] ) Purpose Use the lightScene function to relight a currently open mission. Syntax completeCallback A unadorned function name to execute when the re-light is done. force Optionally either "forceAlways" (always relights) , or "forceWritable" (only relight file is writeable). Returns No return value.

resetLighting() Purpose Use the resetLighting function to reset the OpenGL lighting model. Returns No return value.

A.3.16. CONTAINERS and RAYCASTS


ContainerBoxEmpty( mask , location , xRadius [ , yRadius , zRadius ] ) Purpose Use the ContainerBoxEmpty function to check for any objects of type mask within a box of variable size located at Syntax mask A bitmask corresponding to the type of objects to check for. See the 'Object Types Table' above. location An XYZ position vector in the game world, pinpointing the centroid of the bounding box. xRadius The radius of the bounding box in the X-axis. yRadius The optional radius of the bounding box in the Y-axis. zRadius The optional radius of the bounding box in the Z-axis. Returns Returns true, if not objects were found, and false if objects were found. Notes If the yRadius and zRadius values are not supplied, they are assumed to be equal to xRadius. See Also containerFindFirst, containerFindNext, ContainerRayCast

265

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

containerFindFirst( mask , location, , xRadius , yRadius , zRadius

Purpose Use the containerFindFirst function to find the first instance of an object matching the type mask within the specified bounding box. Syntax mask A bitmask corresponding to the type of objects to check for. See the 'Object Types Table' above. location An XYZ position vector in the game world, pinpointing the centroid of the bounding box. xRadius The radius of the bounding box in the X-axis. yRadius The optional radius of the bounding box in the Y-axis. zRadius The optional radius of the bounding box in the Z-axis. Returns Returns 0 if no objects are found, otherwise returns an integer representing the ID of the first object found. See Also ContainerBoxEmpty, containerFindNext, ContainerRayCast

containerFindNext() Purpose Use the containerFindNext function to find the next instance of an object in a box search previously initiated with containerFindFirst. bounding

Returns Returns 0 if no objects are found, otherwise returns an integer representing the ID of the next object found. See Also ContainerBoxEmpty, containerFindFirst, ContainerRayCast

266

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

ContainerRayCast ( startPos , endPos , mask [ , exempt ] ) Purpose Use the ContainerRayCast function to see if an object matching the specified mask type is positioned along a ray starting at startPos and ending at endPos. One object may be marked for expemption from ray cast collisions. Syntax startPos An XYZ vector containing the tail position of the ray. endPos An XYZ vector containing the head position of the ray. mask A bitmask corresponding to the type of objects to check for. See the 'Object Types Table' above. exempt An optional ID for a single object that ignored for this raycast. Returns Returns 0 if no objects were struck by the ray, or a non-zero integer representing the ID of the object that was struck. Notes The exempt field is used to keep ray casts originating at an object from hitting the object itself. See Also ContainerBoxEmpty, containerFindFirst, containerFindNext

ContainerSearchCurrDist() Purpose Use the ContainerSearchCurrDist function when using InitContainerRadiusSearch and ContainerSearchNext functions to find objects to determine the distance of the center of the last object found from the center of the current search container. Returns Returns a floating point value equal to the distance between center the last found container search item and the center of the container search box. Notes Caution! Do not call this function without first setting up a search container or the engine will crash. See Also ContainerSearchCurrRadiusDist, ContainerSearchNext, InitContainerRadiusSearch

267

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

ContainerSearchCurrRadiusDist() Purpose Use the ContainerSearchCurrDist function when using InitContainerRadiusSearch and ContainerSearchNext functions to find objects to determine the distance of the closest point of the last object found from the center of the current search container. Returns Returns a floating point value equal to the distance between center of the container search box and the point on the last found item that is closest to the container center. Notes Caution! Do not call this function without first setting up a search container or the engine will crash. See Also ContainerSearchCurrDist, ContainerSearchNext, InitContainerRadiusSearch %rad = ContainerSearchCurrRadiusDist();

ContainerSearchNext() Purpose Use the ContainerSearchNext function to find the next object in the currently defined search container. Returns Returns non-zero integer value equal to the ID of an object, or zero if no objects were found. Notes Caution! Do not call this function without first setting up a search container or the engine will crash. See Also ContainerSearchCurrDist, ContainerSearchCurrRadiusDist, InitContainerRadiusSearch

268

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

InitContainerRadiusSearch ( centerPos , radius , mask ) Purpose Use the InitContainerRadiusSearch function to find all objects matching the specified mask type within a bounding radius centered at centerPos. Syntax centerPos An XYZ vector specifying the world position of the search container's centroid. radius A floating-point value specifying the radius of the search container. mask A bitmask corresponding to the type of objects to check for. See the 'Object Types Table' above. Returns No return value. Notes This search is static. That is it will find all objects within the specified radius and then the found objects can be retrieved with ContainerSearchNext. To find new objects, you will have to re-initialize the search. See Also ContainerSearchCurrDist, ContainerSearchCurrRadiusDist, ContainerSearchNext

A.3.17. EDITORS
snapToggle() Purpose Use the snapToggle function to enable object placement toggling in the World Editor. Returns No return value. Notes With toggling enabled, objects will snap to the horizontal world grid when moved with the mouse.

A.3.18. BUILD
getBuildString() Purpose Use the getBuildString function to determine if this build is a "Debug" release, or a "Release" build. Returns Returns a string, either "Debug" for a debug build, or "Release" for a release build. See Also getCompileTimeString, getVersionNumber, getVersionString, isDebugBuild

269

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getCompileTimeString() Purpose Use the getCompileTimeString function to determine when the currently running engine was built. Returns Returns a string containing "Month Day Year at Hour:Minute:Second" showing when this executable was built. See Also getBuildString, getVersionNumber, getVersionString, isDebugBuild

getVersionNumber() Purpose Use the getVersionNumber function to get the version number of the currently executing engine. Returns Returns an integer representing the engine's version number. See Also getBuildString, getCompileTimeString, getVersionString, isDebugBuild

getVersionString() Purpose Use the getVersionString function to get the version name and number for the currently executing engine. Returns Returns a string containing a name and an integer representing the engine's version type and version number. See Also getBuildString, getCompileTimeString, getVersionNumber, isDebugBuild isDebugBuild() Purpose Use the isDebugBuild function to determine if this is a debug build. Returns Returns true if this is a debug build, otherwise false. See Also getBuildString, getCompileTimeString, getVersionNumber, getVersionString

270

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.3.19. TIME
getRealTime() Purpose Use the getRealTime function to the computer time in milliseconds. Returns Returns the current real time in milliseconds. See Also getSimTime Used to get the real time (in milliseconds) Returns a numeric echo(Time in milliseconds: @ getRealTime() );

getSimTime() Purpose Use the getSimTime function to get the time, in ticks, that has elapsed since the engine started executing. Returns Returns the time in ticks since the engine was started. See Also getRealTime function timeMetricsCallback() { return fpsMetricsCallback() @ " Time -- " @ " Sim Time: " @ getSimTime() @ " Mod: " @ getSimTime() % 32; }

A.3.20. GUIS
createCanvas( WindowTitle ) Purpose Use the createCanvas function to initialize the canvas. Returns Returns true on success, false on failure. See Also createEffectCanvas

271

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

createEffectCanvas( WindowTitle ) Purpose Use the createEffectCanvas function to initialize the effects canvas. Returns Returns true on success, false on failure. See Also createCanvas

A.3.21. MATH
getBoxCenter( box ) Purpose Use the getBoxCenter function to find the centroid of a cube (box). Syntax box A vector containing two three-element floating-point position vectors: "X1 Y1 Z1 X2 Y2 Z2". Returns Returns a vector containing a three-element floating-point position vector equal to the centroid of the area defined by box. %pos = getBoxCenter( %this.getWorldBox() ); getRandom( ) getRandom( max ) getRandom( min , max ) Purpose Use the getRandom function to get a random floating-point or integer value. function comes in three forms. This

The first getRandom() takes no arguments and will return a random floating-point value in the range of 0.0 to 1.0. The second getRandom( max ) takes one argument representing the max integer value this will return. It will return an integer value between 0 and max. The third getRandom( min , max ) takes two arguments representing the min and max integer values this will return. It will return an integer value between min and max. Syntax min Minimum inclusive integer value to return. max - Maximum inclusive integer value to return.

272

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Returns If no arguments are passed, will return a floating-point value between 0.0 and 1.0. If one argument is passed, will return an integer value between 0 and max inclusive. If two arguments are passed, will return an integer value between min and max inclusive. Notes Be sure to recognize the difference between the three variants of getRandom. no-args version will return a floating point. See Also getRandomSeed Only the

getRandomSeed() Purpose Use the getRandomSeed function to get the current seed for the random generator. Returns Returns an integer value representing the current seed of the random generator. Notes You can re-seed the generator with this value to allow you to recreate a random sequence. Merely save the seed and execute your random sequence. Later, to reproduce this sequence re-seed the generator with setRandomSeed and your saved value. Then, the generator will produce the same random sequence that followed the call to getRandomSeed. See Also getRandom, setRandomSeed %seed = getRandomSeed();

mAbs( val ) Purpose Use the mAbs function to get the magnitude of val. Syntax val An integer or a floating-point value. Returns Returns the magnitude of val. %abs = mAbs(76.3);

273

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mAcos( val ) Purpose Use the mAcos function to get the inverse cosine of val in radians. Syntax val A value between -1.0 and 1.0 equal to the cosine of some angle theta. Returns Returns the inverse cosine of val in radians. This value will be in the range [ 0 , 3.14159 ]. See Also mCos %acos = mAcos(-8,3);

mAsin( val ) Purpose Use the mAsin function to get the inverse sine of val in radians. Syntax val A value between -1.0 and 1.0 equal to the sine of some angle theta. Returns Returns the inverse sine of val in radians. This value will be in the range [ - 3.14159/2 , 3.14159/2 ]. See Also mSin mAtan( val ) Purpose Use the mAtan function to get the inverse tangent of val in radians. Syntax val A value between -inf.0 and inf.0 equal to the tangent of some angle theta. Returns Returns the inverse tangent of val in radians. This value will be in the range [ - 3.14159/2 , 3.14159/2 ]. See Also mTan

274

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mathInit( extention ) Purpose Use the MathInit function to install a specified math extensions, or to detect and enable all extensions. Syntax extension Can be detect C FPU MMX 3DNOW SSE Returns No return value. Notes Generally speaking, the best extension choice is to used detect. This will automatically detected and enable all extensions supported by the current processor. It will also print out a list of the extension that were enabled to the console. mathInit( detect ); any of these: Detect all supported extensions and enable. - Enable standard C extensions. - Enable floating-point-unit extensions. - Enable Intel MMX extensions. - Enable AMD 3DNOW extensions. - Enable Intel SSE extensions.

matrixCreate( posVec , rotVec ) Purpose Use the matrixCreate function to create a transform matrix from a three-element floatingpoint translation vector and a four-element floating-point rotation vector. Syntax posVec - A three-element floating-point translation vector: "PosX PosY PosZ". rotVec - A four-element floating-point rotation vector: "RotX RotY RotZ". These are rotations about the specified axes. Returns Returns a transform matrix of the form "PosX PosY PosZ RotX RotY RotZ theta". See Also MatrixCreateFromEuler

275

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

MatrixCreateFromEuler ( rotVec ) Purpose Use the MatrixCreateFromEuler function to calculate a transform matrix from a threeelement floating-point rotation vector. Syntax rotVec - A three-element floating-point rotation vector: "RotX RotY RotZ". These are rotations about the specified axes. Returns Returns a transform matrix of the form "0 0 0 X Y Z theta". See Also MatrixCreate MatrixMulPoint( transform , point ) Purpose Use the MatrixMulPoint function to multiply a seven element transform matrix by a three element point vector, producing a three element position vector. Syntax transform A seven-element transform matrix. point A three-element point/position vector. Returns Returns a three-element position vector. See Also MatrixMultiply, MatrixMulVector

276

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

MatrixMultiply( transformA , transformB ) Purpose Use the MatrixMultiply function to multiply two seven-element transform matrices to produce a new seven element matrix. Syntax transformA A seven-element "PosX PosY PosZ transformB A seven-element "PosX PosY PosZ transform RotX RotY transform RotX RotY matrix of the form RotZ theta". matrix of the form RotZ theta".

Returns Returns a seven-element matrix resulting from transiformA x transformB. See Also MatrixMulPoint, MatrixMulVector MatrixMulVector( transform , vector ) Purpose Use the MatrixMulVector function to multiply a seven-element transform matrix with a three-element matrix. Syntax transform A seven-element transform matrix of the form "PosX PosY PosZ RotX RotY RotZ theta". vector A three-element vector. Returns Returns three-element resulting from vector * transform. See Also MatrixMulPoint, MatrixMultiply mCeil( val ) Purpose Use the mCeil function to calculate the next highest integer value from val. Syntax val A floating-point value. Returns Returns an integer representing the next highest integer from val. See Also mFloor

277

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mCos( val ) Purpose Use the mCos function to get the cosine of the radian angle val. Syntax val A value between -3.14159 and 3.14159. Returns Returns the cosine of val. This value will be in the range [ -1.0 , 1.0 ]. See Also mAcos mDegToRad( val ) Purpose Use the mDegToRad function to convert degrees to radians. Syntax val A floating-point number representing some number of degrees. Returns Returns the equivalent of the degree value val in radians. See Also mRadToDeg mFloatLength( val , numDecimals ) Purpose Use the mFloatLength function to limit the number of decimal places in val to numDecimals. Syntax val A floating-point value. numDecimals An integer between 0 and inf representing the number of decimal places to allow val to have. Returns Returns a floating-point value equivalent to a truncated version of val, where the new version has numDecimals decimal places.

278

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mFloor( val ) Purpose Use the mFloor function to calculate the next lowest integer value from val. Syntax val A floating-point value. Returns Returns an integer representing the next lowest integer from val. See Also mCeil

mLog( val ) Purpose Use the mLog function to calculate the natural logarithm of val. Syntax val A numeric value. Returns Returns the natural logarithm of val. mPow( val , power ) Purpose Use the mPow function to calculated val raised to the power of power. Syntax val A numeric (integer or floating-point) value to be raised to a power. piower - A numeric (integer or floating-point) power to raise val to. Returns Returns val^power.

mRadToDeg( val ) Purpose Use the mRadToDeg function to convert radians to degrees. Syntax val A floating-point number representing some number of radians. Returns Returns the equivalent of the radian value val in degrees. See Also mDegToRad

279

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mSin( val ) Purpose Use the mSin function to get the sine of the radian angle val. Syntax val A value between -3.14159 and 3.14159. Returns Returns the sine of val. This value will be in the range [ -1.0 , 1.0 ]. See Also mAsin

mSolveCubic( a , b , c , d ) Purpose Use the mSolveCubic function to solve for x0, x1, x2 in third-order polynomial equation with factors a, b, c, d: aX^3 + bX^2 + cX + d = 0 Syntax a, b , c, d Polynomial factors (see above). Returns Returns x0, x1, x2: ( X + x0 ) ( X + x1 ) ( X + x2 )

mSolveQuadratic( a , b , c ) Purpose Use the mSolveCubic function to solve for x0, x1 in second-order polynomial equation with factors a, b, c: aX^2 + bX + c = 0 Syntax a, b , c Polynomial factors (see above). Returns Returns x0, x1: ( X + x0 ) ( X + x1 ) Notes Warning, x0 and x1 are inverted.

280

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

mSolveQuartic(a,b,c,d,e) Purpose Use the mSolveCubic function to solve for x0, x1, x2, x3 in fourth-order polynomial equation with factors a, b, c, d, e: aX^4 + bX^3 + cX^2 + dX + e = 0 Syntax a, b , c, d, e Polynomial factors (see above). Returns Returns x0, x1, x2, x3: ( X + x0 ) ( X + x1 ) ( X + x2 ) ( X + x3 ) Notes Broken. mSqrt( val ) Purpose Use the mSqrt function to calculated the square root of val. Syntax val A numeric value. Returns Returns the the squareroot of val. mTan( val ) Purpose Use the mTan function to get the tangent of the radian angle val. Syntax val A value between -3.14159/2 and 3.14159/2. Returns Returns the tangent of val. This value will be in the range [ -inf.0 , inf.0 ]. See Also mAtan

281

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setRandomSeed( startSeed ) Purpose Use the setRandomSeed function to initialize the random number generator with the initial seed of startSeed. Syntax startSeed The new starting seed value for the random generator. Returns No return value. See Also getRandom, getRandomSeed

VectorAdd( vecA , vecB ) Purpose Use the VectorAdd function to add two vectors of up to three elements each to each other Syntax vecA A vector of up to three elements. vecB A vector of up to three elements. Returns Returns the result of vecA + vecB. See Also vectorSub

VectorCross( vecA , vecB ) Purpose Use the VectorCross function to calculate the cross product of two vectors of up to three elements each. Syntax vecA A vector of up to three elements. vecB A vector of up to three elements. Returns Returns the result of vecA x vecB. Notes Remember, the resultant vector will always be an right angles to both input vectors. See Also VectorDot

282

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

VectorDist( vecA , vecB ) Purpose Use the VectorDist function to calculate distance between two vectors of up to three elements each. Syntax vecA A vector of up to three elements. vecB A vector of up to three elements. Returns Returns the result of " |Xa Xb| |Ya Yb| |Za Zb| ". See Also VectorLen

VectorDot( vecA , vecB ) Purpose Use the VectorCross function to calculate the dot product of two unit vectors of up to three elements each. Warning! Be sure to always normalize both vecA and vecB before attempting to find the dot product. Calculating a dot product using un-normalized vectors (non- unit vectors) will result in meaningless results. Syntax vecA A unit-vector of up to three elements. vecB A unit-vector of up to three elements. Returns Returns a scalar value equal to the result of vecA . vecB. be a single floating-point value in the range [ -1 , +1 ]. This value which will always

Notes If the return value is < 0, the inner-angle between the vectors is > 90 degrees. If the return value is == 0, the inner-angle between the vectors is == 90 degrees. If the return value is > 0, the inner-angle between the vectors is < 90 degrees. See Also VectorCross

283

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

VectorLen( vec ) Purpose Use the VectorLen function to calculate the length of vector vec. Syntax vec - A vector of up to three elements. Returns Returns a scalar representing the length of the vector vec. See Also VectorDist VectorNormalize( vec ) Purpose Use the VectorNormalize function to calculated the unit vector equivalent of the vector vec. Syntax vec - A vector of up to three elements. Returns Returns the unit vector equivalent of the vector vec. See Also VectorScale

VectorOrthoBasis( vec ) Purpose Use the VectorOrthoBasis function to calculate a 3x3 Row-Major formated matrix representing the orthogonal basis for the vector vec. Syntax vec - A four element vector of the form "AxisX AxisY AxisZ theta", where theta is the angle of rotation about the vector formed by the prior three values. Returns Returns a 3x3 Row-Major formated matrix.

284

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

VectorScale( vec , scale ) Purpose Use the VectorScale function to scale the vector vec by the scalar scale. Syntax vec - A vector of up to three elements. scale A numeric value (integer or floating-point) representing the scaling factor. Returns Returns a scaled version of the vector vec, equivalent to: " ( scale * X ) ( scale * Y ) ( scale * Z ) " See Also VectorNormalize

VectorSub( vecA , vecB ) Purpose Use the VectorSub function to subtract vecB from vecA. Syntax vecA Left side vector in subtraction equation. vecB - Right side vector in subtraction equation. Returns Returns a new vector equivalent to: "vecA - vecB" See Also vectorAdd

285

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.4 GUI Controls Quick Reference


A.4.1. Purpose
This appendix has been created to facilitate scripted creation and use of the standard Torque GUI controls. With the exclusion of the GuiControl profile class, all GUI classes with specific fields, methods, and/or callbacks are listed alphabetically, not by function.

A.4.2. GuiControlProfile Fields


Th GUIControlProfile (profile) class serves a similar purpose to that played by SimDataBlock, except in the context of GUI controls. Instances of this class are used to initialize common features of GUI controls. All GUI controls require a profile.

Creation Syntax
new GuiControlProfile ( GUIProfileName [ : parentProfile ] ) { field_0 = value; ... field_N = value; ... [dynamicfield_N = value;] };

Usage Syntax
Normally, the profile for a GUI control is selected out of the profile pulldown in the GUI Inspector, but they can also be set and changed directly via script:
new GUIControl( GUIControlName ) { profile = GUIProfileName; // ... }; // subsequently, it can be changed: GUIControlName.profile = GUIProfileName2;

Fields
GUIControlProfiles provide the following fields. Not all fields are used by all controls, nor are all fields interpretted the same by all controls that use them. Several examples of field useage are given in the GPGT GUI Sampler. 286

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name autoSizeHeight autoSizeWidth bitmap border borderColor borderColorHL borderColorNA borderThickness canKeyFocus cursorColor fillColor fillColorHL fillColorNA fontColor fontColorHL fontColorLink fontColorLinkHL fontColorNA fontColors[10] fontColorSEL fontSize fontType hasBitmapArray justify

Type Bool Bool String ColorI ColorI ColorI ColorI Integer Bool ColorI ColorI ColorI ColorI ColorI ColorI ColorI ColorI ColorI ColorI ColorI Integer String bool String

Description Auto-size the height-bounds of the control to fit it's contents. Auto-size the width-bounds of the control to fit it's contents. Location of this controls bitmap. For most controls, if border is > 0 a border will be drawn, some controls use this to draw different types of borders however. Border color, used to draw a border around the bounds if border is enabled. Used instead of borderColor when the object is highlighted. Used instead of borderColor when the object is not active or disabled. Thickness of border in pixels. True if the object can be given keyboard focus (in other words, made a first responder). Color for the blinking cursor in text fields (among other cases). Fill color, this is used to fill the bounds of the control if it is opaque. This is used instead of fillColor if the object is highlighted. This is used instead of fillColor if the object is inactive or disabled. Color of base font. \c0. Color of highlighted font. \c1. This is used as the font color for embedded URLs. This is used as the font color for embedded URLs when they are clicked or otherwise highlighted. Color of inactive font. \c2. Each of these corresponds to a font color \cn. Note: Instead of using fontColors for the first four, they should be specified using fontColor, fontColorHL, fontColorNA, and fontColorSel, in that order. Color of selected font. \c3. Size of font. Points or Pixels? Font face name for the control. Enables skinning in skinnable controls Justification for text: left center right If set to true, this is a modeless dialog meaning it will pass input through instead of taking it all. If set to true, this control should be "selected" while the mouse is over it. (Only used by guiTextListCtrl) If true and a text control, this control should accept only numeric inputs. If true, this control is not translucent. Used in GuiTextEditCtrl to specify if a tab-event should be simulated when return is pressed. Sound played when the object is "down" i.e. a button is pushed. Sound played when the mouse is over the object. This control is accessible via the tab key. (i.e. can be tabbed to).

modal mouseOverSelected numbersOnly opaque returnTab soundButtonDown soundButtonOver tab

Bool Bool Bool Bool Bool AudioProfile AudioProfile Bool

287

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name textOffset

Type Vector

Description Vector of two integers x-offset y-offset.

A.4.3. Standard GUI Controls (Alphabetical Listing)


GuiBitmapBorderCtrl
This skinnable control is used to adorn other controls with a frame (or border). If you have never designed a GUI skin, see Standard GUIs chapter in Tech School Section of GPGT.

Skinning

Define a profile with the following settings:


new GuiControlProfile ( aProfileName ) { // ... hasBitmapArray = true; bitmap = "path to bitmap array graphic"; };

Provide an image file with the following structure:


Column 0 Upper-left border Left border Column 1 Upper-right border Right Border Column 2 Top border Lower-left border Column 3 -Lower border Lower-right border Column 4

288

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiBitmapButtonCtrl
This control is a skinnable button. Unlike other skinned controls, this control takes a maximum of four normal (non-array) graphics. Graphics files for this control use the following naming convention:
prefix_tag.suffix

prefix Any name for the image file. _tag Any of the following (based on button state):

_n Normal _h Highlighted _d Depressed _i Inactive

suffix png, jpg, bmp, etc. For example, we could provide the following four images:

gglogo_n.png (normal)

gglogo_h.png (highlighted)

gglogo_d.png (depressed)

gglogo_i.png (inactive)

If an image file is not provided for any of the states: highlighted, depressed, or invalid, the normal image will be substituted. The normal image is always required. Also, setting the extent to "0 0" in the GUI Inspector and then pressing apply will cause the GUI to expand to the size of the image file.

Fields
Field Name Description Path to image file, must be of the format: bitmap "path\path\path\prefix" Do not provide tag or suffix ".\gglogo" Sample

Console Methods
setBitmap()

289

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setBitmap( pathName ) Purpose Use the setBitmap method to change the bitmap this control uses. Syntax pathName A path to a new texture for this control. Returns No return value.

GuiBitmapCtrl
This control is used to display a small bitmap. In TGE versions prior to 1.4, this control can only accept a bitmap with a maximum size of 256 x 256 pixels. For larger images or in case of malfunction, use the GUIChunkedBitmapCtrl control.

Fields
Field Name bitmap Description Path to image file. (Suffix is optional) Boolean value enabling wrapping. If wrap is false and the image is larger than the GUIBitmapCtrl extent, the image will be down-scaled. Vice-versa, if the extent is larger than the image it will be scaled up. However, if wrap is true, and the extent is smaller than the image, then only a portion of the image will be visible. Sample ".\gg_background.png"

wrap

[ false , true ]

Console Methods
setBitmap() setValue()

setBitmap( pathName ) Purpose Use the setBitmap method to change the bitmap this control uses. Syntax pathName A path to a new texture for this control. Returns No return value.

290

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setValue( xOffset , yOffset ); Purpose Use the setValue method modify the positioning of the bitmap this control displays. Syntax xOffset The pixel x-offset for the upper-left corner of this control's bitmap. yOffset The pixel y-offset for the upper-left corner of this control's bitmap. Returns No return value.

GuiButtonBaseCtrl
This is the base class to all other buttons and should NOT be used to make buttons. Its only job is to provide common fields and methods for the GuiBitmapButtonCtrl, GuiButtonCtrl, GuiCheckBoxCtrl, and GuiRadioCtrl.

Fields
Field Name Description Each button has a default type, but that type can be overridden to be: - PushButton Normal on/off button - ToggleButton On/Off button that toggles. - RadioButton When grouped with other radio buttons, only one button in the group may be on. groupNum should be specified when using this setting. Provide a positive integer greater than or equal to zero to group a set of radio buttons. i.e. to group three radio buttons, give them each the same groupNum. Text to be displayed on button. Not displayed on GuiBitmapButtonCtrl buttons with a valid image. PushButton ToggleButton RadioButton Sample

buttonType

groupNum (radio buttons only) text

-1 - Ungrouped [ 0 , inf ) - Grouped "Cancel"

Console Methods
getText performClick setText

getText() Purpose Use the getText method to get the current value of this control's text field. Returns Returns the current value of the text field for this control. See Also setText

291

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

performClick() Purpose Use the performClick method to force a click-event for this control. setText( newText ) Purpose Use the newText method to set the curent value of this control's text field. Syntax newText A string containing text to replace the text field's old value with. Returns No return value.

GuiButtonCtrl
The standard button. It defaults to a buttonType of PushButton. All functionality comes from its parent GuiBaseButtonCtrl.

GuiCanvas
The canvas is the owner of all controls. That is, all other controls are placed in the The canvas can always be found for scripting purposes using the name: 'Canvas'. In order to display another control, that control must be made the 'content' of the Alternately, dialogs, can be pushed onto a canvas layer. Only dialogs exist in layers. The dialog in the highest layer always receives the keyboard and mouse input, unless the dialog is modeless. In this case, the dialog will only capture the input if it is the focus, else the inputs will all through to the next layer. Note: Modal dialogs supersede all other controls below them when it comes to capturing input.

Console Methods
cursorOff hideCursor pushDialog setContent cursorOn isCursorOn renderFront setCursor getContent popDialog repaint setCursorPos getCursorPos popLayer reset showCursor

cursorOff() Purpose Use the cursorOff method to disable the cursor. Returns No return value.

292

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

cursorOn() Purpose Use the cursorOn method to enable the cursor. Returns No return value.

getContent() Purpose Use the getContent method to get the ID of the control which is being used as the current canvas content. Returns Returns the ID of the current canvas content (a control), or 0 meaning the canvas is empty.

getCursorPos() Purpose Use the getCursorPos method to retrieve the current position of the mouse pointer. Returns Returns a vector containing the x y coordinates of the cursor in the canvas.

hideCursor() Purpose Use the hideCursor method to hide the cursor. Returns No return value.

isCursorOn() Purpose Use the isCursorOn method to see if the cursor is currently enabled. Returns Returns true if cursor is on, false otherwise.

293

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

popDialog( handle ) Purpose Use the popDialog method to remove a currently showing dialog. If no handle is provided, the top most dialog is popped. Syntax handle The ID or a previously pushed dialog. Returns No return value. See Also pushDialog, popLayer popLayer( layer ) Purpose Use the popLayer method to remove (close) all dialogs in the specified canvas layer. Syntax layer A integer value in the range [ 0 , inf ) specifying the canvas layer to clear. Returns No return value. See Also pushDialog, popDialog pushDialog( handle [ , layer ] ) Purpose Use the pushDialog method to open a dialog on a specific canvas layer, or in the same layer the last openned dialog. Newly placed dialogs placed in a layer with another dialog(s) will overlap the prior dialog(s). Syntax handle The numeric ID or name of the dialog to be opened. layer A integer value in the range [ 0 , inf ) specifying the canvas layer to place the dialog in.

renderFront( enable ) Purpose Use the renderFront method to modify the canvas rendering order. Syntax enable A boolean value. If true, layers are rendered front-to-back, otherwise, the are rendered back-to-front (default). See Also popDialog, popLayer

294

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

repaint() Purpose Use the repaint method to force the canvas to redraw all elements. Returns No return value. reset() Purpose Use the reset method to reset the current canvas update region. Returns No return value.

setContent( handle ) Purpose Use the setContent method to set the control identified by handle as the current canvas content. Syntax handle The numeric ID or name of the control to be made the canvas contents. Returns No return value.

setCursor( cursorHandle ) Purpose Use the setCursor method to select the current cursor. Syntax cursorHandle The ID of a previously defined GuiCursor object. Returns No return value.

setCursorPos( ) Purpose Use the setCursorPos method to set the position of the cursor in the cavas. Syntax position An "x y" position vector specifying the new location of the cursor.

295

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

showCursor() Purpose Use the showCursor method to enable the display of the cursor. Returns No return value.

Gotchas

Do not try to run two copies of TGE in stand-alone mode unless the binary is complied to Debug. There is code in release compiles that prevents two or more copies of the canvas from running simultaneously on a Windows platform. You are probably safe on OSX and Linux, but if you encounter errors opening multiple instances of TGE, this may be the cause. Do not [ever] try to create a second instance of the Canvas within the same running image of TGE. You will crash or hang your game.

GUICheckBoxCtrl
This skinnable control displays the perennial check-box control. By default this control toggles between on and off. If you have never designed a GUI skin, see Standard GUIs chapter in Tech School Section of GPGT.

Skinning

Define a profile with the following settings:


new GuiControlProfile ( aProfileName ) { // ... hasBitmapArray = true; bitmap = "path to bitmap array graphic"; };

296

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Provide an image file with the following structure:


Sample Array Image Column 0 Unchecked Normal

Checked Normal

Unchecked Inactive

Checked Inactive

GuiChunkedBitmapCtrl
This control is the big brother to GuiBitmapCtrl and serves the same purpose. Its main value is that it can handle images larger than 256 x 256.

Fields
Field Name bitmap Description Path to image file. File extension is optional. Boolean value enabling wrapping. If tile is false and the image is larger than the GUIBitmapCtrl extent, the image will be down-scaled. Vice-versa, if the extent is larger than the image it will be scaled up. However, if tile is true, and the extent is smaller than the image, then only a portion of the image will be visible. See External Bitmap Specification below. Sample ".\ggbackground"

tile

[ false , true ]

useVariable

[ false , true ]

External Bitmap Specification


The GuiChunkedBitmapCtrl provides an interesting feature. It is possible to leave the bitmap field empty and to tell the control to get its bitmap from a global variable. To do this, specify your control similarly to this:
new GuiChunkedBitmapCtrl() { // ... useVariable = true; variable = "MyBitmap"; bitmap = ""; };

297

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Of course, for this to work we must have defined $MyBitmap:


$MyBitmap = ".\some\path\to\some\image";

GuiControl
GUI Control is the root class to all GUI controls and thus provides many fields and console methods. When it is used as a control, it is normally used as a container.

Fields
Member (Field) accelerator Description Specifies the hot-key for this command. See key mappings index. Specifies a command(s) to be executed on a specific control action. This field is used in a control-specific way. Specifies a command(s) to be executed on a specific control action. This field is used in a control-specific way. These two values specify the pixel WIDTH HEIGHT of the control. In short, this field affects re-sizing and repositioning of controls in relation to resolution. These two values specify the minimum size WIDTH HEIGHT of the control. Some controls provide an alternate/supplemental field that does something similar. Setting this does NOTHING. (Deprecated field) This value specifies the coordinate of the controls upper-left corner. This allows you to select a predefined profile for your control. Profiles are used to provide default field values for controls. Values you supply in the inspector will over-ride profile defined values. A value of <NULL> means, use no profile. Most profiles are defined the file: example\common\ui\defaultProfiles.cs. To fine all defined profiles, search for new ControlProfile in all .CS files. Sample/Range --

altcommand

echo("this is the altCommand");

command

echo("this is the command");

extent

--

horizSizing

right, width, left, center, relative

minExtent

--

modal

false

position

"0 0"

profile

SomeGUIProfile

298

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Member (Field) setFirstResponder

Description Setting this does NOTHING. (Deprecated field)

Sample/Range false

variable

This field is used to specify the name of a (global) console variable which will be updated with the value of the control. This field is used in a control-specific way. In short, this field affects re-sizing and repositioning of controls in relation to resolution. Specifies whether the GUI should start off in the visible state, or hidden.

"MyGlobalVar"

vertSizing

bottom, height, top, center, relative

visible

[ false , true ]

Console Methods
getExtent isActive resize setVisible getMinExtent isAwake setActive getPosition isVisible setProfile getValue makeFirstResponder setValue

getExtent() Purpose Use the getExtent method to determine the extent of the current control. Returns Returns a two-value integer vector containing the "x y" extent of the control.

getMinExtent() Purpose Use the getMinExtent method to determine the minimum allowed extent of this control. Returns Returns a two-value integer vector containing the "x y" minimum allowed extent of the control.

getPosition() Purpose Use the getPosition method to get the position of the upper-left corner of this control. Returns Returns a two-value integer vector containing the "x y" position of the control's upperleft corner.

299

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getValue() Purpose Use the getValue method to get the control-specific 'value' for this control. Returns Returns a control-specific specific value. Varies by control.

isActive() Purpose Use the isActive method to determine if this control is active. Returns Returns true if this control is active. Notes An inactive control may visible, but will not accept inputs. shade or re-skin itself to reflect its inactive state. It will also normally re-

isAwake() Purpose Use the isAwake method to determine if this control is awake. Returns Returns true if this control is awake and ready to display.

isVisible() Purpose Use the isVisible method to determine if this control is visible. Returns Returns true if the control is visible. Notes This can return true, even if the entire control covered by another. that the control will render if not covered. This merely means

300

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

makeFirstResponder( isFirst ) Purpose Use the makeFirstResponder method to force this control to become the first responder. Syntax isFirst A boolean value. If true, then this control become first reponder and at captures inputs before all other controls, excluding dialogs above this control. Returns No return value.

resize( ulX , ulY , width , height ) Purpose Use the resize method to resize and/or re-position a control. The upper-left corner of the control will be placed at <uliX, ulY> in its parent and the control will be given an extent of "width height". Syntax ulX ulY width height The The The The upper-left X coordinate of this control in pixels. upper-left Y coordinate of this control in pixels. width of this control in pixels. height of this control in pixels.

Returns No return value.

setActive( isActive ) Purpose Use the setActive method to (de)activate this control. Once active, a control can accept inputs. Controls automatically re-shade/skin themselves to reflect their active/inactive state. Syntax isActive A boolean value. f isActive is true, this control is activated, else it is set to inactive. Returns No return value.

301

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setProfile( profileName ) Purpose Use the setProfile method to change this control's profile to profileName. Syntax profileName A previously defined control profile. Returns No return value.

setValue( value ) Purpose Use the setValue method to set the control specific value to value. Purpose and type varies by control type. Syntax value Some control specific value. Returns No return value.

setVisible( isVisible ) Purpose Use the setVisible method to (un)hide this control. Syntax isVisible A boolean value. control will be hidden. Returns No return value. If true, the control will be made visible, otherwise the

Callbacks
onAction() onWake() onAdd() onRemove onSleep()

onAction( theControl ) Purpose This generic callback will fire if there is an action event and no value is specified for the command field. Syntax theControl The ID of this control.

302

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onAdd( theControl ) Purpose The onAdd callback is called when this control is created. Syntax theControl The ID of this control.

onRemove( theControl ) Purpose This callback fires when the control is destroyed. Syntax theControl The ID of this control.

onSleep( theControl ) Purpose This callback fires when the control is removed from the canvas, as a result of a call to setContent( control ), where control is not this control. This is also called when the control is destroyed, prior to calling onRemove(). Syntax theControl The ID of this control.

onWake( theControl ) Purpose This callback fires when the control is added to the canvas via the setContent( control ) method call, where control is this control. Syntax theControl The ID of this control.

GuiCursor
TGE allows us to define our own cursors, using a simple image file and some information defining the location of the cursor's hot-spot. In order to use a custom cursor, tell the canvas to activate it using the setCursor() method.

Fields
Field Name bitmapName hotSpot Path to cursor image. A two-element vector specifying the offset from the image's upper-left corner where the hot-spot of the cursor should be located. Description Sample ".\mycursor.png" "4 4" Offset by 4 pixels in x and y.

303

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiFadeInBitmapCtrl
This control is used to display an image by fading it in, then out over specified times. This control can be made to fade in and out continously, by putting it to sleep at the end of the fade out cycle and then waking it back up. This has to be done with a script and a call to schedule.

Fields
Field Name done fadeInTime fadeOutTime waitTime Description Boolean value denoting that this control is done fading out. Integer value specifying time to fade in (in milliseconds). Integer value specifying time to fade out (in milliseconds). Integer value specifying time to wait after fade-in completes, before starting to fade out. Sample Always initialize as false. [ 0 , inf ) [ 0 , inf ) [ 0 , inf )

Callbacks
click

click( theControl ) Purpose This callback is fired, if the control has the focus and either a mouse button is clicked, or a keyboard key is pressed. Syntax theControl The ID of this control.

GUIFilterCtrl
This odd control allows us to specify a multi-knotted spline-like GUI that can be used to create a vector of floating-povalues (one per knot), where each value is between 0.0 and 1.0. The control can be used both as an input device and as a feedback device (we can set the position of each knot from script).

Fields
Field Name controlPoints filter Description Number of knots to use. A floating-povector containing the default values for each knot. Values are bracketed between: [ 0.0 , 1.0 ] Sample 3 "0.25 0.5 0.75"

Console Methods
getValue() identity() setValue()

304

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getValue() Purpose Use the setVisible method to (un)hide this control. Syntax isVisible A boolean value. control will be hidden. Returns No return value. Returns a n-tuple floating-povector of values for each knot (left to right). identity() Purpose Resets the filter and places all knots on a line following a 45 degree angle from 0.0 (bottom) on the left to 1.0 (top) on the right. Returns No return value. setValue( knots ) Purpose Sets the knot value for each knot by position. Syntax knots a vector containing the knot positions for each knot. between 0.0 and 1.0.. Returns No return value. Each knot has a value If true, the control will be made visible, otherwise the

GuiInputCtrl
This control is used to capture all input events. Input events in this case are such things as mouse clicks and/or keystrokes. For every input event, a callback is fired.

Callbacks
onInputEvent()

305

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onInputEvent( theControl , deviceString , actionString , makeOrBreak ) Purpose Is called on make or break actions for all input devices. A make action for the mouse being a click-press, and a break action being the click-release. Syntax theControl deviceString actionString makeOrBreak The ID of this control. "keyboard", "mouse", "mouse", etc. (see devices strings below). Event + modifiers (see actions strings below). Was this a make or break action [ 0 , 1 ]?

Device Strings keyboard mouse joystick keyboard0 .. N mouse0 .. N joystick0 ..N Note: If there are multiple devices of the same type, specify an instance number. i.e. keyboard1. If there is only one instance, just using an instance number is acceptable, but not required. Keyboard Actions + Modifiers a .. z backspace shift capslock pageup up insert win_rwindow (win) lopt (mac) numpadadd numpaddivide lshift lalt equals semicolon lessthan A .. Z tab ctrl escape end right delete win_apps (win) ropt (mac) numpadsep numpadenter rshift ralt lbracket apostrophe exclamation F1 .. F24 return alt space home down help cmd (mac) numpad0 .. numpad9 numpadminus numlock lcontrol tilde rbracket comma grave 0 .. 1 enter pause pagedown left print win_lwindow (win) opt (mac) numpadmult numpaddecimal scrolllock rcontrol minus backslash slash greaterthan

Joystick/Mouse Actions button .. 31 Mouse Actions xaxis ryaxis yaxis rzaxis Joystick POV Actions xpov lpov upov2 ypov rpov dpov2 upov xpov2 lpov2 dpov ypov2 rpov2 zaxis slider rxaxis

306

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Miscellaneous Actions anykey nomatch

function myControl::onInputEvent( %this, %device, %action, %makeOrBreak ) { echo( "** onInputEvent called - device = ", %device, ", action = ", %action, ", makeOrBreak = ", %makeOrBreak, " **" ); }

GUIMenuBar
This semi-skinnable control displays the familiar menu bar control. By semi-skinnable, it is meant that graphic icons can be embedded in menu items, but the bar and the drop-downs themselves are not skinnable. If you have never designed a GUI image array, see Standard GUIs chapter GPGT.

Menu Item Icon Arrays

Define a profile with the following settings:


new GuiControlProfile ( aProfileName ) { // ... hasBitmapArray = true; bitmap = "path to bitmap array graphic"; };

Provide an image file with the following structure:


Sample Array Image Column 0 Checked Mark Optional Icon 0 (on) Column 1 Not-Checked Mark Optional Icon 0 (off) Column 2 Inactive Checked Mark Optional Icon 0 (inactive)

...

...

...

Optional Icon N (on)

Optional Icon N (of)

Optional Icon N (inactive)

In effect, a GUIMenuBar can have any number of icon row, but the first (0th) row is normally reserved for the 'checked' icons. You can of course use any icon for that you wish, and you can use those icons elsewhere too.

307

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GUIMenuBar Guidelines/Rules
The following guidelines/rules apply when building menus: 1. Build and Place Place and size the initial menu bar using the GUI Editor. 2. Populate Open the .gui file (or use a separate .cs) and write code to populate the menu. 3. Menus' and Menu Items' names should not start with a digit 4. Menu Items may optionally have accelerators 5. Menu Items may be enabled and disabled from script. 6. Menu Items may have separator lines (-----) between them. 7. Menus' and MenuItems' text can be dynamically changed from scripts. 8. Menu Items can be hidden. 9. Menu Items can have check box behavior and radio behaviors, including the display of a currently checked image in-menu. 10. Menus and Menu Items can be identified/referred to either by Menu/Menu Item text or ID. 11. Hierarchical (cascading) menus are not supported. 12. Menus do not support accelerators (only Menu Items support this)

Console Methods
Note: In the descriptions below, 'Menu Name' and 'Menu Item Name' refer to the Menu and Menu Item text respectively.
addMenu removeMenu setMenuItemEnable setMenuVisible addMenuItem removeMenuItem setMenuItemText clearMenuItems setMenuItemBitmap setMenuItemVisible clearMenus setMenuItemChecked setMenuText

addMenu( menuName , menuID ) Purpose Adds a new menu to the menu bar. Syntax menuName The text (name) of the new menu entry. menuID The ID of the new menu entry. Returns No return value.

308

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addMenuItem( menuID | menuName , menuItemName , menuItemID , [ accelerator ] , [ checkGroup ] ) Purpose Adds a sub-menu entry to the specified menu. Syntax menuID menuName menuItemID menuItemName accelerator The ID of the menu. The text (name) of the menu. The ID of the menu item. The text (name) of the menu item. A boolean value. If set to true, the sub-menu entry is checked, otherwise it is unchecked. checkGroup The check group this item should belong to, if any.

Returns No return value. clearMenuItems( menuID | menuName ) Purpose Removes all the sub-menu items from the specified menu. Syntax menuID The ID of the menu. menuName The text (name) of the menu. Returns No return value. clearMenus() Purpose Clears all menus and sub-menus from the menu bar. Returns No return value. removeMenu( menuID | menuName ) Purpose Removes the specified menu from the menu bar. Syntax menuID menuName menuItemID menuItemName checked The ID of the menu. The text (name) of the menu. The ID of the menu item. The text (name) of the menu item. A boolean value. If set to true, the sub-menu entry is checked, otherwise it is unchecked.

Returns No return value.

309

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

removeMenuItem( menuID | menuName , menuItemID | menuItemName ) Purpose Removes the specified menu item from the menu. Syntax menuID menuName menuItemID menuItemName The The The The ID of the menu. text (name) of the menu. ID of the menu item. text (name) of the menu item.

Returns No return value.

setMenuItemBitmap( menuID | menuName , menuItemID | menuItemName , bitmapIndex ) Purpose Sets the specified menu item bitmap index in the bitmap array. to -1 will remove any bitmap. Syntax menuID menuName menuItemID menuItemName bitMapIndex The ID of the menu. The text (name) of the menu. The ID of the menu item. The text (name) of the menu item. An integer value specifying the row of bitmap entries to use for sub-menu entry. Setting the item's index

Returns No return value.

setMenuItemChecked( menuID | menuName , menuItemID | menuItemName ,

checked )

Purpose Sets the menu item bitmap to a check mark, which must be the first element in the bitmap array. Any other menu items in the menu with the same check group become unchecked if they are checked. Syntax menuID menuName menuItemID menuItemName checked The ID of the menu. The text (name) of the menu. The ID of the menu item. The text (name) of the menu item. A boolean value. If set to true, the sub-menu entry is checked, otherwise it is unchecked.

Returns No return value.

310

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setMenuItemEnable( menuID | menuName , menuItemID | menuItemName , enabled ) Purpose Sets the menu item to enabled or disabled. Syntax menuID menuName menuItemID menuItemName enabled The ID of the menu. The text (name) of the menu. The ID of the menu item. The text (name) of the menu item. A boolean value. If set to true, the sub-menu entry is enabled, otherwise it is disabled.

Returns No return value.

setMenuItemText( menuID | menuName , menuItemID | menuItemName , newMenuItemText ) Purpose Sets the text of the specified menu item to the new string. Syntax menuID menuName menuItemID menuItemName newMenuItemText Returns No return value. The The The The The ID of the menu. text (name) of the menu. ID of the menu item. text (name) of the menu item. new text for the specified sub-menu entry.

setMenuItemVisible( menuID | menuName, menuItemID | menuItemName, visible ) Purpose Use the setMenuItemVisible method to enable or disable the visibility of a specific submenu entry. Syntax menuID menuName menuItemID menuItemName visible The ID of the menu. The text (name) of the menu. The ID of the menu item. The text (name) of the menu item. A boolean value. If set to true, this sub-menu entry will be shown, otherwise it will be hidden.

Returns No return value.

311

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setMenuText( menuID | menuName , newMenuText ) Purpose Sets the text of the specified menu to the new string. Syntax menuID The ID of the menu. menuName The text (name) of the menu. newMenuText The new text to give the menu entry. Returns No return value.

setMenuVisible( menuID | menuName , visible ) Purpose Use the setMenuVisible method to enable or disable the visibility of a specific menu entry. Syntax menuID The ID of the menu. menuName The text (name) of the menu. visible A boolean value. If set to true, this menu entry will be shown, otherwise it will be hidden. Returns No return value.

Callbacks
When a menu is clicked it calls the onMenuSelect() method before displaying the drop-down menu items list. This allows the callback to enable/disable menu items, add/remove menu items, etc. in a context-sensitive way. When a menu item is clicked, the drop-down menu items list removes itself from the display, then calls the onMenuItemSelect() method.
onMenuItemSelect onMenuSelect

onMenuItemSelect( theControl ,

menuID , menuName , menuItemID , menuItemName )

Purpose Called when a menu item is selected. Provides the menu's menuID, and the text in the menu menuName, as well as menu item's menuItemID, and the text in the menu Item menuItemName. Syntax theControl menuID menuName menuItemID menuItemName The The The The The ID of this control. ID assigned to this menu item. text assigned to this menu item. ID assigned to this sub-menu item. text assigned to this sub-menu item.

312

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onMenuSelect( theControl ,

menuID , menuName ) Provides the menu's menuID, and the text in the menu

Purpose Called when a menu is selected. menuName.

Syntax theControl The ID of this control. menuID - The ID assigned to this menu item. menuName The text assigned to this menu item. Returns No return value.

GuiMessageVectorCtrl
This control is normally used to build a chat hud, but it can be used for a number of other purposes as well. In order to use this control, a MessageVector object must also be used (see MessageVector below). Since the actual data to be displayed is stored in the MessageVector and not this control, we can remove and add GUIMessageVectorCtrl controls at will and not corrupt the message data. This control is capable of displaying colorized text and can support beyond the base ten colors. Additionally, this control will recognize URLs and supports opening an external browser on url-click events.

Fields
Field Name allowedMatches[16] Description This string(s) is used to match a message string. When we have a positive match, the matched string will be highlighted with matchColor color. Up to 16 strings can be matched (watched for). TBD Spacing between lines in pixels. Highlight color to use for strings matched against allowedMatches[n]. This index can be used to restrict or increase the number of supported text colors Sample/Range allowedMatches[0] = "GPGT"; allowedMatches[1] = "http"; integer [ 0 , inf ) "128 255 255" [ 0 , 99 ]

lineContinuedIndex lineSpacing matchColor maxColorIndex

Console Methods
attach detach

313

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

attach( aVector ) Purpose Make this gui control display messages from the specified MessageVector. Syntax aVector A previously created messageVector instance. Returns No return value.

detach() Purpose Stop listening to messages from the MessageVector this control was previously attached to. Returns No return value.

Callbacks
urlClickCallback

urlClickCallback( theControl , url ) Purpose Called when a URL is clicked in this control. Syntax theControl The ID of this control.

MessageVector
The MessageVector object is a container of text meant to be consumed by the GuiMessageVectorCtrl control. Having said that, this class can be used for various other text storage purposes too.

Message Vector Console Methods


clear getLineTag insertLine pushFrontLine deleteLine getLineText popBackLine dump getLineTextByTag popFrontLine getLineIndexByTag getNumLines pushBackLine

314

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

clear() Purpose Clear the message vector. Returns No return value.

deleteLine( lineIndex ) Purpose Delete the line at the specified position. Syntax lineIndex The line to delete in this vector. Returns No return value. See Also insertLine, pushBackLine, pushFrontLine

dump( filename [ , header ] ) Purpose Dump the message vector to a file, optionally prefixing the file with a header. Syntax filename The file to dump this vector to. header An optional string containing data to dump to the new file prior to dumping the vector. Returns No return value.

getLineIndexByTag( tag ) Purpose Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found. Syntax tag A special marker, possibly embedded in one or more lines in the vector. Returns Returns the line number of the first line found with the specified tag, otherwise returns -1. See Also insertLine, pushBackLine, pushFrontLine

315

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getLineTag(line) Purpose Use the getLineTag method to retrieve the tag for the specified line. Syntax line Line to search for tag in. Returns Returns a tag value or 0 indicating no tag found. See Also insertLine, pushBackLine, pushFrontLine

getLineText( index ) Purpose Use the getLineIndex method to get the text at a specified line. Syntax index The index in the vector from which to retrieve a line of text. Returns Returns the text at the specified line, or indicating a bad index. See Also insertLine, pushBackLine, pushFrontLine

getLineTextByTag( tag ) Purpose Use the getLineTextByTag method to scan through the lines in the vector, returning the first line that has a matching tag. Syntax tag An special marker that may have been used when creating lines in the vector. Returns Returns the contents of the first line found with a matching tag, or indicating no match. See Also insertLine, pushBackLine, pushFrontLine

316

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getNumLines() Purpose Use the getNumLines method to get the number of lines in the vector. Returns Returns an integer value equal to the line count for this vector. See Also insertLine, pushBackLine, pushFrontLine

insertLine( pos , msg [ , tag ] ) Purpose Use the insertLine method to insert a new line into the vector at the specified position. An optional tag may also be applied. Syntax pos The line at which to insert the new text. msg The text to add to this control. tag An optional tag to tag this line with. If not tag is supplied, a tag of 0 is used. Returns No return value. See Also pushBackLine, pushFrontLine

popBackLine() Purpose Use the popBackLine method to pop a line from the back of the list; destroys the line. Returns No return value. See Also insertLine, pushBackLine, pushFrontLine

popFrontLine() Purpose Use the popFrontLine method to pop a line from the front of the vector, destroying the line. Returns No return value. See Also insertLine, pushBackLine, pushFrontLine

317

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

pushBackLine( msg [ , tag ] ) Purpose Use the pushBackLine method to push a line onto the back of the list. Syntax msg The text to add to this control. tag An optional tag to tag this line with. Returns No return value. See Also popBackLine, popFrontLine, insertLine, pushFrontLine

If not tag is supplied, a tag of 0 is used.

pushFrontLine( msg [ , tag ] ) Purpose Use the pushFrontLine method to push a line onto the front of the vector. Syntax msg The text to add to this control. tag An optional tag to tag this line with. Returns No return value. See Also popBackLine, popFrontLine, insertLine, pushBackLine See Also popBackLine, popFrontLine, insertLine, pushBackLine

If not tag is supplied, a tag of 0 is used.

318

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiMLTextCtrl
This control is a multi-line markup-language supporting text control (ML == Markup Language). In addition to printing multi-line text, this control will accept TGE Markup-Language (TorqueML) formatted text, allowing us to make changes to the font, font-weight, color, etc. A complete listing of the TorqueML tokens and the Syntax for using them is supplied below. This control also supports onURL() and onResize() callbacks.

Fields
Field Name allowColorChars deniedSound lineSpacing maxChars text Enable colored text. Audio profile played when current number of characters == maxChars and an attempt is made to add new characters. Integer value specifying number of pixels between lines. Integer value specifying number of characters that will fit in this control. Initial text to dispaly in control. Description Sample/Range [ false , true ] Audio Profile 2 [ 0 , inf ) "Type Here"

Console Methods
addText() setAlpha() forceReflow() setCursorPosition() getText() setText() scrollToTag()

addText( text , reformat ) Purpose Use the addText method to add new text to the control. the control be reformatted. You may optionally request that

Syntax text Text to add to control. reformat A boolean value that when set to true forces the control to re-evaluate the entire contents and to redisplay it. Returns No return value. See Also getText, setText, forceReflow

319

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

forceReflow() Purpose Use the forceReflow method to force the text control to re-evaluate the entire contents and to redisplay it, possibly resizing the control. Returns No return value. See Also addText

getText() Purpose Use the getText method to return the current text contents of the control, including all formatting characters. Returns Returns the entire See Also addText text contents of the control or indicating no contents.

scrollToTag( tagID ) Purpose Use the scrollToTag method to scroll to the first instance of a tag if it exists. Syntax tagID A tag number to search for. These tags are specified by embedding TorqueML <tag:tag_number> entries in text. Returns No return value. See Also scrollToTop, setCursorPosition

scrollToTop() Purpose Use the scrollToTop method to scroll to the top of the text. Returns No return value. See Also scrollToTag, setCursorPosition

320

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setAlpha( alpha ) Purpose Use the setAlpha method to set alpha of this control to between [0.0 , 1.0]. Syntax alpha A floating point value between 0.0 and 1.0 indicating the control's new alpha setting.

setCursorPosition(newPos) Purpose Use the setCursorPosition method to offset the cursor by newPos characters into the current text contents of the control. Syntax newPos An integer value indicating the character position at which to place the cursor. Returns No return value. See Also scrollToTag, scrollToTop setText(text) Purpose Use the setText method to change the current text content of the control to text. replaces all old content. Syntax text The new contents for this control. Returns No return value. See Also addText, getText This

Callbacks
onURL() onResize()

onURL( theControl , url ) Purpose This callback is called when a hyperlink or a gamelink is clicked in the control. Syntax theControl The ID of this control. url The url value that was specified by the TorqeML link statement.

321

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Notes: If a normal URL was specified like this: <a:gamers.hallofworlds.com>name</a> , the value of url will be the string gamers.hallofworlds.com If a gamelinke URL was specified (starts with the string gamelink) like this: the <a:gamelink_SomeTopic>name</a> , the value of url will be the string gamelink_SomeTopic

onResize( theControl

, width , height )

Purpose This calleback is fired when the control is resized. Syntax theControl The ID of this control. width The new width for this control. height The new height for this control.

TGE Markup Language (TorqueML)


Markup Tag Fonts and Text Effects <font:font_name :font_size> Changes font type and size. <font:Arial Bold:16> <font:Verdana Italic:32> <font:Palatino Linotype:4> <font:Lucida Console:10> <color:FF0000> Pure Red <color:00FFCC > A Nice Green <shadow:10:10> down 10, right 10 <shadow:-5:-10> up 5, left 10 <color:C0C0C0> Light Gray Purpose Samples

font_name Any legal font name. font_size [ 1 , 32 ] points


Changes subsequent text color to that represented in hex_tag. Enables shadowed text with an offset of "XOffset YOffset" pixels. Changes subsequent text shadow color to that represented in hex_tag. Justifies text to left. Justifies text to right. Justifies text to center. Forces text to be clipped if is over pixels pixels wide. Clipped text has ... appended to the end.

<color:hex_tag> <shadow:XOffset :YOffset > <shadowcolor:hex_tag> Test Justification <just:left> <just:right> <just:center> Text Clipping <clip:pixels>text</clip>

<just:left> <just:right> <just:center> <clip:50>abcdefghijk</clip> Displays -> abcde... <clip:20>abcdefghijk</clip> Displays -> a... <lmargin%:5> <lmargin%:20> <rmargin%:5> <rmargin%:20> <lmargin:12>

Margins <lmargin%:percent> <rmargin%:percent > <lmargin:pixels> Sets left margin to percent percentage of viewable area. Sets right margin to percent percentage of viewable area. Sets left margin to pixels number of

322

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Markup Tag pixels. <rmargin:pixels> HyperLinks <a:link_address>text</a>

Purpose Sets right margin to pixels number of pixels. Creates a hyper-text link to link_address and displays text. Same as-<a:link_address> but address is not underlined. Can be used for making context sensitive help GUIs and other nonweb links. Changes subsequent hyperlink color to that represented in hex_tag. Displays a bitmap. Note: path/filename must be a complete path, starting at the mod directory. Use to create table like entities with columns tabstop pixels wide. Subsequently, Each new line is a table row. Columns are separated by tabs. Saves current attributes on stack. Pop last stacked attributes, restoring them. Do not use. Line break. Places a numeric tag tag_number in this line. This is used for searching and is not displayed.

Samples <lmargin:100> <rmargin:12> <rmargin:100> <linkcolor:0000FF><a:www.hallofworlds.com >HOW</a> Displays -> HOW <linkcolor:0000FF><a:gamelinkTopic0>Topic 0</a> Displays -> Topic 0

<a:gamelinkTAG>text</a>

<linkcolor:hex_tag> Images <bitmap:path/filename> Tables

<linkcolor:0000FF> For Blue Links

<bitmap:egt/client/ui/gglogo.png>

<tab:100,150,100> Subsequent lines should be treated as a three column table with columns 100, 150, and 100 pixels wide respectively.

<tab:tabstop,tabstiop,...,tabstop>

Attribute Stacking <spush> <spop> <sbreak> Miscellaneous <br> <div:> <tag:tag_number> <tag:100> Hello<br>World <spush> <spop> --

GuiMLTextEditCtrl
This control is a TorqueML formatted text entry. Nearly all of its functionality derives from its parent GuiMLTextCtrl (above).

Fields
Field Name escapeCommand Description Command to execute on escape key press (while editting). Sample/Range --

323

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiMouseEventCtrl
This control is used to capture and react to (via callback) all standard mouse events. Most controls do not automatically execute a callback for all mouse events. This helps reduce the possible flood of callbacks that would otherwise occur in complicated GUIs. However, when an event needs to be captured, and the control in question does not already do so, simply add this control as a child of the control needing to capture the event and be sure it covers the areas where the event should be captured. This control will then capture the following events:

Left/Right Mouse Down Left/Right Moue Up Mouse Move Left/Right Mouse Drag Mouse Enter Mouse Exit , with these (possible) modifiers:

Left/Right/Either Shift Left/Right/Either Control Left/Right/Either ALT

Fields
Field Name lockMouse Description If true, this control temporarily ignores the mouse. Sample [ false , true ]

Globals
The following globals are made available for script writting purposes.
Field Name $EventModifier::LSHIFT $EventModifier::RSHIFT $EventModifier::SHIFT $EventModifier::LCTRL $EventModifier::RCTRL $EventModifier::CTRL $EventModifier::LALT $EventModifier::RALT $EventModifier::ALT Left Shift-Key Depressed Right Shift-Key Depressed Either Shift-Key Depressed Left Ctrl-Key Depressed Right Ctrl-Key Depressed Either Ctrl-Key Depressed Left Alt-Key Depressed Right Alt-Key Depressed Either Alt-Key Depressed Description

324

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Callbacks
onMouseDown() onMouseMove() onMouseRightUp() onMouseDragged() onMouseUp() onMouseEnter() onMouseRightDown() onMouseLeave() onMouseRightDragged()

onMouse*( theControl , eventModifier , XY , numMouseClicks ) Purpose Called when the * event occurs. All onMouse* events take the same modifiers. The possibilities for * are:

Down Left mouse button-pressed. Dragged Mouse moved while left-button held down. Enter Mouse entered area covered by this control. Leave - Mouse left area covered by this control. Move - Mouse moved in area covered by this control Up Left mouse button-released. RightDown Right mouse button-pressed. RightDragged Mouse moved while right-button held down. RightUp Right mouse button-released.

As noted above, this callback will be passed the eventModifier(s) listed in globals(above). These modifiers are bit-masks and should be treated as such. Lastly, the double-click time is 500ms, thus if the mouse is clicked two or more times in that period of time, this callback will get a numMouseClicks > 1. This is used primarily to determine if a double-click or a single-click processing should occur. Syntax theControl eventModifier XY numMouseClicks

The One The The

ID of this control. or more of the globals specified above. position of the mouse in the current control. number of clicks recorded in the last 500ms.

GuiPopUpMenuCtrl
This is a traditional pop-up menu. When a left-mouse click is applied to this control, a list will pop up. This list will either be above or below the control dependent on its placement, how many entries are in the list, and the nearness of the bottom of its parent. In the case that the list is taller than the height of its parent or maxPopupHeight, it will scroll automatically. Additionally, each text entry can be themed with a coloring scheme.

325

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Fields
Field Name maxPopupHeight Description Integer value specifying the maximum number of list entries to show at any one time. List scrolls if there are more entries than maxPopupHeight. Sample/Range --

Console Methods
add() forceClose() getTextByID() setText() addScheme() forceOnAction() replaceText() size() clear() getSelected() setEnumContent() sort() findText() getText() setSelected()

add( entryText , entryID [ , entryScheme ] ) Purpose Use the add method to add a new entry with text entryText, ID entryID, and using the scheme entryScheme. Syntax entryText Text to display in menu entry. entryID ID to assign to entry. This value may be 1 or greater. entryScheme An integer value representing the ID of an optional color scheme to be used for this entry. Returns No return value. See Also addScheme, clear

326

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addScheme( entryScheme , fontColor , fontColorHL , fontColorSEL ) Purpose Use the addScheme method to create a new color scheme or to modify an existing one. Syntax entryScheme An integer value representing the ID of the scheme, between 1 and inf. fontColor A vector containing an integer representation of the menu entry's normal color. fontColorHL A vector containing an integer representation of the menu entry's highlighted color. fontColorSEL A vector containing an integer representation of the menu entry's selected color. Returns No return value. Notes An integer color vector contains three integer values, each between 0 and 255 and is organized in this order: R G B, See Also add

clear() Purpose Use the clear method to remove all entries and schemes from the menu. Returns No return value. See Also add, addScheme

findText( text ) Purpose Use the findText method to locate the string text in the list of menu items. return the ID of the first entry found. Syntax text A string containing the text to search for. Returns Returns an integer value representing the ID of the menu item, or -1 if the text was not found. Notes This is an exact match, so if the menu item is Gish and you search for Gis, or gish', or any other variation that does not match the entire menu item and the case of each letter, the search will not find a match. It will

327

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

forceClose() Purpose Use the forceClose method to force the menu to close. Returns No return value. Notes This is useful for making menus that fold up after a short delay when the mouse leaves the menu area. See Also forceOnAction

forceOnAction() Purpose Use the forceOnAction method to force the onAction callback to be triggered. Returns No return value. See Also forceClose, onAction (GUIControl callback)

getSelected() Purpose Use the getSelected method to get the ID of the last selected entry. Returns Returns the ID of the currently selected entry, or 0 meaning no menu was selected after the last menu open. Warning If someone opens and then closes the menu without making a selection, the selected entry goes back to 0, even if an entry was previously selected. See Also getText, setSelected

getText() Purpose Use the getText method to get the text currently displayed in the menu bar. Returns Returns the text in the menu bar or if no text is present. See Also getSelected, setText

328

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getTextById( ID ) Purpose Use the getTextById method to get the text value for the menu item represented by ID. Syntax ID An integer value representing the ID of a menu item. Returns Returns a string containing the menu item corresponding to ID, or a NULL string () if no menu item has the specified ID. See Also add, getText replaceText( enable ) Purpose Use the replaceText method to enable the updating of the menu bar text when a menu item is selected. Syntax enable A boolean value enabling or disabling the automatic updating of the menu bar text when a selection is made. Returns No return value. Notes This does not prevent changing the menu bar text with setText. See Also getText, setText

setEnumContent( className , enumName ) Purpose Use the setEnumContent method to fill the menu with a class reps field enumerations. Syntax className The class name associated with this enum content. enumName The name of the enumerated entry to add to the menu. This value must match an enum string as exposed by the engine for the class. The menu item will have the same text as the enum string name, and the ID will be equal to the enumerated entries value. Returns No return value.

329

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setSelected( ID ) Purpose Use the setSelected method to force the selection of a specific entry in the menu as identified by ID. Syntax ID An integer value representing the ID of the entry to select. Returns No return value. Notes This will cause the menu text to update and the onSelect callback will fire. If the control is currently in no-replace mode, the text in the menu bar will not be updated by this selection. See Also getSelected, onSelect (callback), replaceText, setText

setText( text ) Purpose Use the setText method to change the text displayed in the menu bar. Syntax text New text to display in the menu bar. Returns No return value. Notes Pass the NULL string () to clear the menu bar text. See Also getText, replaceText size() Purpose Use the size method to determine the number of entries in the menu. Returns Returns an integer value representing the number of menu items currently in the menu. See Also add

330

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

sort() Purpose Use the sort method to sort the menu in ascending order. Returns No return value. Notes This is a lexicographic sort, so number (1,2,3,...) will come before letters (a,b,c,...).

Callbacks
onCancel onSelect

onSelect( theControl , ID , text ) Purpose Called when an entry is selected from the list. Syntax theControl The ID of this control. ID ID of entry selected. text Text for entry selected. onCancel( theControl ) Purpose Called when a selection fails. i.e. if a scripted selection (setSelected()) provides an invalid entry ID, this event will be called. Syntax theControl The ID of this control. Returns No return value.

GuiProgressCtrl
This control is used to reflect a percentage. Furthermore, it is usually used to give feedback on the progress of a task. It does not add any new fields, methods, or callbacks and is derived from the GUIControl. To update the bar, simply use the setValue() methods with an argument between 0.0 and 1.0. Ex:
%myProgressBar.setValue(0.25); // Sets bar to 25% (left to right)

This control, does not have a text label, but this can easily be accomplished using a GuiTextCtrl.

331

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiRadioCtrl
This is a skinnable radio button control. It is used when a group of buttons must have only one button set at any one time. If you have never designed a GUI skin, see Standard GUIs chapter in Tech School Section of GPGT.

Skinning

Define a profile with the following settings:


new GuiControlProfile ( aProfileName ) { // ... hasBitmapArray = true; bitmap = "path to bitmap array graphic"; };

Provide an image file with the following structure:


Sample Array Image Column 0 Unchecked Normal

Checked Normal

Unchecked Inactive

Checked Inactive

In order for the radio control to behave properly, the buttons all need to have the same parent and groupNum. In this example, either "Radio 0" or "Radio 1" can be selected, but not both.
new guiControl() { new GuiRadioCtrl() { profile = "GuiRadioProfile"; //.. text = "Radio 0"; groupNum = "1"; buttonType = "RadioButton"; }; new GuiRadioCtrl() { profile = "GuiRadioProfile"; //.. text = "Radio 1"; groupNum = "1"; buttonType = "RadioButton";

332

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

};

};

GuiScrollCtrl
This is a container control used in concert with a GUIMLText, GUIMLEditTextCtrl, GuiTextListCtrl, and other resizeable controls. These resizeable controls are made children of the GuiScrollCtrl, which then allows the user to use scroll bars to move to a specific location in the child contol. GuiScrollCtrl can be programmed to supply a vertical and/or a horizontal scrollbar. These scrollbars will be enabled (based on field settings), always, never, or when the child content expands beyond the vertical or horizontal bounds of the view area.

Skinning

Define a profile with the following settings:


new GuiControlProfile ( aProfileName ) { // ... hasBitmapArray = true; bitmap = "path to bitmap array graphic"; };

333

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Provide an image file with the following structure:


Sample Array Image Column 0 Up-Scroll Normal Down-Scroll Normal Top of Vertical Thumb Normal Middle of Vertical Thumb Normal Bottom of Vertical Thumb Normal Vertical Bar Normal Right-Scroll Normal Left-Scroll Normal Left of Horizontal Thumb Normal Middle of Horizontal Thumb Normal Right of Horizontal Thumb Normal Horizontal Bar Normal Lower-Right Affordance Normal Column 1 Up-Scroll Depressed Down-Scroll Depressed Top of Vertical Thumb Depressed Middle of Vertical Thumb Depressed Bottom of Vertical Thumb Depressed Vertical Bar Depressed Right-Scroll Depressed Left-Scroll Depressed Left of Horizontal Thumb Depressed Middle of Horizontal Thumb Depressed Right of Horizontal Thumb Depressed Horizontal Bar Depressed Lower-Right Affordance Depressed Column 2 Up-Scroll Inactive Down-Scroll Inactive Top of Vertical Thumb Inactive Middle of Vertical Thumb Inactive Bottom of Vertical Thumb Inactive Vertical Bar Inactive Right-Scroll Inactive Left-Scroll Inactive Left of Horizontal Thumb Inactive Middle of Horizontal Thumb Inactive Right of Horizontal Thumb Inactive Horizontal Bar Inactive Lower-Right Affordance Inactive

Fields
Field Name childMargin Description A two-value integer vector specifying a horizontal and/or vertical offset for child entries. In effect this is a padding value. If this is true, the bar drag affordance does not scale to reflect the number of entries in the list. This helps when the list is very full. It keeps the drag bar from getting too thin to be grabbed/selected. Attributes for horizontal bar. Sample "4 4" (Provide 4 pixels of padding.) [ false , true ]

constantThumbHeight

hScrollBar

alwaysOn alwaysOff dynamic alwaysOn alwaysOff dynamic [ false , true ]

vScrollBar willFirstRepond

Attributes for vertical bar. Boolean value enabling first responder status.

334

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Console Methods
scrollToBottom() scrollToTop()

scrollToTop() Purpose Use the scrollToTop method to scroll the scroll control to the top of the child content area. Returns No return value. See Also scrollToBottom scrollToBottom() Purpose Use the scrollToBottom method to scroll the scroll control to the bottom of the child content area. Returns No return value. See Also scrollToTop

GuiSliderCtrl
This is a numeric slider control. It allows a value between a lower and upper range to be selected using a sliding interface.

Fields
Field Name range ticks value Description A two-element floating-point vector containing the the low and high values this control can take. Number of ticks to have on bar. Initial value. Sample "-10.0 100.0" "min max" 10 [ min , max ]

Console Methods
getValue()

335

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getValue() Purpose Use the getValue method to get the current value of the slider. Returns Returns current value of control (position of slider).

Notes
1. Holding SHIFT while dragging slider selects closest tick. 2. If altCommand is specified, it will be called every sim tick while this control is awake.

GuiTextCtrl
This is a label control. It displays a fixed (256 characters or fewer) amount of text. It can be updated dynamically from script if needed.

Fields
Field Name maxLength text Description Integer limit on length of text in label. Max is 256. Text is truncated once it reaches the current limit. Initial text for label. Sample 25 "Hello world"

Console Methods
setText()

setText( newText ) Purpose Use the setText method to set the content of label to newText. Syntax newText A string representing the new value for this label.

Notes
1. If altCommand is specified, it will be called when the keypad-enter or regular enter-key is pressed.

GuiTextEditCtrl
This is a simple single-line text entry control. It is a child of GuiTextCtrl and is thus limited to a maximum of 256 characters and can be limited with the same mechanisms provided by its parent. This control can also recall prior entries and allows them to be recalled via the up and down arrows on 336

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

the keyboard.

Fields
Field Name deniedSound escapeCommand historySize password Description Audio profile for sound that should be played when typing continues and the entry is full or at its maxLength limit. Script to execute when escape key is pressed an this control is active. Integer value determining number of prior entries to recall. Boolean value specifying this is a password. If true, insted of letters, asterisks will be printed. Boolean value specifying that this control will capture all key events. Note: This will be ignored of more than one GUITextEditCtrl within the same control has this set to true. Boolean value specifying that when the tab key is pressed, the onTabComplete callback should be executed. Script that should be executed when this control loses focus. Sample NoMoreSpaceSound -[ 0 , inf ) [ false , true ]

sinkAllKeyEvents tabComplete validate

[ false , true ] [ false , true ] --

Console Methods
getCursorPos() setCursorPos()

getCursorPos() Purpose Use the getCursorPos method to get the current position of the text cursor in the control. Returns Returns the current position of the text cursor in the control, where 0 is at the beginning of the line, 1 is after the first letter, and so on. See Also setCursorPos

337

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setCursorPos( newPos ) Purpose Use the setCursorPos method to set the current position of text cursor to newPos. Syntax newPos The new position to place the cursor at, where 0 is at the beginning of the line, 1 is after the first letter, and so on. Returns No return value. Notes If the requested position is beyond the end of text, the cursor will be placed after the last letter. If the value is less than zero, the cursor will be placed at the front of the entry. See Also getCursorPos

Callbacks
onTablComplete

onTabComplete( theControl ) Purpose This callback is executed if the tab key is pressed, and the tabComplete field is set to true. Syntax theControl The ID of this control.

GuiTextListCtrl
This control is a mult-line scrollable list. Alone it can be used to display data, but in concert other controls (buttons), it can be used as a selection control. Futthermore, this can be made the child of a GuiScrollCtrl to allow for long lists.

Fields
Field Name clipColumnText columns Description Boolean value instructing the control to clip contents of entries that extend beyond the visual edge of the current column. A vector contain a series of integer values corresponding to the pixel position for each column. Sample [ false , true ] 0 50 100

338

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Field Name enumerate fitParentWidth resizeCell No longer used.

Description Boolean value specifying that this control should expand to fit the extent (or other limits) of its parent. No longer used.

Sample -[ false , true ] --

Console Methods
addRow() getRowID() getSelectedID() rowCount() setSelectedByID() clear() getRowNumByID() isRowActive() scrollVisible() setSelectedRow() clearSelection() getRowText() removeRow() setRowActive() sort() findTextIndex() getRowTextByID() removeRowByID() setRowByID() sortNumerical()

addRow( ID , text [ , row ] ) Purpose Use the addRow method to add a new entry to the text list. Syntax ID The integer ID to assign to this entry. May be between 0 and inf and can be repeated. i.e., multiple entries may have the same ID. text The text to display for this entry in the list. row An optional integer value representing the position at which to add this entry, where 0 is the top of the list, 1 is after them first entry (if any), etc. Returns Returns the row number of the new entry. See Also clear, removeRow clear() Purpose Use the clear method to remove all entries from the list. Returns No return value. See Also addRow, clearSelection, removeRow

339

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

clearSelection() Purpose Use the clearSelection method to deselect the current selection (if any). Returns No return value. See Also clear, setSelection findTextIndex( text ) Purpose Use the findTextIndex method to do an exact-match search for text in the list of items. Syntax text The text to search for. Returns No return value. Notes This is an exact match, so if the menu item is Gish and you search for Gis, or gish', or any other variation that does not match the entire menu item and the case of each letter, the search will not find a match. See Also getRowText, getRowTextByID getRowId( row ) Purpose Use the getRowId method to get the ID value for a specified row. Syntax row The row in the list to check the ID for. Returns Returns the ID of the specified row, or -1 if row is out of range. Notes Row numbers start at 0. See Also addRow, getRowNumByID, getRowText, getRowTextByID Must match exactly or no match will occur.

340

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getRowNumById( ID ) Purpose Use the getRowNumById method to get the row number of the first entry in the list with the specified ID. Syntax ID An integer value equal to an the entry ID to search for. Returns Returns the number of the first row found with a matching ID, or -1 if no row contains the specified ID. Notes Row numbers start at 0. See Also addRow, getRowID, getRowText, getRowTextByID getRowText( row ) Purpose Use the getRowText method to retrieve the text value of an entry in the list at the specified row. Syntax row The number of the list row from which to retrieve the text. Returns Returns the text found at the specified row, or the NULL string () if the row number is out of bounds. Notes Row numbers start at 0. See Also addRow, getRowID, getRowNumByID, getRowTextByID getRowTextById( ID ) Purpose Use the getRowTextById method to get the text of the first row with an ID matching the passed ID. Syntax ID An integer value equal to the entry ID to search for. Returns Returns a string containing the text of the first row with a matching ID, or the NULL string () if no row matches the specified ID. See Also addRow, findTextIndex, getRowID, getRowNumByID, getRowTextByID

341

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getSelectedId() Purpose Use the getSelectedId method to return the ID value of the currently selected entry (if any). Returns Returns the integer ID of the currently selected row or -1 if no row is selected. See Also addRow, clearSelected, getRowID, getRowNumByID, getRowTextByID isRowActive( row ) Purpose Use the isRowActive method to determine if the specified row is active. Syntax row The row to check the active status for. Returns Returns 1 if the row is active, or 0 if the row is inactive or the specified row is out of bounds. Notes Row numbers start at 0. See Also setRowActive removeRow( row ) Purpose Use the removeRow method to remove the specified row from the list. Syntax row The number of the list row to be removed. Returns No return value. Notes Row numbers start at 0. Nothing is removed if row is out of bounds. See Also add, removeRowbyID

342

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

removeRowById( ID ) Purpose Use the removeRowById method to remove the first row containing a matching ID. Syntax ID An integer value equal to the entry ID of the row to delete. Returns No return value. See Also add, removeRow, rowCount rowCount() Purpose Use the rowCount method to determine how many entries are in the list. Returns Returns 0 if the list is entry or a positive integer value equal to the number of rows in the list if it is not empty. See Also add, removeRow scrollVisible( row ) Purpose Use the scrollVisible method to force the scrollList containing this text list to scroll so that the specified row is visible. Syntax row The number of the list row to be scrolled to. Returns No return value. Notes Row numbers start at 0. See Also rowCount

343

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setRowActive( row , active ) Purpose Use the setRowActive method to activate or deactivate the specified row. Syntax row The number of the list row to activate or deactivate. active A boolean value specifying whether this row is active or inactive. Returns No return value. Notes Row numbers start at 0. The row will not change visibly, but we can check if a selected row is active later to determine whether to respond or not to this selection. See Also isRowActive setRowById( ID , text ) Purpose Use the setRowById method to update the text if the first row fond with an ID matching the specified ID. Syntax ID An integer value equal to the entry ID of the row to change the text for. text The text to replace the found row value with. Returns No return value. setSelectedById( ID ) Purpose Use the setSelectedById method to selected a row by a specified ID. This will select the first row found to have an ID matching the specified ID. Syntax ID An integer value equal to the entry ID of the row to select. Returns No return value. Notes No selection will be made if no row has a matching ID. Additionally, if no selection is made and a prior row was selected, that selection will stay in effect. See Also setSelectedRow

344

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setSelectedRow( row ) Purpose Use the setSelectedRow method to select a specified row in the list. Syntax row The number of the list row to set as selected. Returns No return value. Notes Row numbers start at 0. No selection will be made if the row number is out of bounds. Additionally, if no selection is made and a prior row was selected, that selection will stay in effect. See Also setSelectedByID sort( columnID [ , ascending = false ] ) Purpose Use the sort method to sort the list using a lexicographic sort. either ascending or descending (default). The sort order may be

Syntax columnID The column to sort on. ascending An optional boolean value, which when true means to do an ascending sort, otherwise the sort will be descending. Returns No return value. Notes Columns may be specified when setting up the list, by default most lists have one column so the columnID should be 0. See Also sortNumerical

345

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

sortNumerical( columnID [ , ascending = false] ) Purpose Use the sortNumerical method to sort the list using a numeric sort. be either ascending or descending (default). Syntax columnID The column to sort on. ascending An optional boolean value, which when true means to do an ascending sort, otherwise the sort will be descending. Returns No return value. Notes Columns may be specified when setting up the list, by default most lists have one column so the columnID should be 0. See Also sort The sort order may

Callbacks
onDeleteKey onSelect

onDeleteKey( theControl , entry ) Purpose This callback is executed if the delete key is pressed, and this control is active. Syntax theControl The ID of this control. entry The ID of the currently selected text list item. onSelect( theControl , entry ) Purpose This callback is executed when an item in the text list is selected. Syntax theControl The ID of this control. entry The text value of the selected text list item.

346

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiTextEditSliderCtrl
This is another floating-poslider control, but it uses up-down buttons instead of a a left-right slider. This control is a bit more flexible in terms of its output as it uses a standard-C printf style formatting string.

Fields
Field Name Description A format string of the form: %[Flags][Width].[Precision][Size][Type] "%#5.5f" format See standard-C printf formatting rules for specifics. This is fed directly into an sprintf() command in TGE. A two-element floating-point vector containing the min/max range for this control. A floating-povalue specifying step size (per-click) "0.0 255.0" "min max" 1 Sample

range increment

Notes
1. If altCommand is specified, it will be called with an argument of 'false' when the keypad enter or regular enter key is pressed.

GuiWindowCtrl
This is a completely skinnable window control. It behaves like a standard window, providing the ability to drag, resize, minimize, maximize, restore, and close. If you have never designed a GUI skin, see Standard GUIs chapter in Tech School Section of GPGT.

Skinning

Define a profile with the following settings:


new GuiControlProfile ( aProfileName ) { // ... hasBitmapArray = true; bitmap = "path to bitmap array graphic"; };

347

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Provide an image file with the following structure:


Sample Array Image Column 0 Close Button Normal Maximize Button Normal Revert Button Normal Minimize Button Normal Title Bar Left Edge Title Bar Left Edge Inactive Left Edge Column 1 Close Button Depressed Maximize Button Depressed Revert Button Depressed Minimize Button Depressed Title Bar Right Edge Title Bar Right Edge Inactive Right Edge Column 2 Close Button Inactive Maximize Button Inactive Revert Button Inactive Minimize Button Inactive Title Bar Middle Title Bar Middle Inactive Lower Left Corner Column 3 -----Column 4 ------

-Bottom Edge

-Lower Right Corner

Fields
Field Name canClose canMaximize canMinimize canMove closeCommand minSize resizeHeight resizeWidth Description Allow window to be closed. Allow window to be maximized.If false, button does not render. Allow window to be minimized. If false, button does not render. Allow window to be moved. Command(s) to issue on close. Two-element integer vector specifying minimum <x,y> dimensions window can assume when drag-resizing. Enable (drag) height resizing. Enable (drag) width resizing. Sample [ false , true ] [ false , true ] [ false , true ] [ false , true ] [ false , true ] "10 20" [ false , true ] [ false , true ]

348

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiFrameSetCtrl
This control is used to automatically or manually frame any number of children controls, in regular row column format. The first time you try to use it; it may seem a little odd, but once you understand the rules by which it operates, you'll be using it for all kinds of tasks. Frames are organized as a grid, where frame 0 is the upper-left corner frame, frame N is the lower-right frame. Frame numbers increment left-to-right and top-to-bottom. i.e. A control with 3 rows and 3 frames would have these frame numbers:

0 | 3 | 6 |

1 4 7

| 2 | 5 | 8
Fields

-----------------------

Field Name autoBalance borderColor borderEnable

Description If set to true, the control will attempt to make all cells have the same height and width on waking. An integer color vector representing the color of the control's borders. An enumerated string value determining if borders are rendered. This affects the filling of the border, not its presence. If you want no border, set borderWidth to 0. An enumerated string value determining if borders are mouse draggable. An integer value representing the pixel width of the borders. An integer vector specifying the pixel starting position of each new column. An integer value representing the number of pixels to subtract from the ends of borders (making them shorter). Does not affect ability to grab borders. An integer vector specifying the pixel starting position of each new row.

Range/Sample [ false , true ] "255 128 128" alwaysOn alwaysOff dynamic alwaysOn alwaysOff dynamic [ 0 , inf ] 0 100 200

borderMovable borderWidth columns

fudgeFactor rows

[ 0 , inf ] 0 100 200

Console Methods
addColumn() frameMovable() getRowOffset() setRowOffset() addRow() getColumnCount() removeColumn() frameBorder() getColumnOffset() removeRow() frameMinExtent() getRowCount() setColumnOffset()

349

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addColumn() Purpose Use the addColumn method to add another column to the control. Returns No return value. Notes The current contents of the GUIFrameCtrl may shift to fill the new column. New columns are added on the right of the control. See Also addRow, removeColumn, removeRow

addRow() Purpose Use the addRow method to add another row to the control. Returns No return value. Notes The current contents of the GUIFrameCtrl may shift to fill the new row. New rows are added on the bottom of the control. See Also addColumn, removeColumn, removeRow

frameBorder( index [ , enable = true ] ) Purpose Use the frameBorder method to change the frame's enable state. Syntax index Frame index to enable/disable/ enable Currently a boolean, but should actually be a string: alwaysOn, alwaysOff, dynamic. Returns No return value. Notes This function is not working as of this writing.

350

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

frameMinExtent(index, w, h ) Purpose Use the frameMinExtent method to set the minimum extent allowed for a frame. Syntax index The frame number w Minimum width in pixels. h - Minimum height in pixels. Returns No return value. Notes These minimum extents do not prevent a parent control from collapsing the frame control and its frames. These limits apply to dragging and resizing as is done with the frames' draggable borders.

frameMovable( index [ , enable = true ] ) Purpose Use the frameMovable method to change the frame's draggable state. Syntax index Frame index to enable/disable/ enable Currently a boolean, but should actually be a string: alwaysOn, alwaysOff, dynamic. Returns No return value. Notes This function is not working as of this writing.

getColumnCount() Purpose Use the getColumnCount method to determine the number of columns in this control. Returns Returns an integer value equal to the number of columns in this frame. See Also getRowCount

351

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

getColumnOffset( column ) Purpose Use the getColumnOffset method to determine the current pixel location of the specified column. Syntax column An integer value specifying the column to examine. Returns Returns the pixel offset for the specified column. Notes Column 0 is the first column on the left side of frame 0. of frame 0 and the left side of frame 1, etc. See Also getRowOffset, setColumnOffset, setRowOffset Column 1 is on the right side

getRowCount() Purpose Use the getRowCount method to determine the number of rows in this control. Returns Returns an integer value equal to the number of rows in this frame. See Also getColumnCount

getRowOffset( row ) Purpose Use the getRowOffset method to determine the current pixel location of the specified row. Syntax row An integer value specifying the row to examine. Returns Returns the pixel offset for the specified row. Notes Row 0 is the first row on the top of the first row of frames. Row 1 is below the first row of frames and above the second row of frames, etc. 1, etc. See Also getColumnOffset, setColumnOffset, setRowOffset

352

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

removeColumn() Purpose Use the removeColumn method to remove a column from the right side of the control. Returns No return value. Notes Columns are removed right to left. See Also addColumn, addRow, removeRow

removeRow() Purpose Use the removeRow method to remove the bottom row from the control. Returns No return value. Notes Rows are removed bottom to top. See Also addColumn, addRow, removeColumn

setColumnOffset( column , offset ) Purpose Use the setColumnOffset method to determine the current pixel location of the specified column. Syntax column An integer value specifying the column to examine. offset An integer value specifying the new column offset in pixels. Notes Column 0 is the first column on the left side of frame 0. of frame 0 and the left side of frame 1, etc. The left-most and right-most columns cannot be moved. See Also getColumnOffset, getRowOffset, setRowOffset

Column 1 is on the right side

353

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setRowOffset( row , offset ) Purpose Use the setRowOffset method to set the current pixel location of the specified row. Syntax row An integer value specifying the row to modify. offset An integer value specifying the new row offset in pixels. Notes Row 0 is the first row on the top of the first row of frames. Row 1 is below the first row of frames and above the second row of frames, etc. 1, etc. The bottom-most and top-most rows cannot be moved. See Also getColumnOffset, getRowOffset, setColumnOffset

354

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.5 Callbacks Quick Reference


A.5.1. Game Callbacks
These of callbacks are the ones that you will encounter after the game has started.

aiPlayer::
onMoveStuck( Obj ) Purpose Called when aiPlayer gets stuck while moving. Syntax Obj The object this callback is called for. onReachDestination( Obj ) Purpose Called when aiPlayer reaches last programmed 'destination'. Syntax Obj The object this callback is called for. onTargetEnterLOS( Obj ) Purpose Called when current target of Obj aiPlayer comes into it's field-of-view, and therefore line of sight. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onTargetExitLOS( Obj )

Purpose Called when aiPlayer loses sight of current target. comes Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for.

355

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

gameBaseData::
onAdd( DB , Obj ) Purpose Called when Obj object is added to the scene. initialization work required for Obj object. This callback should be used to do any

Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onNewDataBlock( DB , Obj ) Purpose Called whenever a datablock needs to be registered on the server. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onRemove( DB , Obj ) Purpose Called just before Obj object is removed from the scene. to do any cleanup work required for Obj object. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. This callback should be used

itemData::
onEnterLiquid( DB , Obj , percentCovered , liquidType ) Purpose Called when an item enters water. Syntax DB Obj percentCovered liquidType The ID of the datablock this callback is executed on. The object this callback is called for. A value between 0.0 and 1.0 equivalent to the water coverage. Water = 0, OceanWater = 1, RiverWater = 2, StagnantWater = 3, Lava = 4, HotLava = 5, CrustyLava = 6, Quicksand = 7

356

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onLeaveLiquid( DB , Obj , liquidType ) Purpose Called when an item exits water. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. liquidType - Water = 0, OceanWater = 1, RiverWater = 2, StagnantWater = 3, Lava = 4, HotLava = 5, CrustyLava = 6, Quicksand = 7 onStickyCollision( DB , Obj ) Purpose Called when an item has a sticky collision. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for.

lightningData::
applyDamage( DB , Obj , hitObject, hitPosition , hitNormal ) Purpose Called when lightning strikes an object. Syntax DB Obj hitObject hitPosition hitNormal The ID of the datablock this callback is executed on. The object this callback is called for. ID of object struck by lightning. Position of strike. Surface normal at strike position.

pathCamera::
onNode( Obj , node ) Purpose Called when a path camera gets to a node on its path. Syntax Obj The object this callback is called for. node The number of the node that the camera got to.

357

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

playerData::
animationDone( DB , Obj ) Purpose Called when a scripted animation (playThread) completes. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. Notes This doesn't specify which animation completed, just that an animation started by playThread completed. doDismount( DB , Obj ) Purpose Called when the player is mounted to another shape, and $mvTrigger2 is set to 1. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onEnterLiquid( DB , Obj , percentCovered, liquidType ) Purpose Called when this player enters a waterblock. Syntax DB Obj percentCovered liquidType The ID of the datablock this callback is executed on. The object this callback is called for. A value between 0.0 and 1.0 equivalent to the water coverage. Water = 0, OceanWater = 1, RiverWater = 2, StagnantWater = 3, Lava = 4, HotLava = 5, CrustyLava = 6, Quicksand = 7

onEnterMissionArea( DB , Obj ) Purpose Called when this player enters the mission area. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for.

358

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onLeaveLiquid( DB , Obj , liquidType ) Purpose Called when this player exits a waterblock. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onLeaveMissionArea( DB , Obj ) Purpose Called when this player leaves the mission area. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for.

projectileData::
onCollision( colliderDB , colliderObject , collidedObj , fade , pos , normal ) Purpose Called when a projectile strikes another object. Syntax colliderDB colliderObj collidedObject fade Datablock handle for Obj projectile object. Handle to Obj instance of the projectile object. Handle to the instance of the object the projectile has struck. How much Obj projectile has faded at the time of collision [0.0 , 1.0]. pos World position the collision occurred. normal The normal vector for the surface that was struck.

onExplode( DB , Obj , position , fade ) Purpose Called when an projectile explodes. Syntax DB Obj position fade The ID of the datablock this callback is executed on. The object this callback is called for. World position the collision occurred. How much the projectile has faded at the time of explosion [0.0 , 1.0].

359

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

shapeBaseData::
onCollision( colliderDB , colliderObject , collidedObj , vec , speed ) Purpose Called when a shape collides with an object. Syntax colliderDB colliderObj collidedObject vec speed Datablock handle for Obj projectile object. Handle to Obj instance of the projectile object. Handle to the instance of the object the projectile has struck. The collision vector for Obj object. The velocity of the collision. Just the magnitude of the collision vector.

onDamage( DB , Obj , damage ) Purpose The onDamage callback executes when the shape using this datablock receives damage or repair. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. damage Damage or repair that was applied to this shape. onDestroyed( DB , Obj ) Purpose Called when a shape is destroyed. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onDisabled( DB , Obj ) Purpose Called when a shape is disabled. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onEnabled( DB , Obj ) Purpose Called when a shape is enabled. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for.

360

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onEndSequence( DB , Obj , slot ) Purpose Called when a non-cyclic animation ends for this shape. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. onImpact( colliderDB , colliderObject , collidedObj , vec , speed ) Purpose Called when a shape impacts an object. Syntax colliderDB colliderObj collidedObject vec speed Datablock handle for Obj projectile object. Handle to Obj instance of the projectile object. Handle to the instance of the object the projectile has struck. The collision vector for Obj object. The velocity of the collision. Just the magnitude of the collision vector.

onMount( DB , Obj , mountToObject , node ) Purpose Called when this object mounts to another object. Syntax DB Obj mountToObject node The The The The ID of the datablock this callback is executed on. object this callback is called for. object this shape mounted to. node that this shape mounted to.

onTrigger( DB , Obj , triggerNum , triggerVal ) Purpose Called when this object is told that a trigger was pressed or released. Syntax DB Obj triggerNum triggerVal The ID of the datablock this callback is executed on. The object this callback is called for. Ths trigger number (between 0 and 6 ). 1, if the trigger was pressed. 0, if the trigger was released.

361

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onUnmount( DB , Obj , mountToObject , node ) Purpose Called when this object dismounts from another object. Syntax DB Obj mountToObject node The The The The ID of the datablock this callback is executed on. object this callback is called for. object this shape dismounted from. node that this shape dismounted from.

shapeBaseImage::
onMount( DB , mountToObject , node ) Purpose Called when this image mounts to a shape. Syntax DB The ID of the datablock this callback is executed on. mountToObject The object this image is mounted to. slot The slot (for the shape this image is mounted to) this image is mounted in. onUnmount( DB , mountToObject , node ) Purpose Called when this image dismounts from a shape. Syntax DB The ID of the datablock this callback is executed on. mountToObject The object this image is mounted to. slot The slot (for the shape this image is mounted to) this image is mounted in. userCallback( DB , mountToObject , slot ) Purpose Called by an image's state machine. 'userCallback' can be anything the user specifies in the state machine's 'userCallback' fields. Syntax DB The ID of the datablock this callback is executed on. mountToObject The object this image is mounted to. slot The slot (for the shape this image is mounted to) this image is mounted in.

362

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

triggerData::
onEnterTrigger( DB , Obj , intruder ) Purpose Called when an object enters the bounds of this trigger. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. intruder The object interacting with this trigger. onLeaveTrigger( DB , Obj , intruder ) Purpose Called when an object leaves the bounds of this trigger. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. intruder The object interacting with this trigger. onTickTrigger( DB , Obj , intruder ) Purpose Called when a trigger tick goes by and there are objects within the bounds of this trigger. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. intruder The object interacting with this trigger. onTrigger( DB , Obj , enter ) Purpose Executes when a group trigger enter or leave event occurs. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. enter If an object entered the group trigger, this value is 1, otherwise it is 0. onTriggerTick( DB , Obj )

Purpose Called when a group trigger ticks and objects are within the bounds of that trigger. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for.

363

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

vehicleData::
onEnterLiquid( DB , Obj , percentCovered , liquidType ) Purpose Called when this vehicle enters a water block. Syntax DB Obj percentCovered liquidType The ID of the datablock this callback is executed on. The object this callback is called for. A value between 0.0 and 1.0 equivalent to the water coverage. Water = 0, OceanWater = 1, RiverWater = 2, StagnantWater = 3, Lava = 4, HotLava = 5, CrustyLava = 6, Quicksand = 7

onLeaveLiquid( DB , Obj , liquidType ) Purpose Called when this vehicle leaves a water block. Syntax DB The ID of the datablock this callback is executed on. Obj The object this callback is called for. liquidType - Water = 0, OceanWater = 1, RiverWater = 2, StagnantWater = 3, Lava = 4, HotLava = 5, CrustyLava = 6, Quicksand = 7

A.5.2. GUI Callbacks


Please see GUI Controls Quick Reference.

364

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.5.3. Other Callbacks


scriptObject::/scriptGroup::
onAdd( Obj ) Purpose Called when a scriptObject or scriptGroup is created. Syntax Obj The object this callback is called for. onRemove( Obj ) Purpose Called when a scriptObject or scriptGroup is about to be destroyed. Syntax Obj The object this callback is called for.

Game
onExit() Purpose The onExit callback executes when the game starts to shutdown.

365

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.6 Scripted Systems Quick Reference


The EGTGE Kit comes with the following 'systems': Volumes 1 & 2

Simple Task Manager This is a semi-advanced self-executing task management system that allows you to create a list of tasks. Tasks can be functions, statements, methods targeting some object, or some combination thereof. The task manager can then be told to start self-executing, or it can be manually forwarded, based on one's need. There is also a manager-manager that can be enabled to watch for rogue task managers, etc. Simple Inventory This is a standard inventory system implemented using a scriptObject instead of in the current control object. This system is described thoroughly in Volume 1 Inventories. EGTGE Utilities Although not actually a system, there are several utility functions and methods provided with the kit. Their use is documented here.

Next Book ONLY


EGTGE Brains This is a simple, yet very flexible, state-machine manager that can be be used to drive anything from complex user interfaces to AI players and vehicles. EGTGE Ranged Weapons In the Weapons chapter of Volume 2, we discuss the difficult task of designing a set of weapons. This discussion leads to the definition and implementation of a weapons system designed to handle any type of mounted, thrown, or deployed ranged weapon.

This e-document contains references for each of the above systems. Please note, although this document is included in volume 1, the actual volume 2 systems are not available unless you have purchased that volume.

366

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.6.1. Simple Task Management System (SimpleTaskMgr)


While experimenting with the original TGE FPS kit, I came across some code embedded in the AI handling scripts. Basically, this code created a queue of tasks and then executed them. I thought, "Gee that would be useful for a variety of things. It sure would be nice if that functionality were centralized instead..." Thus, SimpleTaskMgr was born. This 'task management system' can be used in both client and server space and provides the following features:

Tasks can be enqueued in a 'task queue' A task queue can operate standalone (i.e. each task is a function), or a target object can be assigned to the task queue. In the latter case, each task in the queue (excluding special tasks) will be called on the target object: "%obj.sometask()". The tasks manager is an object and can be deleted, automatically canceling any pending tasks. Tasks can be programmed to recycle themselves (task executes and re-adds it self to end of queue between 0 and infinite times). Tasks can be programmed to preempt subsequent tasks (task executes and re-adds it self to front of queue a finite number of times). Tasks in the queue can be manually scheduled, or self-scheduled. When self-scheduling, tasks can provide their own schedule times, or use an overriding default time specified in the SimpleTaskMgr instance. There is no limit on the number of task manager that can be executing at once and all task managers are independent of all other managers.

This may seem a bit complicated, but be assured, the interfaces to the above features are simple and you don't need to use functionality you don't want. Furthermore, a thorough coverage of SimpleTaskMgr is provided below because it is used heavily in the EGTGE Kit.

367

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

SimpleTaskMgr Usage
Executing Functions
In its most basic incarnation, SimpleTaskMgr can be treated like a queue of tasks or function. In order to use it, we create a manger, add some tasks, execute the tasks, and remove the manager:
%testTaskMgr = newTaskManager(); %testTaskMgr.addTask( "echo(\"Hello\" );"); %testTaskMgr.addTask( "echo(\"World\" );"); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.delete();

This would produce the output:


Hello World

Executing Methods
In addition to executing a series of functions, we can tell the task manager to run a series of methods, by assigning a target object:
// A method to call on our target object function TestObj::dummyTaskFunc( %this, %val) { echo("TestObj::dummyTaskFunc("@%val@")"); } %myObj = new scriptGroup(TestObj); %testTaskMgr = newTaskManager(%myObj); %testTaskMgr.addTask( "dummyTaskFunc( 10 );" ); %testTaskMgr.addTask( "dummyTaskFunc( 20 );" ); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %myObj.delete(); %testTaskMgr.delete();

368

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

This would produce the output:


TestObj::dummyTaskFunc(10); TestObj::dummyTaskFunc(20);

Re-Targetting
Generally speaking, a SimpleTaskMgr instance should be used for either functions, or for methods, but not for both. However, an SimpleTaskMgr instance can be re-targeted at any time with the setTarget() method:
%testTaskMgr.setTarget( %someNewTarget );

Recycling
Because it would not be very useful to have to specify a repetitive list, or to re-specify a list that is supposed to run forever, SimpleTaskMgr supports the idea of recycling. A task can be added to the list with an additional argument specifying the number of times this task should 'recycled' before being removed from the list:
%testTaskMgr = newTaskManager(); %testTaskMgr.addTask( "echo(\"Echo...\" );", 2); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.delete();

Even though executeNextTask() was called three times, the output would only be:
Echo... Echo...

Why? The task was told to execute twice, or to recycle N-1 times, where N was 2. Once this was done, the task was deleted from the queue. Note: A recycle value of 0 or 1 both mean 'run once', because (logically) a task can only be cycled a minimum of once. Beyond finite repeats, a task can be made to recycle infinitely by doing this:
%testTaskMgr = newTaskManager(); %testTaskMgr.addTask( "echo(\"Echo A...\" );", -1); %testTaskMgr.addTask( "echo(\"Echo B...\" );"); %testTaskMgr.executeNextTask();

369

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

%testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.delete();

The new output would be:


Echo Echo Echo Echo A B A A ... ... ... ...

Preempting
In addition to repeating tasks, we can make some tasks preempt subsequent tasks:
%testTaskMgr = newTaskManager(); %testTaskMgr.addTask( "echo(\"Echo A...\" );", 2, true); %testTaskMgr.addTask( "echo(\"Echo B...\" );", 2, false); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.executeNextTask(); %testTaskMgr.delete();

This code would produce:


Echo Echo Echo Echo A A B B ... ... ... ...

What has happened is the first task was told to preempt subsequent tasks. To do this, the task manager executes the function and then places it at the front of the queue instead of the back.

Scheduled Preemption
The task manager allows us to add new tasks any time we wish. As we add tasks, we may wish to have them execute immediately instead of waiting their turn and working from back to front in the queue. Thus, this variation on the addTask() method is provided:

370

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

%testTaskMgr.addTaskFront( "echo(\"Echo A...\" );", 2, true);

Adding to the front of the task queue insures that this method will be executed next.

Return Values
SimpleTaskMgr can be used to drive decisions based on the values it returns when executing tasks. That is, if a task returns a value, the executeNextTask() method will return it:
function dummyTaskFunc(%val) { echo("dummyTaskFunc("@%val@")"); return %val; } %testTaskMgr = newTaskManager(); %testTaskMgr.addTask( "dummyTaskFunc(" @ 10 @ ");", 2 ); %testTaskMgr.addTask( "dummyTaskFunc(" @ 20 @ ");", -1 ); %total %total %total %total %total %total = 0; += %testTaskMgr.executeNextTask(); += %testTaskMgr.executeNextTask(); += %testTaskMgr.executeNextTask(); += %testTaskMgr.executeNextTask(); += %testTaskMgr.executeNextTask(); // // // // // +10 +20 +10 +20 +20

echo("Our total is ", %total); %testTaskMgr.delete;

This produces:
Our total is 80

371

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Late Evaluation
All tasks execute with the scope of the current task manager, thus we can pass variables to functions and methods that are scoped to the manager that is executing them. This gives us the ability to 'late evaluate' arguments. Used appropriately, this late execution feature can be used to create a very complex execution model. This sample of 'late evaluation' is taken directly from the SimpleTaskMgr validation code:
function TestObj::dummyTaskFunc( %this, %val) { echo("TestObj::dummyTaskFunc("@%val@")"); return %val; } // Test target execution and ability to change task manager values // and have them 'late evaluated' function validateTaskMgr2() { %myObj = new scriptGroup(TestObj); %testTaskMgr = newTaskManager(%myObj); %testTaskMgr.addTask( "dummyTaskFunc( %this.val0 );" , 2); %testTaskMgr.addTask( "dummyTaskFunc( %this.val1 );" , 0); %testTaskMgr.val0 = 10; %testTaskMgr.val1 = 20; %total = 0; %total += %testTaskMgr.executeNextTask(); // +10 %total += %testTaskMgr.executeNextTask(); // +20 %testTaskMgr.val0 = 30; %total += %testTaskMgr.executeNextTask(); // +30 %total += %testTaskMgr.executeNextTask(); // +0 %myObj.delete(); %testTaskMgr.delete();

Self-Execution
SimpleTaskMgr allows us to manually execute tasks and also provides the ability to schedule them. In other words, the task manager can be made to self-execute. When self-executing, the task manager will step over the queue till all tasks have been executed. Additionally, it can be told to execute tasks according to their own schedule or a fixed schedule:
%testTaskMgr = newTaskManager(); %testTaskMgr.setDefaultTaskDelay(2000); %testTaskMgr.addTask( "echo(\"Task 0\" );" , 0, false, 500); %testTaskMgr.addTask( "echo(\"Task 1\" );" , 0, false, 1000); %testTaskMgr.selfExecuteTasks( true ); // Use Default Default Task Delay

372

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

In the above example, although the tasks have provided their own schedule times, these times will be overridden and they will be executed two seconds apart. Understand that delays come first, then execution, thus the execution would go like this:
// two second delay Task 0 // two second delay Task 1

Alternately, we could tell the task manager to use the tasks' times instead:
%testTaskMgr = newTaskManager(); %testTaskMgr.setDefaultTaskDelay(2000); %testTaskMgr.addTask( "echo(\"Task 0\" );" , 0, false, 500); %testTaskMgr.addTask( "echo(\"Task 1\" );" , 0, false, 1000); %testTaskMgr.selfExecuteTasks( false ); // Ignore Default Task Delay

Now we get:
// half-second delay Task 0 // one second delay Task 1

In either case, if a delay value is set to -1, it means execute 'immediately':


%testTaskMgr = newTaskManager(); %testTaskMgr.addTask( "echo(\"Task 0\" );" , 0, false, 3000); %testTaskMgr.addTask( "echo(\"Task 1\" );" , 0, false, -1); %testTaskMgr.selfExecuteTasks( false );

Now we get:
// half-second delay Task 0 Task 1

373

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Stopping Self-Execution
In many cases we will need to stop the execution of our task manager and to re-start it at a later time. Therefore, a method is provided to stop the current execution:
%testTaskMgr.stopSelfExecution();

Stopping does not affect the contents of the task manager queue, nor does it stop the currently scheduled or executing task. The stop is applied after the next scheduled task. Self-execution can be started again later with another call to 'selfExecuteTasks()'.

Deletion and Safety


As previously noted, SimpleTaskMgr is self-contained. It has been designed to handle deletion and to appropriately clean up. Subsequent tasks are canceled and the task list is cleaned up. Also, if the task manager is used with a target object and the target object is deleted or becomes invalid, the task manager will handle it and not attempt to execute against this object. Cycling will continue, if it is self-executing, but no work will be done. It is best to create SimpleTaskMgr instances in objects' onAdd() methods and then to removed them in the objects' onRemove() methods.

Special Tokens
The task manager allows you to embed the following tokens in the task definitons:
Token TERMINATE# LASTRET# LOCK# STMT# TASKMGR# TASK# NULL# Purpose Causes the task manager to self-terminate when it reaches this task. Replaces token with return value from last task. Causes task manager to not store the return value for the current task, thereby retaining the last known return value. Treats the current task like a standalone statement or function call, regardless of whether this task manager has a 'target' object. Replaces the token with the numeric ID of this task manager instance. Replaces the token with the numeric ID of the current task. This is an empty task. It is preferable to use delays on real tasks, but sometimes this is useful.

374

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.6.2. EGTGE Tasks Management Reference


EGTask:: Methods
A script object representing an individual task. This has two support function, only one of which should be used.
execute() Execute this task. Normally, this is called by SimpleTaskMgr and should not normally be called directly by the user. %myTask.execute();

setTaskDelay( delay ) Set the automatic execution delay for this task.

Delay is in milliseconds.

%myTask.setTaskDelay( 500 ); // Delay this task for 500 ms

SimpleTaskMgr Functions
newTaskManager( [target] ) Creates a new task manager with an optional target, and returns ID. %testTaskMgr = newTaskManager();

SimpleTaskMgr:: Methods
addTask( task , [ recycleCount , preempt , taskDelay ] ) Creates a new task at back of tasks queue and return ID of task. task String specifying task to execute. Must be of a form acceptable to the eval() console function. recycleCount Optional cycles to execute this task. Task is executed and pushed to back of task queue. 0, 1 Execute once. N > 1 Execute N-1 times -1 - Execute forever. preempt Boolean value specifying that recycled transactions should be pushed to front of task queue. Cannot be used with infinite recycleCount. taskDelay Number of milliseconds to wait prior to executing task when selfExecuting. -1 means execute immediately. "doit();" "doit();" "doit();" "doit();" ) // Add task doit to back of queue 2 ) // doit() twice 2 , true) // doit() twice in a row -1, false, 1000 ) // doit() once per second forever

%testTaskMgr.addTask( %testTaskMgr.addTask( %testTaskMgr.addTask( %testTaskMgr.addTask(

375

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

addTaskFront( task, [ recycleCount, preempt , taskDelay ] ) Creates a new task at front of tasks queue and return ID of task. argument details.

See addTask() for

%testTaskMgr.addTaskFront( "doit();" ) // Add task doit to front of queue

clearTarget() Clear target for this manager. Once cleared, manager will assume tasks are not associated with an object and treat them like functions. %testTaskMgr.clearTarget();

clearTasks() Delete all tasks from queue without executing. %testTaskMgr.clearTasks()

executeNextTask() Executes next task in queue and returns value returned by task if any. %retVal = %testTaskMgr.executeNextTask();

getTarget( ) Returns ID of current manager target object. %curTarget = %testTaskMgr.getTarget();

selfExecuteTasks( [ useDefaultDelay ] ) Causes task manager to execute tasks automatically (with delays if specified ). If useDefaultDelay is true, all tasks will use the delay specified via 'setDefaultTaskDelay()' (see below), otherwise each task will use its own delay. %testTaskMgr.selfExecuteTasks(); // Use task delays %testTaskMgr.setDefaultTaskDelay( 1500 ); %testTaskMgr.selfExecuteTasks( true ); // Use default delay of 1.5 seconds

setDefaultTaskDelay( delay ) Sets default delay for tasks when executed with over-ride delay. i.e. useDefaulDelay is true when calling 'selfExecuteTasks()' method. %testTaskMgr.setDefaultTaskDelay( 1500 ); // Set default delay to 1.5 seconds %testTaskMgr.setDefaultTaskDelay( -1 ); // Set default delay to immediate mode

376

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

setTarget( target ) Set target to ID or Name supplied in target. %testTaskMgr stopSelfExecution( ) Stop automatic execution of tasks. %testTaskMgr.stopSelfExecution();

Does not stop current scheduled or in-action task.

A.6.3. Simple Inventory System (SimpleInventory)


The Simple Inventory system provides the ability to create an inventory container and place it in any ShapeBase class. This container then scopes all the methods required to store and retrieve non-unique instances of Items. It also provides methods for constraining the inventory. i.e. One can specify max values for specific inventory items. Lastly, it provides a set of methods that are scoped to the ShapeBase/Data and Item/Data classes to handle all basic inventory interactions. Described otherwise, SimpleInventory has the following attributes:

It is script based and will work with the standard TGE kit. It is implemented with ScriptObjects and can be placed in any object or stand-alone. In effect, this allows any object to have an inventory or inventories, further compartmenting and structuring game interactions. It is a generalized inventory system, designed to store non-unique items referenced by their datablock names. Items are stored and referenced by their datablock and can thus items with unique properties can be stored, but their unique-ness will be lost. An inventory can store any number of any type of datablock identified item. A max count limit can be set for any specific inventory item. All methods that operate on SimpleInventory are scoped under the SimpleInventory:: namespace. Inventory methods are provided for ShapeBaseData:: to enable a basic set of SimpleInventory interactions:

doPickup() Pick up one instance of an object. doThrow() Throw or drop one instance of an object from inventory. doUse() Use an object from inventory.

Inventory methods are provided for ItemData:: and Item:: classes to complete the inventory functionality.

377

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.6.4. SimpleInventory:: Structure


SimpleInventory Object
SimpleInventory (scriptObject) Owner knownItemTracking SimSet Count[KnownItem0]=10; Count[KnownItem1]=0; ... Count[KnownItemN]=N; [name] [OwnerID] KnownItem0 KnownItem1 KnownItemN

Inventoryable Items Datablock Template


datablock ItemData ( inventoryableItem ) { [InventoryItem = alternateInventoryItemToStore;] [InventoryValue = 10;] }; // ...

InventoryItem Optional alternate item to store in inventory. Stores this items intead of collision item. InventoryValue Optional inventory value for this item. By default, items are worth one inventory instance. This field can be used to increase that value. Values should only be positive.

A.6.5. Simple Inventory Console Functions


newSimpleInventory()

378

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

newSimpleInventory( [ %name ] ) Purpose: Create a new SimpleInventory instance. Arguments: %name Handle to this inventory.

Optionally give it the name %name.

%Obj.myInventory = newSimpleInventory( backPack );

A.6.6. SimpleInventory:: Console Methods


addObject() listContents() setOwner() dumpContentsToString() removeObject() verifyArgs() getInventoryCount() setInventoryCount() getInventoryMaxCount() setInventoryMaxCount()

addObject( %theInventory , %objectName [ , %numobjects ] ) Purpose: Add one %objectName object to the inventory. Returns number of objects successfully added. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object. %numObjects Number objects to add. dumpContentsToString( %theInventory ) Purpose: Returns semi-colon (;) separated list of current inventory contents in the form: ObjName0;Count0;...ObjNameN;CountN Arguments: %theInventory Handle to this inventory. getInventoryCount( %theInventory , %objectName) Purpose: Get total number of %objectName objects in the inventory. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object. getInventoryMaxCount( %theInventory , %objectName) Purpose: Get max number of %objectName objects allowed in the inventory. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object.

Returns 0 if none found.

379

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

listContents( %theInventory ) Purpose: Prints a list of the inventory's contents to the console. Arguments: %theInventory Handle to this inventory. removeObject( %theInventory , %objectName [ , %numobjects ] ) Purpose: Remove one %objectName from the inventory. Returns number of objects successfully removed. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object. %numObjects Number objects to remove. setInventoryCount( %theInventory , %objectName , %numObjects ) Purpose: Set total number of %objectName objects in the inventory. Returns number of objects succesfully set. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object. %numObjects Number objects to set. setInventoryMaxCount( %theInventory , %objectName , %maxCount ) Purpose: Sets maximum number of %objectName objects allowed in the inventory. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object. %maxCount Max objects of this type allowed. (Can be "", 0, or N > 0) setOwner( %theInventory , %ownerObj ) Purpose: Assigns and 'owner' to this inventory. Arguments: %theInventory Handle to this inventory. %ownerObj Object that contains/owns this inventory. verifyArgs( %theInventory , %objectName ) Purpose: Verifies that %objectName is in fact an object and returns the stringized name of said object. Arguments: %theInventory Handle to this inventory. %objectName Datablock ID for this object.

380

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.6.7. ShapeBaseData:: Inventory Methods


doPickup() doThrow() throwObject() doUse()

doPickup( %ownerDB , %ownerObj , %pickupObj ) Purpose: Do a pickup of %pickupObj. Returns: 0 Fail N > 0 Success.

N == number of objects added (always 1 for SimpleInventory).

Arguments: %ownerDB Datablock ID or string for this owner. %ownerObj Handle to this owner. %pickupObj Object to pick up. Notes: Assumes inventory field is named: myInventory doThrow( %ownerDB , %ownerObj , %throwDB ) Purpose: Do a throw of %throwDB. If the throw is successful, call %throwObj (returned by onThrow().schedulePop() to remove the object in $Item::PopTime milliseconds. Returns: 0 Failed throw. 1 Succesful throw. Arguments: %ownerDB Datablock ID or string for this owner. %ownerObj Handle to this owner. %throwDB Datablock ID or string for throw object. Notes: Assumes inventory field is named: myInventory throwObject( %ownerDB , %ownerObj , %throwObj ) Purpose: Execute the actual throwing of the object. This function simply applies a mass specific impulse to the throw item along upward-diagonal vector calculated from the owners eye vector. Arguments: %ownerDB Datablock ID or string for this owner. %ownerObj Handle to this owner. %throwObj Object to throw.

381

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

doUse( %ownerDB , %ownerObj , %useDB ) Purpose: Attempt to use execute the onUse() method fo %useDB. Returns: 0 Fail N > 0 Success.

N == number of objects added (always 1 for SimpleInventory).

Arguments: %ownerDB Datablock ID or string for this owner. %ownerObj Handle to this owner. %useDB Object to use. Notes: Assumes inventory field is named: myInventory This function may seem to fulfill no real purpose, but it actually an ideal place to put additional use functionality for different ShapeBase derived class types.

A.6.8. ItemData:: Inventory Methods


onInventory() onPickup() onThrow() onUse()

onInventory( %inventoryObj , %ownerObj, %amount ) Purpose: This method is called for all items when the value (count) of this item is changed (either added or removed) in inventory. Returns: 0 Fail N > 0 Success.

N == number of objects added (always 1 for SimpleInventory).

Arguments: %inventoryObj Datablock ID or string for this object. %ownerObj Object that owns inventory. %amount Non-zero change value (either positive for add, or negative for remove) Notes: Assumes inventory field is named: myInventory

382

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

onPickup( %pickupDB , %pickupObj, %ownerObj ) Purpose: Attempt to place %pickupDB in %ownerObj inventory. If the add is successful, call %pickupObj.respawn() to temporarily hide the object. The object will reappear in $Item::RespawnTime milliseconds Returns: 0 Fail N > 0 Success.

N == number of objects added (always 1 for SimpleInventory).

Arguments: %pickupDB Datablock ID or string for this object. %pickupObj Handle to this item object. %ownerObj Agent calling onPickup(). Notes: Assumes inventory field is named: myInventory onThrow( %throwDB , %ownerObj ) Purpose: Attempt to remove %throwDB from %ownerObj inventory. If successful, create a new instance of type % throwDB and return the handle %throwObj. Returns: 0 Item not found in inventory, or failed to create instance. %throwObj New object handle. Arguments: %throwDB Datablock ID or string for this object. %ownerObj Agent calling onThrow(). Notes: Assumes inventory field is named: myInventory onUse( %useDB , %ownerObj ) Purpose: Attempt to remove %useDB from %owneObj inventory. object. Returns: false Item not found in inventory. true Successfully used object. Arguments: %ItemDB Datablock ID for this object. %Owner Agent calling onUse().

If successful, do something with the

383

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

A.6.9. Item:: Inventory Globals


$Item::PopTime Time afer pickup till items pop (auto-delete). $Item::RespawnTime Time afer pickup till items respawn (un-hide). Default == 5000 milliseconds.

Default == 5000 milliseconds.

A.6.10. Item:: Inventory Helper Methods


respawn() schedulePop()

respawn( %Item ) Purpose: Fades out and hides %Item. Then schedules an un-hide and fade-in to occur in $Item::RespawnTime milliseconds. Arguments: %Item Handle to object to operate on. schedulePop( %Item ) Purpose: Schedules a fade-out and delete() on %Item in $Item::PopTime millisconds. Arguments: %Item Handle to object to operate on.

A.6.11. GPGT Utilities


This section documents various utility scripts that have been included with the book for your use. These scripts were created to ease some of my coding tasks and I thought they would also be useful to you, the reader. Enjoy.

A.6.11.1. String Utilities


randomizeWords( %words , %iterations ) Purpose: Randomizes the ordering of all words found in the %words string, returning a new string containing the result. Randomization can be improved by increasing the %iterations setting. Arguments: %words A string containing word to randomize. %iterations The number of passes to make while randomizing the list.

384

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

%wordList = "This is a test"; %randWordList = randomizeWords( %wordList , 10 ); swapWords( %words, %first, %second ) Purpose: Swaps two words, found in %words and located at indexes %first and %second. Returns a string containing the contents of %words, with the two strings re-ordered. If either index is out of bounds, the original string is returned. Arguments: %words A string containing words, two of which will be swapped. %first An integer value indicating the index of the first word. %second - An integer value indicating the index of the second word. %wordList = "This is a test"; %swapWordList = swapWords( %wordList , 0, 2 ); echo( %swapWordList ); // Produces a is This test

A.6.11.2. SimSet Utilities


These utilities are used to manipulated SimSet, SimGroup, and ScriptGroup objects.
simSet::copyToSet( %theSet, %destSet ) Purpose: Copies the contents of %theSet to %destSet.

There is no return value.

This can also be used to move the contents of one SimGroup or ScriptGroup to another. Arguments: %theSet SimSet to copy contents from. %destSet SimSet to copy contents to. new SimGroup(source); new SimGroup(dest); source.add( new SimObject(A) ); source.add( new SimObject(B) ); source.copyToSet( dest ); source.add( new SimObject(C) ); source.listObjects(); // Lists only dest.listObjects(); // Lists A and B C

385

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

simSet::deleteSet( %theSet, %selfDestruct) Purpose: Iteratively deletes the contents of the set %theSet. to true, the object will then self-delete.

Optionally, if %selfDestruct is set

This is not the same as clear(), but rather it actually deletes every object stored in the simSet. Arguments: %theSet The SimSet from which to remove and delete all contents. %selfDestruct A boolean value, when set to true, indicating that the SimSet should be deleted after the contents have been removed. %myTask.execute(); simSet::forEach( %theSet, %function, [ %isMethod , [ %printEval ] ]) Purpose: This method allows us to iteratively call a function or a method on every entry in the SimSet. Arguments: %theSet The SimSet whose contents will be called against. %function The unadorned name of the function or method. %isMethod A boolean value, when true, indicating %function is a method, otherwise %function is treated as a function. %printEval A debug feature that will print the strings being fed to eval(). If you are having trouble with this method, set this to true to see what scripts are actually being constructed and executed.

function test_func( %obj ) { echo(test_func got %obj == , %obj.getname() ); }; function SimSet::test_method( %obj ) { echo(test_method got %obj == , %obj.getname() ); }; new SimSet( testSet ); testSet.add( new SimObject( A ) ); testSet.add( new SimObject( B ) ); testSet.add( new SimObject( C ) ); // There are three ways to call the above function and method // // 1 testSet.forEach( test_func ); // call test_func as method

386

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

// 2 testSet.forEach( test_method , 1 ); // call test_method as method // 3 testSet.forEach( SimSet::test_method ); // treat test_method like function

simSet::forEachStmt( %theSet, %statement, %token, %printEval) Purpose: This method allows us to iteratively call a statement using each object in the SimSet. This will call a series of statements, each operating on one of the objects in the set at a time. Arguments: %theSet The SimSet whose contents will be called against. %statement Any legal TorqueScript statement (with closing semi-colon (;)). %token A token name embedded in %statement which will be replaced with the ID of the object that is currently being operated on. The token can be used multiple times in the same statement. %printEval A debug feature that will print the strings being fed to eval(). If you are having trouble with this method, set this to true to see what scripts are actually being constructed and executed. $x = new SimSet(); $x.add( new SimObject( A ) ); $x.add( new SimObject( B ) ); $x.add( new SimObject( C ) ); $x.forEachStmt("echo( tok.getName(), \" has ID == \", tok.getID() );" , tok ); // // // // Above A has B has C has code call would print something like: ID == 123 ID == 124 ID == 125

387

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

simSet::getRandomObject( %theSet ) Purpose: Returns the ID of an object from %theSet, selected at random. Also, for any set with two or more objects stored in it, this method is guaranteed to never return the same value twice in a row. Warning: This method adds a field to all sets that call it named: _lastRandomObject. This field is used to track the last object returned and allows the method to protect against repeats. Arguments: %theSet The SimSet to get our random selection from. $x = new SimSet(); $x.add( new SimObject( A ) ); $x.add( new SimObject( B ) ); $x.add( new SimObject( C ) ); echo( echo( echo( echo( // // // // // $x.getRandomObject $x.getRandomObject $x.getRandomObject $x.getRandomObject ); ); ); );

Above echo calls might print something like: B A C A

A.6.11.3. Array Object


The purpose of these utilities is to allow the creation of array objects that allow easy sorting and passing of arrays. Normal Torque arrays cannot be passed as arguments to functions or methods, and sorting them requires that you write code. The array object utilities now take care of this for you. With the provided utility methods, you may create a scriptObject (or scriptGroup) using any of the following syntaxes:
%aryObj = new ScriptObject( arrayObject ); // OR %aryObj = new ScriptObject( { class = "arrayObject"; }; // OR %aryObj = new ScriptObject( ) { superClass = "arrayObject"; }; )

388

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Subsequently, you may use this object as an array as follows:


// Add four entries to the array object %aryObj.addEntry( %aryObj.addEntry( %aryObj.addEntry( %aryObj.addEntry( "This" ); "Is" ); "a" ); "test" );

// Get the count of objects in this array echo( "My array object has ", %aryObj.getCount() , " entries."); // Sort the array (increasing sort only) %aryObj.sort(); // Pass the array to a function for printing // Just demonstrating ability to pass the array function dumpArray( %aryObject ) { for( %count = 0; %count < %aryObject.getCount(); %count ++ ) { echo("Entry(", %count , ") == ", %arrayObject.getEntry( %count ) ); } } dumpArray( %aryObj );

The result of the above dumpArray() call will be:


Entry(0) Entry(1) Entry(2) Entry(3) == == == == a Is test This

arrayObject::addEntry( %Obj , %entry ) Purpose: Adds a new entry into the array. Arguments: %Obj The array Object. %entry New value to add to array. arrayObject::getCount( %Obj ) Purpose: Returns the number of entries in the array. Arguments: %Obj The array Object.

389

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

arrayObject::getEntry( %Obj , %index ) Purpose: Returns the entry in this array at index %index, or if no entry is found. Arguments: %Obj The array Object. %index Index to entry to retrieve. arrayObject::sort( %Obj [ , %Decreasing ] ) Purpose: Sorts the contents of the array in increasing order if %Decreasing is set to false, or not specified. If %Decreasing is set to true, the array is sorted in decreasing order. Warning: This sort uses a lexicographic comparison, meaning the entries <5 5000 a b c> would be sorted in these orders: Increasing Order -> < 5 5000 a b c > Decreasing Order -> < c b a 5000 5 > Arguments: %Obj The array Object. %Decreasing Optional boolean value. Setting this to true sorts in decreasing ordering.

A.6.11.1. Miscellaneous Utilities


CalculateObjectDropPosition( %oldPosition , %offsetVector ) Purpose: This function will calculate a drop point without moving the object and returns the calculated drop position. The function casts a ray from %oldPosition downward and calculates a drop position based on the first object that is hit. This ray cast will hit ALL game objects that have a collision mesh. Final positions will be: initially calculated drop + %offsetVector Arguments: %object - The object to be dropped. %offsetVector - An offset to adjust the drop by.

390

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

DropObject( %object , %offsetVector ) Purpose: This function will drop an object to the ground, or on top of the first object found with a valid collision box below the dropping object. The dropping object starts its 'drop' at the position it was created at and drop from there. Note 1: To calculate a starting position, use CalculateObjectDropPosition(). Note 2: This function will not move TSStatic() objects. Their positions must be set once and only once, upon construction. Use CalculateObjectDropPosition() instead. Arguments: %object - The object to be dropped. %offsetVector - An offset to adjust the drop by. DropObjectFromMarker( %object , %marker , %offsetVector ) Purpose: This is the same as DropObject, but it drops the object from the position of another world object whose handle is passed in %marker. Arguments: %object - The object to be dropped. %marker - A valid marker (object) to be used as a starting point. %offsetVector - An offset to adjust the drop by. getLeftVector( %vec ) Purpose: Returns the pre-normalized right-hand cross-product of %vec and the world up-vector. i.e., This operation is performed: ( | vec | X 0 0 1 ) Arguments: %vec The vector to find a left vector for. echo( getLeftVector(" 100 50 75 " ) ); // prints: < 0.447 -0.894 0 >

getRightVector( %vec ) Purpose: Returns the pre-normalized left-hand cross-product of %vec and the world up-vector. i.e., This operation is performed: -( | vec | X 0 0 1 ) Arguments: %vec The vector to find a left vector for. echo( getRightVector(" 100 50 75 " ) ); // prints: < -0.447 0.894 0 >

391

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GuiMLTextCtrl::fillFromFile( %theControl , %textFile [ , %clear ] ) Purpose: This handy helper adds the contents of any file to any GUIMLTextCtrl object. optionally clear the current contents or append to them. Arguments: %theControl Handle to the GUIMLTextCtrl. %textFile Path to the file to be loaded. %clear Optional boolean value. If set to true, the control is cleared before loading occurs. // Place the contents of soHandy.txt into myGUIMLTextCtrl, and clear it first myGUIMLTextCtrl.fillFromFile( ~/soHandy.txt, true );

You may

sceneObject::getCompassPoints( %Obj, %Offset) Purpose: Returns a SimSet containing scriptObjects, each having a field named 'position' containing the location of a compass point about the shape. Compass points are in a circle about the shape, where the circle lies on the outer edges of the shape's world box. The circle's radius can be increased by specifying an %Offset, where %Offset will be added to the initial radius. Arguments: %Obj The sceneObject to find eight compass points for. %Offset A floating-point value by which to increase the radius of the base circle on which the compass points lie. // Place eight spawn spheres around an object. function scriptObject::placeMarker( %Obj ) { %obj = new SpawnSphere() { dataBlock = %data; position = %Obj.position; }; } MissionCleanup.add( %obj );

%compassSet = myObject.getCompassPoints(); %compassSet.forEach( placeMarker ); %compassSet.deleteSet( true ); // Destroy the set and all markers in it.

392

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

SceneObject::getLeftVector( %Obj ) Purpose: This method passes the forward vector of %Obj to getLeftVector and returns the result. Arguments: %Obj The object to get the left vector for. echo( getObjLeftVector( %player ) ); SceneObject::getRightVector( %Obj ) Purpose: This method passes the forward vector of %Obj to getRightVector and returns the result. Arguments: %Obj The object to get the left vector for. echo( getObjRightVector( %player ) );

393

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Appendix B. GPGT Lesson Kit Docs

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Table of Contents
B.1 GPGT Lesson Kit User's Guide.................................................................................... 4 B.1.1. About The GPGT Lesson Kit........................................................................................ 4 B.1.2. Running The GUI Sampler.......................................................................................... 6 B.1.3. Running The Interface Sampler................................................................................... 7 B.1.4. Running The 3D Lessons (SinglePlayer)....................................................................... 8 B.1.5. Running The Sample Script Console........................................................................... 12 B.1.6. Adding New GUI Samples......................................................................................... 14 B.1.7. Adding New Interface Samples.................................................................................. 15 B.1.8. Adding New 3D Lessons........................................................................................... 16 B.1.9. Adding New Sample Scripts...................................................................................... 27 B.2 Instructing and Collaborating.................................................................................. 28 B.2.1. Running The 3D Lessons Server (MultiPlayer)............................................................. 28 B.2.2. GPGT Lesson Client (LAN)......................................................................................... 29 B.2.3. GPGT Lesson Client (Remote Networks)..................................................................... 30 B.2.4. Special Considerations For Lesson Hosting................................................................. 31 B.2.5. Running The Included Master Server......................................................................... 32 B.3 Recording Lesson Kit Sessions................................................................................. 34 B.3.1. Recording A Lesson Kit Session................................................................................. 34 B.3.2. Replaying A Recorded Lesson Kit Session................................................................... 34 B.3.3. Some Prerecorded Lesson Kit Sessions....................................................................... 34 B.4 GPGT 3D Lesson Descriptions.................................................................................. 35 B.4.0. Lesson Template...................................................................................................... 35 B.4.1. SceneObject............................................................................................................ 35 B.4.2. ShapeBase (General)................................................................................................ 36 B.4.3. Item....................................................................................................................... 38 B.4.4. TSStatic.................................................................................................................. 40 B.4.5. ShapeBaseImageData (Images)................................................................................ 40 B.4.5. InteriorInstance....................................................................................................... 40 B.4.7. Mission Objects........................................................................................................ 41 B.4.8. Special Effects......................................................................................................... 43 B.4.9. Game Elements....................................................................................................... 45

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1 GPGT Lesson Kit User's Guide


B.1.1. About The GPGT Lesson Kit

he Game Programmer's Guide to Torque (GPGT) was created with a variety of audiences in mind, including Lone-Wolf Developers, Teams, Instructors, and Students. The members of this broad audience have some unique and several shared requirements.

Lone-Wolf Developers This part of the audience faces the most challenges. The lonewolf has to make all of his own content, purchase it from others, or contract help to build it. To help the individual who makes her own content, the kit includes samples of the majority of the engine's scripting feature, 2D classes (GUI controls), and 3D classes. With samples in hand, the lone-wolf developer has a leg up on learning and understanding the Torque Game Engine. See "Lesson Kit Features" below for the list of samplers included with this kit. Team Developers This part of the audience faces many of the same challenges as the lone-wolf with the added challenge of collaboration. The kit is designed to facilitate collaborative work using scripts, 2D classes, and 3D classes. If each member of the team has a copy of the kit, it is simple to create sample works and pass them to other team members. They can then drop the work into their own kit (anywhere) and examine it. The kit automatically loads new script samples, 2D samples, and 3D samples which have been created using the rules outlined in "Adding New Lessons and Samples" below. You can think of the kit as a collaboration harness. Instructors and Students Instructors and students require all of the features that the lone-wolf and team developers require. Additionally, they may require ways to record and play-back lessons, or to display samples to a distributed audience. The kit has these capabilities too. See Section B.2 Instructing and Collaborating below for more details.

This kit is designed to facilitate learning and collaboration through the hands on application and examination of all Torque Game Engine features. To do that, the kit includes these major tools:

GUI Sampler This tool provides a harness for examining individual GUI controls in use. The sampler comes with 22 samples demonstrating the features and usage of the most common GUI controls. Additionally, a blank template is supplied that can be used to drop new samples in the kit. See Section B.1.6 to learn how to add new GUI samples. Figure B.1.1 shows the GUIMouseEventCtrl sample which graphically demonstrates all of the features of this mouse-input capturing control.

Figure B.1.1. GuiMouseEventCtrl

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Interface Sampler This tool is used to demonstrate entire interfaces in action. Creating an interface is a bit more complicated than using the individual GUIs. Also, not having a specific native control for your particular application should not be a barrier to completion. It is possible to use multiple controls and scripts together to create new HUDs and interface elements. Figure B.1.2 shows the tech version of the main menu interface supplied with this kit. See Section B.1.7 to learn how to add new interface samples.

Figure B.1.2. Main Menu (Tech)

3D Lessons Mission This tool is used to demonstrate the usage of individual 3D classes and other engine features, used alone or together. A special mission has been created with a set of utility scripts and a harness to allow individuals and groups to share and examine any 3D content. Figure B.1.3 shows the 3D lessons selector displaying a summary of the Transforms lesson. In addition to the many included lessons, it is possible to add new lessons using the supplied template. See B.1.8 to learn how to add new 3D lessons. Figure B.1.3. 3D Lesson Selector Sample Script Console This tool is used to show sample scripts in a classroom setting. Samples may be typed in directly, or loaded from a file. Figure B.1.4 shows this tool in use. See Section B.1.5 to learn more about using this tool.

Figure B.1.4. Sample Script Console

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.2. Running The GUI Sampler


To run the existing GUI Samples, start the kit and follow these steps:

Start Sampler Tool


Click on the GUIs Sampler button. Figure B.1.5. GUIs Sampler

Navigate to Sample
Navigate to the sampler page containing the sample you wish to view using the navigation buttons. Note: The back button is also tied to the ESCAPE key and the forward button is tied to the SPACE key.

Back

Forward

No Pages

Figure B.1.6. Sampler navigation buttons

Figure B.1.7. guiBitmapCtrl

Run Sample
Click on the button containing the name of the GUI control you wish to see a sample of.

Figure B.1.8. guiBitmapCtrl Sample

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.3. Running The Interface Sampler


To run the existing Interface Samples, start the kit and follow these steps:

Start Sampler Tool


Click on the Interface Sampler button. Figure B.1.9. Interface Sampler

Navigate to Sample
Navigate to the sampler page containing the sample you wish to view using the navigation buttons. Note: The back button is also tied to the ESCAPE key and the forward button is tied to the SPACE key.

Back

Forward

No Pages

Figure B.1.10. Sampler navigation buttons

Run Sample
Click on the button containing the name of the GUI control you wish to see a sample of. Figure B.1.11. Splash Screen

Figure B.1.12. Toon Splash Sample

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.4. Running The 3D Lessons (SinglePlayer)


To run the existing 3D Lessons, start the kit and follow these steps:

Load 3D Lessons Mission


Click on the Start Mission... button. Figure B.1.13. Start Mission...

Launch 3D Lessons Mission


Select 3D Lessons from the missions list and click the Launch Mission! button as in Figure B.1.14. TGE will load the mission, load datablocks, do some lighting calculations and eventually pop you into the mission. See Figure B.1.15. When you are dropped into the mission, you will find yourself in an empty grassy cauldron (Figure B.1.16). Figure B.1.14. Missions Dialog

Figure B.1.15. Loading the mission...

Figure B.1.16. In the 3D Lessons mission!

Open Lesson Selector Dialog


PC Users: Click on the right-mouse button to start the lesson-selector dialog (Figure B.1.17). Macintosh Users: Click EFM + the mouse to start the lessons-selector dialog (Figure B.1.17). Figure B.1.17. Lesson Selector Dialog

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Navigate To Lesson
You will find that this dialog has several parts related to navigating and selecting a lesson for loading:

Lesson Tiles (Figure B.1.18. A) These pictures and/or text tiles show the currently selected lesson. Lesson Description Pane (Figure B.1.18. B) This pane displays the title of and a summary of the currently selected lesson. Lesson Navigation Buttons (Figure B.1.19.) These buttons are used to scroll up and down through the current lesson group(s). They navigate as follows: A Up 3 lessons; B Up 1 lesson; C Down 3 lessons; D Down 1 lesson Lesson Group Buttons (Figure B.1.20.) These buttons select which lessons are included in the lesson tiles scroll group. They select the following lesson groups: A Volume 1 lessons; B Volume 2 lessons; C Community created lessons; D Q & A solution lessons.

Figure B.1.18. Lesson Tile and Description Pane

Figure B.1.19. Lesson navigation buttons

A C

Figure B.1.20. Lesson group buttons

B D

Use these buttons to find and lesson to run, then move on to the next step.

Run Lesson

Figure B.1.21. Lesson selection buttons

A B

Running a lesson is as simple as finding the one you want (last step) and then clicking the Select button (Figure B.1.21. A). Alternately, you can just double-click the lesson tile for the lesson you want to run. If you decide not to run a lesson after all, simply click the Cancel button (Figure B.1.21.B).

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Individual Lessons
Some, but not all lessons will first pop up a dialog box. Be sure to read the contents of this box as it will usually supply additional instructions regarding how the lesson works and how to use it. Additionally, almost all lessons have these common parts, including:

Lesson Compass (Figure B.1.22.) The lesson compass will tell you in which direction your avatar is facing at any time during the lesson. Metrics Dialog (Figure B.1.23.) This dialog will present the following information (based on what your player is looking at):

Figure B.1.22. Lesson compass

FPS Client ID Numeric ID of your client. Player ID Numeric ID of player. DB Datablock used by player. Cur Energy Energy points of player. Max Energy Maximum energy points of player. Damage Percentage of maximum damage this player has taken. Camera

ID Numeric ID of camera. DB Datablock used by camera.

Figure B.1.23. Metrics Dialog

10

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Individual Lessons (cont'd)


Current Look-at (Object under reticle) Obj Numeric server-side ID of current look-at object. Name Alphanumeric name of current look-at object. Class Class name of current look-at object DB Name Datablock used by current look-at object. Cur Energy Energy points of current look-at object. Max Energy Maximum energy points of current look-at object. Damage Percentage of maximum damage current look-at object has taken.

Compass Signs and Position Markers (Figure B.1.24.) Lastly, most lessons will have compass point signs North, South, East, and West, as well as position markers along the major axes, marking the 10 meter through 70 meter positions. These are all used to help you correlate lesson parts to that lessons documentation.

Figure B.1.24. Compass Signs and Markers

Additional instructions are provided later in this appendix for running the lesson kit in multiplayer mode. This mode is provided to allow an instructor/lecturer or collaborators to run 3D lessons such that one individual drives the 3D lesson while others participate as observers. Please see Section B.2 Instructing and Collaborating below.

11

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.5. Running The Sample Script Console


To run the existing GUI Samples, start the kit and follow these steps:

Start Sample Script Console


Click on the Sample Script Console button. Figure B.1.25. Sample Script Console

Manually Type Scripts


At this point, you may manually type scripts into the left pane and then evaluate them by pressing the Eval button (Figure B.1.27). The result of the execution will be shown in the right pane.

Figure B.1.27. Eval button Figure B.1.26. Ready to script

Run Scripts From File


Alternatively, if you are running the kit in a classroom or lecture setting you may want to prepare scripts in advance and then run them for your class. Simply click on the Load button (Figure B.1.28) and then using the file selection dialog to select a script for running (Figure B.1.29).

Figure B.1.28. Load button

Figure B.1.29. File selection dialog

12

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Sample Script Console Dashboard


The Sample Script Console tool provides a dashboard with several buttons and check boxes.

Figure B.1.30. The Sample Script Console Dashboard


auto-clear check box If this is checked, the output pane will be automatically cleared on each press of Execute or Eval. auto-execute check box If checked, every time a file is loaded it will automatically be executed. quieter check box If checked, this will turn off most warnings and messages that appear in the output pane, showing only warnings and output from your script/sample instead. Execute Button Pressing this button saves the contents of the input pane to a temporary file and then calls exec() on it. The output of this action is directed to the output pane.

Warning: This is designed for multi-line entries and entries with comments. This is the preferred method of executing sample scripts.

Eval Button Pressing this button passes the contents of the input pane to the eval() statement. The output of this action is directed to the output pane.

Warning: This is designed for single line entries or entries with no comments. Pressing this button when the input pane contains comments may not behave as expected.

Echo Input Pressing this button will echo the contents of the input pane in the output pane. Clear Input Pressing this button will clear the input pane. Load Button Pressing this button will start the load file dialog allowing you to load a previously written script. Save Button Pressing this button will start the load save dialog allowing you to save the contents of the input pane to a file. Please note, that scripts saves in this manner will not be easily editable in an external editor. << Prev Button Pressing this button loads the previous file found in the last directory files were loaded from using the Load button. >> Next Button Pressing this button loads the next file found in the last directory files were loaded from using the Load button. cls() Button Pressing this button clears the output pane. Close button Pressing this button closes the Sample Script Console tool.

13

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.6. Adding New GUI Samples


In the future, you may wish to create a GUI Sample using a new GUI control. Assuming your new GUI control is named guiMyControl, you can create a sample for it by following these steps: 1. In the directory ~\gpgt\GUISamples you will find a directory named Template. Copy this directory and then rename the copy as gs_MyControl. 2. Open your new directory ~\gpgt\GUISamples\gs_MyControl. In this directory you will find two files, Template.cs and Template.gui. Rename these two files gsMyControl.cs and gsMyControl.gui respectively. 3. Open the newly renamed file gsMyControl.cs and globally replace the word Template with MyControl. Save and close the file. 4. Open the newly renamed file gsMyControl.gui and globally replace the word Template with MyControl. Save and close the file.

At this point, if you have correctly followed the above steps, you will be able to start the lesson kit and find your new GUI Sample button (Figure B.1.31) by running the GUI Sampler tool. Figure B.1.31. MyControl GUI Button

Clicking on this new button will start your sample, which is currently blank (Figure B.1.32). Now, you may edit this GUI to your hearts delight and simply save the edits in the .gui file we just created. Please note, the .cs file we just created is for loading the .gui file and for any scripts or profiles that you might need for your sample. Please see the provided samples for reference. Figure B.1.32. MyControl Sample (starts blank)

14

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.7. Adding New Interface Samples


In the future, you may wish to create a Interface Sample. Assuming your new intefaace is named named myInterface, you can create a sample for it by following these steps: 1. In the directory ~\gpgt\InterfaceSamples you will find a directory named Template. Copy this directory and then rename the copy as ifcs_myInterface. 2. Open your new directory ~\gpgt\InterfaceSamples\ifcs_myInterface. In this directory you will find two files, Template.cs and Template.gui. Rename these two files ifcsmyInterface.cs and ifcsmyInterface.gui respectively. 3. Open the newly renamed file ifcsmyInterface.cs and globally replace the word Template with myInterface. Save and close the file. 4. Open the newly renamed file ifcsmyInterface.gui and globally replace the word Template with myInterface. Save and close the file. At this point, if you have correctly followed the above steps, you will be able to start the lesson kit and find your new Interface Sample button (Figure B.1.33) by running the Interface Sampler tool.

Figure B.1.33. myInterface Interface Button

Clicking on this new button will start your sample, which is currently blank (Figure B.1.34). Now, you may edit this interface to your hearts delight and simply save the edits in the .gui file we just created. Please note, the .cs file we just created is for loading the .gui file and for any scripts or profiles that you might need for your sample. Please see the provided samples for reference. Figure B.1.34. myInterface Sample (starts blank)

15

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.8. Adding New 3D Lessons


In the future, you may wish to create your own 3D lessons either to demo an idea, as a teaching tool, for for team collaboration and debug work. As with the GUI Sampler and the Interface Sample, a template is provided to simply this task. Because this is the most complicated kind of lesson/sampler to add, lets take some time to examine the components of a 3D lesson before walking through the steps to build one.

B.1.8.1. 3D Lesson Components


New 3D lessons are created by providing a set of required and optional files (Table 1.). These files follow a specific format. Within these files, we define the contents of our lesson and how it will interact with the lesson environment. The sample lesson files can be found in directory: "~\gpgt\3DLessons\Volume1\000_SimpleSampleLesson". Table 1. 3D Lesson Template Files Filename lesson.map.txt (required) Purpose This is the lesson map. This file tells the lesson kit what other files are present in this directory and defines their purpose.

SimpleSampleLeson.cs (required)

This is the lesson file. In this file, we define all of the loading and unloading methods of a file.

sample.png sample_sel.png (optional)

The deselected and selected lesson tiles for this lesson. Each measures 210 x 210 pixels.

deselected

selected

The ML formated lesson summary file that will be displayed in SimpleSampleLesson.ml.txt the description pane of the lesson selector when the tile for this (required) lesson is selected.

clientScripts.cs (optional)

This client script file contains any script(s) that should be run on the client, when the lesson is loaded. This can include such things as actionMap definitions, GUI definitions and scripts, etc.

16

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

The Lesson Map File


It is the job of the lesson map (lesson.map.txt) to tell the 3D lesson manager what files are present and should be loaded for the current lesson. The general layout of a lesson map is shown in Table 2. Table 2. Lesson Map File Layout Line 0 Name Lesson Filename (required) Lesson Name (required) Description The filename given to the lesson file.

A unique one-line description of the lesson 20 characters or fewer in length.

Lesson-Object Name (required)

This is the name of the scriptObject that defines the lesson.

Lesson Summary Filename This contains the name of the lesson summary ML file. (required)

Lesson Tile Filename Root (line may be blank)

This is the root for the lesson tile filename. For example for the sample tiles, this string would be 'sample'. If this line is blank, the 'Lesson Name' will be printed on the Lesson Selector Button.

Client Script Filename (line may be blank)

This is the filename for the client script file. If present, this file is executed on every client loading this less.

In our sample lesson (template) we can find a lesson map: "~\gpgt\3DLessons\Volume1\000_SimpleSampleLesson\lesson.map.txt". This file contains the following text:

SimpleSampleLesson.cs Simple Sample Lesson SimpleSampleLesson SimpleSampleLesson.ml.txt sample clientScripts.cs


17

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

The Lesson File


It is the job of the lesson file to define any datablock, functions, and methods used by the lesson. Additionally, it must define set of console methods in the namespace of the lesson scriptObject (as it was named in the lesson map). The order of this file is optional, but is usually organized as follows: Load/Define Datablocks Load, using exec(), and/or define any datablocks used in the lesson.

Load/Define Functions and Methods Load, using exec(), and/or define any functions and methods required for the lesson, excluding those discussed next. Define Required Lesson Methods Define the three required methods, in the namespace of this lesson's, lesson scriptObject. LessonName::onAdd() - It is the job of this method to do any lesson initialization, prior to execution. LessonName::onRemove() - It is the job of this method to do any special lesson cleanup, not already handled by the lesson manager. LessonName::ExecuteLesson() - It is the job of this method to build and run the lesson.

Here is an (edited) example of the lesson file: "~\gpgt\3DLessons\Volume1\000_SimpleSampleLesson\SimpleSampleLesson.cs".

//-----------------------------------------------------// ****** LOAD/DEFINE DATABLOCKS FOR LESSON //-----------------------------------------------------//None. //-----------------------------------------------------// ****** LOAD/DEFINE FUNCTIONS and METHODS FOR LESSON //-----------------------------------------------------function dummyItem::onPickup( ... ) { // ... } function dummyItem::onCollision( ... ) { // ... } function serverCmdbumpBlock( ... ) { // ... }

18

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

//-----------------------------------------------------// ****** DEFINE THE *REQUIRED* LESSON METHODS //-----------------------------------------------------function SimpleSampleLesson::onAdd( ... ) { // ... } function SimpleSampleLesson::onRemove( ... ) { // ... } function SimpleSampleLesson::ExecuteLesson( ... ) { // ... }
DefaultLessonPrep()

Generally, the only job the onAdd() method has is to call the lesson manager supplied function DefaultLessonPrep():

function SimpleSampleLesson::onAdd( ... ) { DefaultLessonPrep(); }


It is the job of the DefaultlessonPrep() function to prepare the lesson area for your lesson to build in. Among the things it does are: 1. Creates a group called objectMarkers, containing 28 markers from which to drop objects. 2. Creates billboards on the ground below each of these markers. 3. Creates signs on the hills of the lesson area, marking N, S, E, and West. A blank lesson, when loaded, might look something liked Figure B.1.35, which shows lesson marker billboards (10, 20, 30, ...), a sign (North), and the other screen elements we have already discussed, the Lesson Compass and the Metrics Dialog. The markers and signs are placed by DefaultLessonPrep().

19

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Figure B.1.35. Lesson w/ Marker Billboards and Signs


DefaultLessonPrep() Options

As was just mentioned, when DefaultLessonPrep() is called, the lesson manager will create marker billboards and North-South-East-West signs on the cauldron's sides. Not visible are drop points position above each of the billboards. The purpose of these drop points is to allow one to drop objects from a known position without having to specify coordinates. In TGE, the generally accepted compass points and their associated direction vectors are: North <010> East South <010> West Furthermore, the drop points themselves are named and located thus: Name North10 North20 North30 Position < 0 10 25 > < 0 20 25 > < 0 30 25 > 20 Name East10 East20 East30 <100> < -1 0 0 > Position < 10 0 25 > < 20 0 25 > < 30 0 25 >

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Name North40 North50 North60 North70 South10 South20 South30 South40 South50 South60 South70

Position < 0 40 25 > < 0 50 25 > < 0 60 25 > < 0 70 25 > < < < < < < < 0 0 0 0 0 0 0 -10 25 -20 25 -30 25 -40 25 -50 25 -60 25 -70 25 > > > > > > >

Name East40 East50 East60 East70 West10 West20 West30 West40 West50 West60 West70

Position < 40 0 25 > < 50 0 25 > < 60 0 25 > < 70 0 25 > < < < < < < < -10 -20 -30 -40 -50 -60 -70 0 0 0 0 0 0 0 25 25 25 25 25 25 25 > > > > > > >

In addition to these 28 named drop points, the mission specifies a central drop point named CentralDropPoint. It should be noted that the billboards below the drop points are actual offset slightly. This way objects may be dropped directly below the drop point and not be on top of the drop point's marker billboard.
Turning Off Billboards or Signs

In some lessons, you may not want the DefaultLessonPrep() method to place either signs or billboard markers. Therefore, there are three four ways to call the function. The first way, disables the North-South-East-West signs on the hills:

// No signs DefaultLessonPrep( $GPGT::NOSIGNS );


The second way disables the billboard markers on the ground.

// No billboards DefaultLessonPrep( $GPGT::NOMARKERS );


The third way disables both signs and billboard markers.

// No signs or billboards DefaultLessonPrep( $GPGT::NOMARKERS | $GPGT::NOSIGNS );

21

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

The fourth and final way enables both signs and billboard markers.

// Signs or billboards enabled DefaultLessonPrep( );


Using Drop Points Manually

In order to use a drop point for placing an object, you may refer to it by name and get its position like this:

%dummyBlock = new Item( dummyItem ) { dataBlock = "BaseItem"; position = North10.getPosition(); scale = "2 2 2"; };
Alternately, you may use one of three supplied functions to calculate a position and/or execute the object drop.
Drop Point Functions

The Lesson Manager provides three 'drop functions' for placing newly created objects in a lesson:

DropObject( %object , %offsetVector ) This function will drop an object to the ground, or on top of the first object found with a valid collision box below the dropping object. The dropping object starts its 'drop' at the position it was created at and drop from there. Note 1: To calculate a starting position, use CalculateObjectDropPosition(). Note 2: This function will not move TSStatic() objects. Their positions must be set once and only once, upon construction. Use CalculateObjectDropPosition() instead. DropObjectFromMarker( %object , %marker , %offsetVector ) This function drops an object from one of the markers, using the same rules as DropObject().

22

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

CalculateObjectDropPosition( %oldPosition , %offsetVector ) This function will calculated a drop point without moving the object and returns the calculated drop position.
Arguments and their Defintions (For all above functions): %object The object to be dropped.

%marker

A valid marker to be used as a starting point.

%offsetVector

An offset to adjust the drop by. Final positions will be: initially calculated drop + %offsetVector

The Lesson Summary ML File


Every lesson requires a description. That description is placed in the Lesson Summary ML File. A lesson summary should be short, just providing a title for the lesson and a summary of what it does. For example, this is the Lesson Summary for the Simple Sample Lesson (template):

<just:center><font:Arial Bold:18> <spush><color:FFFF00>Simple Sample Lesson <spop> <font:Arial:16><br><br> <just:left> <lmargin%:5> This is a simple sample lesson file, showing some of the basics.<br><br> Please see other lessons for more advanced feature usage and don't forget to read the <spush><color:FF2222>'Appendix B GPGT Lesson Kit'<spop>. <br> <br> <br> <just:center> <bitmap:gpgt/3DLessons/Volume1/000_SimpleSampleLesson/how>
If you need to provide extra information about the lesson, either write a document or use the Client Script File to populate and launch the Lesson Message Dialog.

23

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

The Client Script File


The Client Script files is responsible for doing key mappings for a lesson and for populating and displaying the Lesson Message Dialog, if it is needed. The Simple Sample Lesson (Template) defines the following Client Script file (edited for legibility):

// 1 - Define any functions we need function bumpIt( %val ) { if( !%val ) return; } commandToServer('bumpBlock');

// 2 - Create and define the new action map new ActionMap( lessonActionMap ); lessonActionMap.bind( keyboard , "b" , "bumpIt" ); lessonActionMap.bindCmd( keyboard, "h", "", "canvas.pushDialog( LessonMessage , 100 );"); lessonActionMap.push(); // 3 Clear, populate, and push 'Lesson Message Dialog' LessonMessageText.setValue("<font:Arial Bold:14>"); LessonMessageText.addText( ... ); // ... canvas.pushDialog( LessonMessage , 100 );
If we examine this code, we will see that it does the following: 1. It defines a function which will later be called as the result of a key press. This function does nothing more than send a command to the server. 2. It creates, populates, and enables an action map named lessonActionMap. Be aware, you must ALWAYS name the lesson action map lessonActionMap, otherwise the lesson manager will no know how to unload it later. 3. It clears the last message from the 'lesson message dialog', adds a new message(s), and then pushes it onto the canvas.

24

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.8.2. Step-by-Step Creation of New 3D Lesson


Step#1 Duplicate The Template
As your first step in making a new 3D lesson, please make a copy of the directory: , renaming it as: "~\gpgt\3DLessons\Volume1\000_SimpleSampleLesson" "~\gpgt\3DLessons\Volume1\My3DLesson"1 Then, in our new directory, rename

SimpleSampleLesson.cs as My3DLesson .cs, SimpleSampleLesson.ml.txt as My3DLesson.ml.txt, sample.png as my3dlesson.png, and sample_sel.png as my3dlesson_sel.png.

Step #2 Update The Lesson Map


Continuing with the creation of your new 3D Lesson, modify the contents of the lesson map to look like this:

My3DLesson.cs My 3D Lesson My3DLesson My3DLesson.ml.txt clientScripts.cs


In this implementation, we are specifying that: the lesson file is named My3DLesson.cs,

the arbitrary name of the lesson is My 3D Lesson, the scriptObject for your lesson will be named My3DLesson, the lesson summary file will be named My3DLesson.ml.txt, no lesson tile is specified (a blank tile will be supplied and the lesson name My 3D Lesson will be printed on it), and the client script file will be named clientScripts.cs.

In the future you may rename the lesson however you like, but for this first attempt, we will assume the lesson is named My3DLesson.
1

25

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Step #3 Modify The Lesson File


Because this is just a sample, we will merely open the lesson file My3DLesson.cs and rename every instance of SimpleSampleLesson as My3DLesson. Although no modifications are necessary for this example, be aware that by copying the template, we have now duplicated the definition of dummyItem::onPickup(), dummyItem::onCollision(), and serverCmdbumpBlock()

Step #4 Modify The Lesson Summary ML File


Because this lesson will not change the functionality of the template, we only need to open the file Lesson Summary ML File My3DLesson.ml.txt and change the BOLD parts of of this code:

<just:center><font:Arial Bold:18> <spush><color:FFFF00>My 3D Lesson <spop> <font:Arial:16><br><br> <just:left> <lmargin%:5> This is My 3D Lesson, showing some of the basics.<br><br> Please see other lessons for more advanced feature usage and don't forget to read the <spush><color:FF2222>'Appendix B GPGT Lesson Kit'<spop>. <br> <br> <br> <just:center> <bitmap:gpgt/3DLessons/Volume1/My3DLesson/how>
Step #5 Modify The Client Scripts
Although no modifications are necessary for this example, be aware that by copying the template, we have now duplicated the definition of 'bumpBlock()'.

26

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.1.9. Adding New Sample Scripts


In order to add new scripts that will be automatically loaded, simple create a script file (.cs) containing your new functions and be sure that the first three letters of the file's name are: sts. Examples names:

stsVol1_BasicScripting.cs stsVol1_AdvancedScripting.cs

Place you new file(s) in the 'gpgt' directory or in a sub-directory of 'gpgt' and the next time the kit is started, it will automatically find and loads the script file and all the function definitions therein.

27

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.2 Instructing and Collaborating


The GPGT Lesson Kit provides the ability for one person to host a 3D Lesson while other users participate remotely.

B.2.1. Running The 3D Lessons Server (MultiPlayer)


To run the GPGT Lesson Kit in Instructing/Collaborating mode, you must start a lesson server. To do so, simply follow these steps:

Load 3D Lessons Mission


Click on the Start Mission... button. Figure B.2.1. Start Mission...

Launch 3D Lessons Mission


Select 3D Lessons from the missions list, be sure that 'Host Multiplayer' is checked/filled, and click the Launch Mission! button as in Figure B.2.2. TGE will load the mission, load datablocks, do some lighting calculations and eventually pop you into the mission. See Figure B.2.3. Figure B.2.2. Missions Dialog When you are dropped into the mission, you will find yourself in an empty grassy cauldron (Figure B.2.4).

Figure B.2.3. Loading the mission... Figure B.2.4. In the 3D Lessons mission!

The remainder of the navigation steps proceed exactly as the do for singleplayer mode (documented in Section B.1.4 above). Once the server is started, clients must attach. Read on to learn how clients attach to the lesson.

28

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.2.2. GPGT Lesson Client (LAN)


To run the GPGT Lesson Kit in Instructing/Collaborating mode, you must start a lesson server. To do so, simply follow these steps:

Join Server
Click on the Join Server... button. Figure B.2.5. Join Server...

Server Query
The Lan Server radio button is automatically clicked (you may click it again to repeat the query). Momentarily, the local server should be listed in the left scroll list. Clicking on an entry in this list will display details about that server in the right window. Once you have selected the server you wish to connect to you may optionally type a name for your player and the click on the Join button. Figure B.2.6. Server Query Dialog TGE will load the mission, load datablocks, do some lighting calculations and eventually pop you into the mission.

As a client, your interactions are somewhat limited. You are only allowed whatever freedom the instructor's/collaborator's lesson allows you. You cannot load new lesson. only the person hosting the 3D Lessons can do this.

29

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.2.3. GPGT Lesson Client (Remote Networks)


The GPGT Lesson Kit can also allow clients on remote networks to connect to the server, but to do so requires the following:

The server must be outside of any firewall, or otherwise made visible on the Internet. The server machine or another machine elsewhere must be running a Master Server.

We did not discuss Master Servers in GPGT as this is a topic for a future book. However a rudimentary Master Server has been supplied and instructions for using it are provided below under Section B.2.5. Running The Included Master Server. Once a master server is running and at least GPGT server is advertising itself on the master server, the following steps can be followed by any client connected to the Internet:

Join Server
Click on the Join Server... button. Figure B.2.7. Join Server...

Server Query
Select the Master Servers radio butto. Momentarily, a list of available servers should be listed in the left scroll list. Clicking on an entry in this list will display details about that server in the right window. Once you have selected the server you wish to connect to you may optionally type a name for your player and the click on the Join button. TGE will load the mission, load datablocks, do some lighting calculations and eventually pop you into the mission.

Figure B.2.8. Server Query Dialog

As a client, your interactions are somewhat limited. You are only allowed whatever freedom the instructor's/collaborator's lesson allows you. You cannot load new lesson. only the person hosting the 3D Lessons can do this.

30

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.2.4. Special Considerations For Lesson Hosting


As the person hosting a lesson, you control the level of interaction available to a client, but you must keep the following facts in mind:

All Clients Run Client Scripts Be sure not to provide any interaction features you do not want available to a client via keystrokes (action maps). Alternately, you may provide some features but not document them in the Lesson Description Dialog. Every Client Will be Represented by a Single Blue Guy Avatar In this edition of the GPGT Lesson kit, all clients arrive in a lesson as a player, represented by the Blue Guy. Future editions may provide other options, but for now, a lesson area can get very crowded if the maximum of 63 (as set by the kit not TGE) clients are allowed to attach. Be sure to state ground rules for those who have attached. At any time, as the host, you may kill client connection by looking at the player, noting its client ID, and then deleting that client from the console (~):

123.delete(); Clients May Not Start New Lessons Only the host may change the current lesson

31

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.2.5. Running The Included Master Server


On the accompanying disk, you will find a ZIP file named masterserver.zip. It contains a master server implemented as a PERL script. This Master Server supplies the minimum set of features required to advertise and find remote servers. It does not provide any security features and should only be run in a safe computing environment.

B.2.5.1. Running the Master Server


To start the master server follow these directions (you must have PERL installed for this to work): 1. Unzip the masterserver.zip file to a directory of your choice. 2. Open the file c3masterserver.pl and find the line containing:

my $PORT = 7777;
3. Choose a port number for your server to advertise (28002 for example) and change this line of code to reflect the port number:

my $PORT = 28002;
4. Save the PERL script. 5. Execute the PERL script by double clicking it or ryping this on the command line:

perl c3masterserver.pl
B.2.5.2. Advertising the Lesson Server
Now that you have the master server running, to make the lesson server visible to clients, do this: 1. Edit the file gpgt\client\prefs.cs and find the line that contains this variable: $pref::Master0. 2. Edit the line so that it has the same IP address as the machine running the IP server (I usually run both the lesson server and the master server on the same machine). Also be sure that the port number is the same as the one you selected above. For example on my local network I use this:

$pref::Master0 = "2:192.168.123.15:28002";
3. Save the file and start the lesson server by following the direction listed in Section B.2.1. above.

32

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.2.5.3. Connecting Remote Clients


Now that you have the master server and the lesson server running, clients may connect by doing this: 1. Edit the file gpgt\client\prefs.cs and find the line that contains this variable: $pref::Master0. 2. Edit the line so matches the values used in Section B.2.5.2 above. For example:

$pref::Master0 = "2:192.168.123.15:28002";
3. Save the file and start the GPGT Lesson Kit. 4. Have students/collaborators connect by following the steps listed in Section B.2.3 above.

33

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.3 Recording Lesson Kit Sessions


It is possible to record a lesson kit session. That is, you may run the kit and record all inputs. Subsequently, another user can load these recorded inputs and replay the entire sequence. This is a standard feature (known as Journaling) of the Torque Game Engine which is often used for debugging failures, but which also lends itself to demonstrating exact sequences of events to a remote audience. In this short section, I will show you how to record a lesson, how to play it back, and they list a few prerecorded that you may play to see a demonstration of the lesson playback.

B.3.1. Recording A Lesson Kit Session


In order to record a lesson kit session, simply start the GPGT lesson kit from the command line as follows:

gpgt -jSave mySession.jrn


This will start the GPGT lesson kit and record all inputs to a journal file named mySession.jrn in the local directory. At the end of the session, this file will contain all inputs for th session. Any user may now play the session back exactly are recorded.

B.3.2. Replaying A Recorded Lesson Kit Session


In order to replay a lesson kit session, simply start the GPGT lesson kit from the command line as follows:

gpgt -jPlay mySession.jrn


This will start the GPGT lesson kit in journal-replay mode and will play back the journal mySession.jrn located in the local directory. Of course, this relies on the existence of the journal file, but I am assuming that you just recorded one following the steps above in section B.3.1. If not, please record a journal and then try replaying it, or replay one of the included sessions as listed in Section B.3.3. below. Please note, during the replay of a lesson kit session, no external inputs will be accepted by the GPGT lesson kit until the session is over.

B.3.3. Some Prerecorded Lesson Kit Sessions


In order to provide you some samples, the following GPGT lesson kit sessions have been prerecorded for you: Lesson Kit Session GUISampler1.jrn GUISampler2.jrn ToonInterfaces.jrn HUDs.jrn 3DLessons.jrn SampleScriptsConsole.jrn Demonstrates Demonstrates Demonstrates Demonstrates Demonstrates Demonstrates Description guiTextListCtrl. guiMouseEventCtrl. Toon Interfaces. the three HUD samples. several 3D Lessons. usage of Sample Scripts Console.

34

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4 GPGT 3D Lesson Descriptions


B.4.0. Lesson Template
B.4.0.1. SimpleSampleLesson
This is a lesson template. You can use it to create new lessons. Please see the "Lesson Kit User's Guide" appendix for instructions on creating your own lessons.

Example Bouncing Box

Description Press the 'b' key to apply an impulse to the bottom of this box.

Location North 10

B.4.1. SceneObject
B.4.1.1. SceneObject Transforms
This lesson shows how to use the various translation, rotation, and scaling methods provided by the SceneObject class.

Example

Description leftEgg shows that using code like: %obj.position = "10 10 10"; will not translate an object. rightEgg shows that using code like:

Location

leftEgg (wrong)

North 10

rightEgg (correct)

%obj.setTransform( "10 10 10" ); will translate an object.

North 10

initializedEgg setEgg smallEgg normalEgglargeEgg

initializedEgg uses a scale value provided in the new block, while setEgg is scaled as a result of a call to setScale(). Eggs scaled in to various sizes.

East 10 West 10

35

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.2. ShapeBase (General)


B.4.2.1. ShapeBase Rendering
Shows various shapeBase supported rendering features, including: - Fading - Fading & Hiding - Cloaking Levels - Blank cloakTexture - Re-Skinning Marker Description Demonstrates the ability to fade an shape out of view. Also shows that a faded object can still be collided with. Demonstrates that setHidden() will take a shape out of the world, including its collision mesh. Demonstrates re-skinning with setSkinName(). Demonstrates Various Levels of Alpha in a cloakTexture. Demonstrates cloaking with cloakTexture not set. Object(s)

North 10

fadeEgg

East 10 South 10 West 10 West 30

FadeHideEgg -normalCloak (left) cloakMore (middle) totalCloak (right) NoCloakEgg

B.4.2.2. ShapeBase Damage


Explores various shapeBase supported damage features, including: - Damaging - Disabling - Destroying - Exploding - repairing - Invincibility Marker North 10 East 10 South 10 West 10 Description Demonstrates damage and self-repair. Demonstrate damage and disabling. Demonstrates damage, destruction, postdestruction rendering, and explosion. Demonstrates Invincibility. Object(s) SelfHealingBlock DisableGears ExplodeGears InvincibleBlock

36

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.2.3. ShapeBase Energy


Explores various shapeBase supported energy features, including: - Energy Consumption - Recharging

Marker North 10 East 10

Description Demonstrates energy consumption and recharging Demonstrated disabling due to lack of energy.

Object(s) SelfRechargingBlock DisableEnergyGears

B.4.2.4. ShapeBase Animations


Explores various shapeBase supported animation features, including: - Blended - Cyclic

Marker North 10

Description Demonstrates combinations of cyclic blended animations.

Object(s) BlendAnimAlone0 BlendAnimAlone1 BlendCombo

B.4.2.5. ShapeBase Sounds


Explores use of sound threads for shapes, including differenct behaviors of: - MONO (16-bit) Sounds - STEREO (16-bit) Sounds

Marker North 10

Description Demonstrates difference between a MONO and a STEREO sound file when used for 3D sounds.

Object(s) soundEgg

37

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.3. Item
B.4.3.1. Item Rendering
Show various Item/ItemData supported rendering features, including: - Constant Lights - Pulsing Lights - Constant Light + Fading - Constant Light + Cloaking - Pulsing Light + Cloaking Marker(s) North 10 North 20 North 30 East 10 East 20 East 30 South 10 Description There are three items (eggs) at this marker locations. These eggs each emit a constant light of either Red, Green, or Blue. There are three items (eggs) at this marker locations. These eggs each emit a pulsing light (1.5 second period). One egg is rendered with a constant yellow light and then faded in and out over time. Notice that the light also fades in and out to match the translucency of the egg. One egg is rendered with a constant yellow light and then cloaked. Notice that the light is still fully visible. One egg is rendered with a pulsing yellow light and then cloaked. Notice that the light is still fully visible. Object(s) ConstantLightEgg0 ConstantLightEgg1 ConstantLightEgg2 PulsingLightEgg0 PulsingLightEgg1 PulsingLightEgg2 FadeAndHideConstantLightEgg

West 10

CloakedConstantLightEgg

West 20

CloakedPulsingLightEgg

38

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.3.2. Item Physics


Show various Item/ItemData supported physics features, including: - Static vs. non-static - Rotating - Sticky vs. non-sticky - Variations on friction - Gravity Effects - Elasticity Marker North 10 Description Object(s) In this sample, two eggs are repeatedly dropped from a low height. The left egg is NonStaticEgg non-static and thus falls, the right egg is made StaticEgg static and stays where it is placed. Demonstrates the effect of setting Item.rotate RotatingEgg to true. Demonstates stickiness property. Both eggs have been made bouncy, but one is sticky and StickyEgg the other is not. The sticky one does not NonStickyEgg bounce while the non-sticky one does. This sample demonstrates friction and its effect on a sliding object. From left to right, the NegativeFrictionEgg (-2.0) items have the following friction values: -2.0, 0.0, 0.3, and 2.0. ZeroFrictionEgg (0.0) The eggs are all simultaneously placed and then boosted towards the hillside to show the MediumFrictionEgg (0.3) effect of different frictions. Notice that the one with a negative friction HighFrictionEgg (2.0) actually speeds up and the one with a high friction stops almost immediately. This sample demonstrates the results of various elasticity settings. From left to right, the eggs have the following elasticity settings: NoBounceEgg (0.0) 0.0 , 0.1, 0.5, 1.0, and 2.0. Notice, that the 0.0 actually hesitates and then LowBounceEgg (0.1) settles, 0.1 settles immediately, 1.0 does not actually bounce back to its starting position, MediumBounceEgg (0.5) and 2.0 bounces higher and higher. The lesson here is that elasticity calculations FullBounceEgg (1.0) have some error, so you must adjust your game settings to match your need. Do not OverBounceEgg (2.0) assume the values will just work without testing them.

North 20

North 50

South 10

West 30

39

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.4. TSStatic
B.4.4.1. Static Shapes
Shows very simple uses of TSStatic.

B.4.5. ShapeBaseImageData (Images)


B.4.5.1. Image State Machines
Shows sample usage of ShapeBaseImage state machine. This is the sample from the book with a slight modification to the Green Light - Yellow Light - Red Light ... repeat timing.

B.4.5. InteriorInstance
B.4.6.1. Interiors
Shows sample InteriorInstance features, , including: - LOD - Re-lighting

Marker North 10

Description A sample interior is shown. It has 5 LOD levels. You may switch between them manually and examine the effect of relighting.

Object(s) InteriorInstance

40

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.7. Mission Objects


B.4.7.1. Lightning
This lesson demonstrates: - Lightning - Thunder - Lightning Scripting

Marker ----

Description Lighting Strike (Generated ONLY). Use generated lightning. Use textured lightning.

Key Strokes 1 g t

B.4.7.2. Particles
Particles lesson, showing sample definitions of: - ParticleData - ParticleEmitterData - ParticleEmitterNode Data - ParticleEmitterNode

Marker North 10 East 10 South 10 West 10

Description Bubbles. Two variations on smoke. Two variations on fire. The left fire uses animated particles, and the right is the same particle generated in various sizes. Sparks.

Object(s) -----

41

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.7.3. Precipitation (Sky Object)


This lesson demonstrates a few variations on precipitation: - Raining Cats 'n Dogs (key 1) - Snowing (key 2) - Cartoon Rain (key 3)

Name Raining Cats 'n Dogs

Description A goofy example exploring basic rain with splashes and sound. A second example demonstrating a soundless snowfall. Note: Because we are using the same mission, and because Sky.windEffectPrecipitation is true, we've made the mass of the snowflakes quite high so they will not be affected much by the wind. A final example, using low-mass rain-drops plus light turbulence.

Keystrokes Press Key 1

Snowing

Press Key 2

Cartoon Rain

Press Key 3

B.4.7.4. StormClouds (Sky Object)


Simple demonstration of stormClouds feature. Clouds fade in (100% on) and out (0% on) on key press.

Marker --

Description In this simple example, the clouds fade in and out based on a keystroke.

Keystrokes Press Key 1 to Toggle

42

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.8. Special Effects


B.4.8.1. Debris
Debris lesson, showing examples of: - 3D Debris - Debris w/ Fire Trail (particles) - Fast Moving Debris - Fading Debris - Non-Fading (popping) Debris - Debris w/ Low Friction - Bouncing Debris - Static on maxBounce

Marker North 10 North 30 East 10 East 30 South 10 West 10 West 30 West 70

Description Basic 3D Debris, with no fade. 2D Debris. Fast Moving 3D Debris. 3D Debris with flaming trail. Basic 3D Debris, with random fade. Bouncy 3D Debris. Replace w/ Static 3D Debris. Sliding Debris. (Uses large debris so you can see them.)

Object(s) ---------

B.4.8.2. Explosions
Explosion lesson, showing examples of: - Sub-Explosions - Particle Usage - Debris - Camera Shake

Marker North 20

Description Alternates between big explosion with debris, smoke, and camera shake AND a confetti explosion.

Object(s) --

43

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.8.3. Projectiles
Projectiles lesson, showing examples of: - Ballistic Projectiles - Non-Ballistic Projectiles - Delayed Activation - Projectile Particle Usage - Projectile Explosions Marker North 10 South 10 West 10 Description Slow moving non-ballistic projectile. A ballistic-projectile demonstrating delayed activation, elasticity, and friction. A purely ballistic projectile. Object(s) ----

44

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.9. Game Elements


B.4.9.1. Game Views
In this lesson, we explore the effects of: - POV Settings - FOV Settings. - setFOV() - setZoomSpeed()

Marker ----------

Description Switch camera datablock. Switch player datablock. Increase FOV. Decrease FOV. Increase Zoom Speed. Decrease Zoom Speed. Zoom. Toggle free-camera mode. Toogle 1st and 3rd POV.

Keystrokes 1 2 Up Arrow Down Arrow Right Arrow Left Arrow E CTRL+C TAB

Experiment with various combinations of camera datablocks, player datablocks, POV modes, and camera free modes. Also experiment with zooming. For example, try these settings: Player DB: testAvatar8 Camera DB: testCamera2 POV: 3rd Zoom Speed: 2000 Now experiment with zooming in and out by changing the zoom value with the up and down arrow keys.

45

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

B.4.9.2. Simple Inventory


In this lesson, the following inventory actions are demonstrated: - pickup - drop/throw - use

Marker North 10 .. 40 South 10 .. 40 East 10 .. 40 West 10 .. 40

Description Coin inventory items that self-replace, fade in/out, and emit light. They can be Thrown/Dropped. Holy Hand grenade inventory items that selfreplace, fade in/out, and emit light. They can be Used or Thrown/Dropped.

Keystrokes Press t to throw coin. Press g to use. (explodes) Press CTRL + g to throw.

B.4.9.3. Basic Vehicles


Vehicle Sampler, showing: - Box Car - Box Hover - Psionic Jeep - Flying Vehicle

Marker North 10

Description Shows three mountable, dismountable, and drivable vehicles.

North 10

Same vehicles can be 'bounced.

Press Press Press Press Press

Keystrokes Key 1 Box Car Key 2 Psionic Jeep Key 3 Box Hover Key 4 Box Flyer b to bounce vehicle.

46

Appendix C. Combined Maze Runner Lessons

Table of Contents
Appendix C. Combined Maze Runner Lessons...................................................................................... 1 C.0 Setting Up Our Workspace....................................................................................................... 4 C.1 Lesson #1 Terrain for Our Game .......................................................................................... 8 C.2 Lesson #2 loading datablocks............................................................................................... 9 C.3 Lesson #3 Game Coins....................................................................................................... 10 C.4 Lesson #4 Fade and Fireball Blocks..................................................................................... 12 C.5 Lesson #5 Maze Blocks....................................................................................................... 16 C.6 Lesson #6 Simplest Player................................................................................................. 18 C.7 Lesson #7 Preparing Our Game Inventory............................................................................ 23 C.8 Lesson #8 Lava in the Cauldron.......................................................................................... 26 C.9 Lesson #9 Starry Night....................................................................................................... 27 C.10 Lesson #10 Low Lighting.................................................................................................. 28 C.11 Lesson #11 Stormy Weather............................................................................................. 29 C.12 Lesson #12 Teleport Station Effect.................................................................................... 31 C.13 Lesson #13 Celestial Bodies.............................................................................................. 34 C.14 Lesson #14 Teleport Stopper............................................................................................ 35 C.15 Lesson #15 Teleport Triggers............................................................................................ 36 C.16 Lesson #16 MoveMap....................................................................................................... 40 C.17 Lesson #17 Level Loader.................................................................................................. 42 C.18 Lesson #18 Game Events.................................................................................................. 50 C.19 Lesson #19 FireBall Explosion............................................................................................ 55 C.20 Lesson #20 The FireBall.................................................................................................... 58 C.21 Lesson #21 Game Sounds................................................................................................. 61 C.22 Finishing the Prototype........................................................................................................ 66 C.23 Finish Gameplay Code......................................................................................................... 66 C.24 Improve Feedback............................................................................................................... 74 Note1 : The versions of the lessons included in this document may vary slightly from the book versions, but this will not affect the creation of your game.

C.0 Setting Up Our Workspace


Before we can work on any lessons, we must first set up a work area. Everything that you need to do this is supplied on the CD that comes with this guide. If you examine the CD you will find the following directories (and others):

\Base - This directory contains data and scripts that are used in the lessons and can also be used later to make new games. Please see the Lesson Kit Assets appendix for additional information about the contents of this directory. \MazeRunner - Excluding the data and scripts in \Base and the executable from the demo kit, this directory contains all of the unique resources and scripts required to build the MazeRunner prototype. \MazeRunnerAdvanced - This directory contains a completed version of MazeRunner with several additional features as are suggested in Section 14.10 Improving The Game. \TorqueDemoInstallers - This directory contains installers for TGE.

At this time, if you do not have the demo installed on your machine, please do so by running the appropriate installer (based on your computer and operating system type). Once you have finished with that, please continue reading.

C.0.1 Starting from Torque Demo


First, let's make a new directory named "MazeRunner" and place it on a drive with at least 100 MB of free space. We'll want some elbow room while we work. In my case, the directory would be on my working drive here: "H:\MazeRunner". Second, now that we have a place to work, let's copy the entire contents of the TGE demo directory into our new directory "MazeRunner".

C.0.2 Write Cleanup Scripts


It is a good idea to have the ability to remove temporary files from a working directory. If we remove all compiled scripts (DSOs) before re-running the engine, we are insuring that only new script content will be used. Additionally, it is a good idea to occasionally remove terrain lighting files (ML). To accomplish these two tasks, we will write some scripts. The first script (if you are running Windows) will be called "DELDSO.bat". It is used to delete all compiled script files (DSO cleaning) and contains this simple line of script:
del /S /F *dso

In UNIX/Linux/OSX, the file would be "deldso", and the content of the file is:
rm -rf *dso

The second file (if you are running Windows) will be called "DELML.bat". It is used to delete all terrain lighting files (ML cleaning) and contains this simple line of script:
del /S /F *ml

In UNIX/Linux/OSX, the file would be "delml", and the content of the file is:
rm -rf *ml

We'll run the DSO cleaner each time we modify our scripts, and occasionally we'll run the ML cleaner to get rid of stale lighting files.

C.0.3 Copy MOD Directory


Although it is possible to modify the demo to create MazeRunner, it will be far simpler to start with a blank slate instead. To that end, a bare bones mod has been provided. To start with this mod, please copy \MazeRunner\A_SettingUp\prototype from the accompanying disk into \MazeRunner.

C.0.4 Modify "main.cs"


Next, edit "main.cs" and change line 6 from this:
$defaultGame = "demo";

to this:
$defaultGame = "prototype";

This will use our new prototype mod instead of the demo mod.

C.0.5 Add Systems Scripts


The accompanying disk comes with a number of scripts that are provided to simplify your gamewriting endeavors. We will be discussing some of these scripts in the guide, and those we do not discuss are documented in the "Scripted Systems" appendix. From the accompanying disk, please copy the "\Base\Scripts\EGSystems" directory into "\MazeRunner\prototype". Then, edit the onStart() function in "\MazeRunner\prototype\main.cs" so it looks like this (BOLD lines are new code):
function onStart() { // Maze Runner Changes Begin --> exec("./EGSystems/SimpleInventory/egs_SimpleInventory.cs"); exec("./EGSystems/SimpleTaskMgr/egs_SimpleTaskMgr.cs"); exec("./EGSystems/Utilities/egs_ArrayObject.cs"); exec("./EGSystems/Utilities/egs_Misc.cs"); exec("./EGSystems/Utilities/egs_Networking.cs"); exec("./EGSystems/Utilities/egs_SimSet.cs"); exec("./EGSystems/Utilities/egs_String.cs"); // <-- Maze Runner Changes End //.. leave remaining code alone

C.0.6 Add Maze Runner Data


You are not expected to create your own content for this game. I have included all of the models, textures, and sounds you will need. From the accompanying disk, please copy the 1. "\Base\Data\GPGTBase" directory into "\MazeRunner\prototype\data", and 2. "\MazeRunner\A_SettingUp\MazeRunner" directory into "\MazeRunner\prototype\data".

C.0.7 Create Maze Runner Scripts Directory


Although we will not be placing anything in yet, in preparation for our lessons, let's create the directory: "\MazeRunner\prototype\server\scripts\MazeRunner".

C.0.8 Test Run


After saving the modified "main.cs" and "prototype\main.cs", run the executable you placed in "MazeRunner" and the prototype should start up. If it does not, please retrace your steps and see if you missed something.

C.0.8.1. Windows users.


On Windows platforms, some users will get a warning about a missing or wrong sound setup. If, and only if, you get this message, copy the "\MazeRunner\A_SettingUp\OpenAL32.dll" file (found on the accompanying disk) into your "MazeRunner" directory and try again.

C.0.9 Ready To Start


OK, if you got the prototype to run, you're ready to start.

C.1 Lesson #1 Terrain for Our Game


Alright, here is the first of several lessons in which we'll apply the massive amount of knowledge we're gaining in a practical situation, building our own simple game step-by-step. In this first quick lesson, we'll create a terrain for our game to be played out on. Follow the simple steps in this section to get started.

C.1.1. Copy required files.


From the accompanying disk, please copy the "\MazeRunner\Lesson_001\heightFields" directory into "\MazeRunner\prototype\data".

C.1.2. Generate new terrain


To generate the cauldron for our game terrain, do the following (see Figure C.1.1): 1. Start up your prototype. 2. Start the "Maze Runner" mission. 3. Start the Terraformer. 4. Use the 'Bitmap' operation to generate a terrain using the file "\MazeRunner\prototype\data\heightFields\mazerunner.png".

Terrain Preview Terraformer Settings Figure C.1.1. Terrain preview and Terraformer settings. After applying the generator, the terrain should be shaped like a cauldron. Save the mission.

C.1.3. Adjust spawn point.


Now we have a simple terrain. You might also want to use the Inspector to move the spawn point to "0 0 100" so we don't have such a long ways to fall when we spawn into the mission again. Now, don't forget to save your changes. And with that, we have taken the first small step towards making our little Maze Runner game!

C.2 Lesson #2 loading datablocks


As we work on the Maze Runner Game, we are going to need several datablocks and the accompanying scripts that were created for your use in this game and in your future creations. So, let's take the time now to get them loading. From the accompanying disk, please 1. Copy the "\Base\Scripts\GPGTBase" directory into "\MazeRunner\prototype\server\scripts" 2. now, edit the function onServerCreated() in the file "\MazeRunner\prototype\server\game.cs" to look like this (BOLD lines are new or modified):
exec("./markers.cs"); exec("./player.cs"); exec("./GPGTBase/loadGPGTBaseClasses.cs"); // MazeRunner

In the above script, we are loading all of the Volume 1 base datablocks (classes) after all the other datablocks that FPS normally includes. We also added the data files that go with the datablocks. To test for a successful load, simply start the prototype and load the "Maze Runner" mission. Then run the "Creator" tool and you should have directories in the creator as in Figure C.2.1.

Figure C.2.1. Creator Directory

C.3 Lesson #3 Game Coins


In this lesson, we will examine the game coin's datablock definition. Later, we will implement scripts to pick up these coins, but for now, all we need to do is talk about the coin's geometry, the datablock definition, and the creation script.

C.3.1. Copy required files.


From the accompanying disk, please copy the file into \MazeRunner\Lesson_003\coins.cs \MazeRunner\prototype\server\scripts\MazeRunner. Now, edit the function onServerCreated() in the file: \MazeRunner\prototype\server\game.cs to look like this (BOLD lines are NEW or MODIFIED):
exec("./GPGTBase/loadGPGTBaseClasses.cs"); // MazeRunner exec("./MazeRunner/coins.cs"); // MazeRunner

Please note, until this step, the \MazeRunner\prototype\server\scripts\MazeRunner directory did not exist, so you need to create it yourself.

C.3.2. Coin geometry.


The geometry for this coin is very simple, and can be found in: "\MazeRunner\prototype\data\MazeRunner\Shapes\Items\coin.ms3d", where we copied it earlier. If you load the file in Milkshape, you will see that it is nothing more than a thin disk. It has one render mesh and no collision mesh. Because this model is used for an item, a collision mesh will automatically be generated by TGE. The skin was generated using Ultimate Unwrap 3D. It's simple and does the job. Now, all we need is a datablock and a creation script (onAdd()).

C.3.3. The coin datablock.


The datablock for our coins is very simple. If we look at the file we just copied, we will see this datablock definition:
datablock ItemData( Coin : BaseItem ) { shapeFile = "~/data/MazeRunner/Shapes/items/coin.dts"; category = "GameItems"; sticky = true; lightType = NoLight; mass = 1.0; respawn = false; };

The coin item has the following attributes:


It is an instance of Item (just to be clear about this). It is derived from BaseItem. (There are base datablocks for all of the classes we discuss in this guide.)

10

We'll be able to find this object under Shapes/GameItems in the Creator menu. It is sticky and will stay put when it hits terrain or an interior. It does not emit light. As a rule, I never create a massless object. This avoids any future difficulties should I choose to apply an impulse to the shape. So, this coin gets an arbitrarily chosen mass of 1. When this coin is picked up we don't want it to be respawned. So, we set the field respawn to false. This won't mean anything to you yet, but when we discuss the Simple Inventory system in Chapter 7, Gameplay Classes, this will become clear.

C.3.4. The coin onAdd().


We have mentioned callbacks only briefly thus far, and we will discuss them later in Chapter 9 Gameplay Scripting. For now, just know that all SimObject instances and all instances of children of SimObject call the onAdd() callback after the object it created and initialized. Later, when we write the scripts to place objects, it will become clear that we want objects to stay put when they are placed. Coins have the option of being static (won't move on their own), or 'non-static' (affected by gravity and other forces). Therefore, we need to force the coin to be static by making a suitable onAdd() callback. Find the following code at the end of the file we just copied:
function Coin::onAdd( %DB , %Obj ) { Parent::onAdd( %DB , %Obj ); %Obj.static = true; %Obj.rotate = true; }

The callback does the following:


Calls the Parent:: version of this callback to allow it to do any work it needs to do (optional and based on your design methodology). Sets the object to a static object. Now, it won't fall (due to gravity) or be affected by impulses. Make the coin rotate. Now the render code will rotate the coin. Please note, this only rotates the render mesh, not the collision box that TGE generates.

C.3.5. Testing.
To verify that our changes worked, you can: 1. re-start the prototype, 2. open the Maze Runner mission, 3. start the Creator, 4. look under Shapes and find the folder GameItems, and 5. open the GameItems folder to find a new placeable shape, Coin. If this did not work, check your console for errors (typos, files not found, etc).

11

C.4 Lesson #4 Fade and Fireball Blocks


In our game, we are going to have two kinds of special maze blocks. The first one will be a block that can be faded in and out of view, and the second will be a block that shoots fireballs.

Figure C.4.1. Fade blocks (left) and Fireball blocks (right) Both of these blocks require features from the ShapeBase hierarchy. The fade blocks use the fading and hiding features. The fireball block uses the reskinning property. In this lesson, we will concentrate on the mesh properties and the datablocks that go with these two blocks. Later, we will write the scripts to fade the fade blocks, and to shoot fireballs from the fireball blocks.

C.4.1. Copy required files.


From the accompanying disk, please copy the: 1. file \MazeRunner\Lesson_004\fadeblock.cs into: \MazeRunner\prototype\server\scripts\MazeRunner. 2. file \MazeRunner\Lesson_004\fireballs.cs into: \MazeRunner\prototype\server\scripts\MazeRunner. Then, modify onServerCreated() in \MazeRunner\prototype\server\scripts\game.cs to include these lines (BOLD lines are new):
exec("./MazeRunner/coins.cs"); // MazeRunner exec("./MazeRunner/fadeblocks.cs"); // MazeRunner exec("./MazeRunner/fireballs.cs"); // MazeRunner

C.4.2. Block geometry.


The blocks will both have the same geometry, namely a single render mesh and a single collision mesh. To see this geometry, open the file: \MazeRunner\prototype\data\MazeRunner\Shapes\MazeBlock\blockA.ms3d using Milkshape. You will see that this model has a render mesh named block0 and a single collision mesh named collision-1. To enable re-skinning, we need to do something special with the model's skin.

12

C.4.3. Re-skinning.
Still in MS3D, if you look at the material named skin you will see that we are using a texture named base.skin.png. (It only shows as base on the MS3D button, but trust me, the file is named 'base.skin.png'. By using a skin with this name, we will later be able to change the skin on this model. To clarify, the rules for reskinning are simple: 1. Skin your mesh with texture named base.XYZ.png, where XYZ can be anything you choose. The important thing to notice is that the skin starts with base.. This tells TGE this is a reskinnable mesh. 2. Create as many extra textures as you need, as long as they have the name LMN.XYZ.png, where XYZ is the same name from step #1 and LMN is a name to make your texture name unique. 3. Reskin a shape at any time by writing this code:
%obj.setSkinName( "LMN");

The above code tells the mesh to use the texture LMN.XYZ.png instead of 'base.XYZ.png'.

C.4.3.1. Self-illuminating.
Because we are using a sort of cartoon/platform theme in our game, we will want all of the blocks to self-illuminate. This means that they will not be affected by the in-game lighting. To do this, I choose the self-illuminating option when exporting (using the DTS-Plus exporter). Please see Figure C.4.2.

Figure C.4.2. Making material self-illuminating

C.4.4. Datablocks.
Alright, these base blocks are pretty much good to go. Let's create some datablocks and we can move on.

13

C.4.4.1. Fade blocks datablock .


For the fade block, please open this file: \MazeRunner\prototype\server\scripts\MazeRunner\fadeblocks.cs. In this file, and find the following lines of script:
datablock StaticShapeData( FadeBlock ) { category = "FadeBlocks"; shapeFile }; = "~/data/MazeRunner/Shapes/MazeBlock/blockA.dts";

isInvincible = true;

This datablock has the following attributes:


Based on the value in 'category', these blocks will found in the Creator tree under "\Shapes\FadeBlock". It loads the mesh for the model we just discussed. It is invincible and thus takes no damage. We want this so that fireballs striking a fadeblock will not damage it.

Not shown, but present in the completed copy of this file (as written by me), there is another bit of code at the top. It is a reloader. Reloaders are little scripts that are used to re-load the file, thus reloading the datablock definitions and any scripts in the file. In single-player mode, I use reloaders to reload files I have changed while the mission is still running. This way I can make minor tweaks to scripts, etc. and not have to reload the entire mission. The reloader for the fadeblocks.cs file would be:
function rldfb() { exec("./fadeblocks.cs"); }

14

C.4.5. Fireball blocks datablock .


For the fade block, please open this file: '\MazeRunner\prototype\server\scripts\MazeRunner\fireball.cs In this file, and find the following lines of script:
datablock StaticShapeData( FireBallBlock ) { category = "FireBallBlocks"; shapeFile = "~/data/MazeRunner/Shapes/MazeBlock/blockA.dts"; isInvincible = true; };

As you can see, this datablock is identical (except for the name) to our fadeblock datablock. The behavior differences are entirely script based, and the only reason we need another datablock is for scoping. We'll get to this later.

15

C.5 Lesson #5 Maze Blocks


The primary geometry of our maze consists of blocks and groups of blocks. Later, when we discuss the level building scripts, we'll talk about how these blocks are placed. For now, we will restrict ourselves to the creation of these blocks. The MazeBlocks share the same geometry and skin set up as the fade blocks and fireball blocks from Lesson #4 (above). So, if you have not completed that lesson please do it first.

C.5.1. Block Geometry


In addition to the single block geometry we produced for the prior blocks, we need several additional variations for the maze blocks. In theory, we could build our entire level out of single blocks. However, I don't advise this as we do pay a penalty (network and processing) for each block in the scene. So, knowing in advance that we will have various structures in our levels combining several blocks, we will make a few larger meshes. This way if we need an area the size of say nine (3x3) blocks, we can place just one big block. If you look in the ''\MazeRunner\prototype\data\MazeRunner\Shapes\MazeBlock' directory we created earlier, you will see that there are blocks A through J. Those shapes have the following geometries (as seen from the top down):

As you may notice, there are four square blocks, and three each of the 'linear' block for horizontally oriented and vertically oriented. It may not be apparent immediately, but with these blocks we can create symmetrically laid out levels without needing to re-orient the blocks at placement time.

16

C.5.2. Placing Blocks


We aren't writing the code to place these block yet, but when we do it will look something like this:
new TSStatic() { shapeName = "~/data/MazeRunner/Shapes/MazeBlock/block" @ %blockType @ ".dts"; position }; scale = %actX SPC %actY SPC $CurrentElevation; = "1 1 1";

This code snippet is actually from our level builder and as you can see, we will be dynamically selecting the mesh to use as well as calculating the position as we place the block.

C.5.2. Examine The Blocks


This guide does not discuss modeling, nor does it cover the various modeling tools. However, as the blocks have already been created for you, I suggest that you examine a few to see how they are constructed. Pay particular attention to blocks E through J. Don't forget, all of the mazeblocks have been copied over to our data directory already at: "\MazeRunner\prototype\data\MazeRunner\Shapes\MazeBlock". You can open any of the block models (*.MS3D) with a copy of Milkshape.

17

C.6 Lesson #6 Simplest Player


For our game, we will need to make a very simple player. This player is nothing more than a ball with three nodes (joints), root, eye, and cam (Figure C.6.1).

Figure C.6.1. Simplest Player.

C.6.1. Copy required files.


From the accompanying disk, please copy the file "\MazeRunner\Lesson_006\mazerunnerplayer.cs" into "\MazeRunner\prototype\server\scripts\MazeRunner". Now, edit the function onServerCreated() in the file "\MazeRunner\prototype\server\game.cs" to look like this (BOLD lines are new or modified):
exec("./MazeRunner/fireballs.cs"); // MazeRunner exec("./MazeRunner/mazerunnerplayer.cs"); // MazeRunner

C.6.2. Simplest Player skeleton.


Because we're not going to animate this player, it doesn't need very many nodes (joints) in its skeleton. In fact it only needs a root node and the two camera mount points (see Table C.6.1). Table C.6.1. Simples Player nodes.
Node root eye cam The 1st POV camera mount. The 3rd POV camera mount. Description The root node, specifying the physical bottom of the mesh.

18

C.6.2.1. Root node.


In this model, the root node is located at the bottom of the player and all vertices in the player are assigned to it. This node defines the bottom of the player and is where mesh contacts the ground. If this node were placed in the middle of the player, the player would sink into the ground.

C.6.2.2. Eye and Cam Nodes


The next node is the eye node. It is located on the "forehead" just above and between the eyes. This is where the 1st POV camera will be mounted. The last node is the cam node. This is located behind and above the model. It doesn't necessarily need to be here, but this model was designed (in part) to show the difference between an eye mount and a cam mount. As you've probably guessed, this is where the 3rd POV camera will mount.

C.6.3. Simplest Player geometry.


C.6.3.1. Visible mesh.
There isnt much to say about this. It's a ball. The player has one mesh and one skin. We're not using any IFLs or other fancy features.

C.6.3.2. Collision mesh.


We do not need to define a collision mesh for instances of the Player class, as the engine does this automatically.

C.6.4. Simplest Player animations.


Earlier I said that this player is not animated. I lied. OK, I didn't exactly lie. For any player to work, the root animation needs to be exported at a minimum. Then, to get rid of some annoying warnings, you'll need to export the other animations (shown in Table C.6.2). Since the player isn't going to need these animations, I've left them blank and just exported the same sequence for each. Table C.6.2. Animation descriptions.
Animation root run back side jump standjump fall land Forward running animation. Backwards running animation. Sideways stepping animation. Moving jump animation. Stationary jump animation. Long falling animation which starts about 1 second after fall starts. Hard landing animation. (Played while in recovery-mode). Description A default animation which plays while the player is at rest.

19

The sequences for these animations are shown in Table C.6.3. Table C.6.3. Sequences for animations.
Animation root run back side jump standjump fall land Start Key 1 1 1 1 1 1 1 1 End Key 2 2 2 2 2 2 2 2 FPS 1 1 1 1 1 1 1 1 Cyclic Y Y Y Y N N N N Blended N N N N N N N N

seq: root=1-2, fps=1, cyclic seq: run=1-2, fps=1, cyclic seq: back=1-2, fps=1, cyclic seq: side=1-2, fps=1, cyclic seq: jump=1-2, fps=1, cyclic seq: standjump=1-2, fps=1 seq: fall=1-2, fps=1, cyclic seq: land=1-2, fps=1, cyclic

Table C.6.3. shows the following information: Animation This is the (required) name for the animation sequence in question.

Start Key/End Key These are the frames in which the named animation begins and ends. FPS This is the base frame-rate at which the animation should be played. Cyclic This indicates whether the animation should be played once or in a cycle. Blended This indicates whether the sequence should be blended or not.

Finally, for each sequence there is a combined line something like "seq: root=1-2, fps=1, cyclic." This is what you would type in for the default exporter, but since we're using the DTS Plus exporter, you will enter the values via that exporter's dialog. See the "MS3D Cheat Sheets" in the appendix for a quickie tutorial on exporting animations with DTS Plus. Also note, the old exporter does not support blending.

20

C.6.5. Simplest Player's datablock.


Because the datablock for this shape is a bit long, only the pertinent portions are listed here:
datablock PlayerData( MazeRunner : BasePlayer ) { shapeFile = "~/data/MazeRunner/Shapes/Players/MazeRunner.dts"; boundingBox invincible = "1.6 1.6 2.3"; = true;

groundImpactMinSpeed = 1000; ImpactMinSpeed = 1000; renderFirstPerson = false;

observeThroughObject = true; }; // ...

This player has the following notable attributes: 1. It derives (copies) from the BasePlayer datablock that comes on the accompanying disk. 2. As would be expected, the mesh we just built (or copied) is used. 3. The shape is a little bigger than the normal character, so we've increased the dimensions of its bounding box from "1.2 1.2 2.3" to "1.6 1.6 2.3," adding an extra three-tenths of meter in the x and y dimensions. 4. The player is marked as invincible because we are not going to use damage to determine if it is "Dead." Instead, we'll kill it immediately if the mesh is hit by a fireball or if it falls in the lava. 5. Impacts are effectively disabled by setting the velocities above any velocity the player will be able to achieve in this game. 6. renderFirstPerson is disabled, meaning the mesh will not render in 1st POV. 7. The camera has been instructed to use the player's camera settings (observeThroughObject is true). To get the entire datablock, please copy: "MazeRunnerPlayer.cs" (MazeRunner_008) to "\MazeRunner\prototype\server\scripts\MazeRunner"

C.6.6. Loading the datablock.


Now, edit the "\MazeRunner\prototype\server\scripts\game.cs" file and update onServerCreated() to contain this code (BOLD is new code):
exec("./MazeRunner/fireball.cs"); // MazeRunner exec("./MazeRunner/MazeRunnerPlayer.cs"); // MazeRunner

21

C.6.7. Using this player.


Now, to use this player instead of the Blue Guy we have been using thus far, edit the "\MazeRunner\prototype\server\scripts\game.cs" file and modify the highlighted code (below) in GameConnection::createPlayer() to look like this:
function GameConnection::createPlayer(%this, %spawnPoint) { //... // Create the player object %player = new Player() { dataBlock = MazeRunner; // Change this line client = %this; }; //...

22

C.7 Lesson #7 Preparing Our Game Inventory


In this short lesson, we will examine the steps required to get our player (MazeRunnerPlayer) to use the Simple Inventory system to pick up coins.

C.7.1. Loading the inventory system.


In order to use our inventory system, we must ensure that it is getting loaded. In fact, we have already done this first step. When we set up our "MazeRunner" directory and prepared a copy of prototype mode, we modified the file "\MazeRunner\prototype\main.cs". We had it load the inventory system's main script file:
function onStart() // in main.cs { exec("./EGSystems/SimpleInventory/egs_SimpleInventory.cs"); // MazeRunner exec("./EGSystems/SimpleTaskMgr/egs_SimpleTaskMgr.cs"); // MazeRunner //..

This then loaded the other script files that compose this system.
// in egs_SimpleInventory.cs exec("./SimpleInventoryBuilder.cs"); exec("./SimpleInventoryGeneral.cs"); exec("./SimpleInventoryValidation.cs");

C.7.2. Adding an inventory.


With the inventory system being loaded, we now have to hook it to any classes that wish to "own" an inventory. The simplest way to do this is to have each class add an inventory system to the object when the object's onAdd() callback is executed. Take a look in this file: "\MazeRunner\prototype\server\scripts\GPGTBase\Player\PlayerDataConsoleMethods.cs" It contains the definitions for all of the important callbacks used by a player class. All of these callbacks are scoped to PlayerData::, ensuring that they will be called unless a new datablock, deriving from PlayerData::, redefines the callbacks. We are already loading this script file, so we get the benefit of all of these callbacks already. One of these callbacks is PlayerData::onAdd(), which, among the other things that it does, creates an inventory and saves a reference to it in the player object.

23

function PlayerData::onAdd(%DB,%Obj) { // 1 Parent::onAdd(%DB,%Obj); // 2 %Obj.enableMountVehicle = true; // 3. %Obj.myInventory = newSimpleInventory(); } %Obj.myInventory.setOwner(%Obj);

This means, we do not have any work to do. We do not have to implement a new version of onAdd() scoped to MazeRunnerPlayer::, but if we wanted to, we could write one like this:
function MazeRunner::onAdd( %DB , %Obj ) { Parent::onAdd( %DB , %Obj ); // Usually called firstPersonOnly } // Other statements here ...

C.7.3. Removing an inventory.


It is normal to destroy objects created in the onAdd() callback, when the onRemove() callback is executed. Again, this is taken care of for us by the base code we are using from the prototype. Here is the onRemove() callback from the same file we just examined above:
function PlayerData::onRemove(%DB,%Obj) { // 1 if( isObject( %Obj.myInventory ) ) %Obj.myInventory.delete(); // 2 Parent::onRemove(%DB,%Obj);

Easy as pie! Of course, we could again write a specialized version of the onRemove() callback and just be sure to call the Parent:: version at some point (normally last):
function MazeRunner::onRemove( %DB , %Obj ) { // Other statements here ... } Parent::onRemove( %DB , %Obj ); // Usually called last

24

C.7.4. What about constraining?


In our game, we don't want to constrain the inventory, but if we wanted, for some reason, to prevent the player from picking up coins, we could simply modify the onAdd() callback to look like this:
function MazeRunner::onAdd( %DB , %Obj ) { Parent::onAdd( %DB , %Obj ); } %obj.myInventory.setInventoryMaxCount( Coin , 0 ); // No coins for you!

C.7.5. In review.
I know you're disappointed that there was no work to do in this lesson. So, let's just summarize the steps instead. This way you will know what they are when you are on your own. 1. Load inventory system scripts. 2. Ensure that the onAdd() callback adds an inventory to the object when it is created. 3. In your own onAdd(), be sure to constrain the inventory system as is required by your game. Use the constraint methods included with the inventory system. 4. Make sure that the onRemove callback deletes the inventory.

25

C.8 Lesson #8 Lava in the Cauldron


The game will have lava at the bottom of the cauldron. Falling into this lava kills the avatar and causes it to be respawned in its original spawn position. For now, we're only worried about getting the visual part done (the lava). We'll handle the interactions later. For now, please do the following: 1. Start up your prototype, run the "Maze Runner" mission, and start the Creator Tool. 2. Create a water block (Mission Objects -> Environment -> Water), only providing the name "MazeRunnerWater" when the creator dialog appears (Figure C.8.1).

Figure C.8.1. Creating a water block. 3. Using the Inspector, be sure that the water has the settings shown in Table C.8.1. Table C.8.1. Water settings for lava.
Parameter position scale UseDepthMask surfaceTexture shoreTexture specularMaskTex specularColor specularPower All others Value -256 -256 55 512 512 15 true prototype/ data/GPGTBase/water/lava.png prototype/ data/GPGTBase/water/lava.png prototype/ data/GPGTBase/water/lavaspecmask.png 1 1 1 0.2 12 Use defaults

OK, so it doesn't look exactly like lava, but it gets the point across. You can tweak this to your heart's content after we get the game running. For now, let's move on.

26

C.9 Lesson #9 Starry Night


If you are building the Maze Runner game while you read this guide, the original sky is a bit too bright for our game, so we will need to do the following to create a starry night instead. 1. Start up your prototype, run the "Maze Runner" mission, and start the Inspector Tool. 2. Find the Sky object and change the DML file to one you will find in "/MazeRunner/prototype/data/GPGTBase/skies/starrynight/starry_sky.dml". This file contains this list of texture names:
stars0 stars1 stars2 stars3 stars4 stars5 stars6 cloud1

The textures used in this file are just a set of five generated starfields, a placeholder for the seventh texture, and a randomly (noise) generated translucent cloud texture (Figure C.9.1).

sky0 .. sky5 (similar)

sky6 (placeholder) Figure C.9.1. Sky textures.

cloud1

3. Using the Inspector, be sure that the water has the settings show in Table C.9.1. Table C.9.1. Sky settings for starry night.
Parameter materialList cloudHeightPer[0] cloudHeightPer[1] cloudHeightPer[2] cloudSpeed1 cloudSpeed2 cloudSpeed3 visibleDistance fogDistance fogVolume1 fogVolume2 fogVolume3 all others Value prototype/ data/GPGTBase/skies/starrynight/starry_sky.dml 0.5 0 0 0.0005 0 0 1000 2000 550 0 300 000 000 Use defaults

27

C.10 Lesson #10 Low Lighting


If you are building the Maze Runner game while you read this guide, the original sun (lighting) is a bit too bright for our game, so we will need to do the following to match our night sky. 1. Using the Inspector, lower the lighting values for the Sun object to the values in Table C.10.1. Table C.10.1. Lighting values for low lighting.
Fields elevation azimuth color ambient Values 90 90 0.5 0.3 0.3 1 0.2 0.2 0.2 1

Now, relight the scene (ALT + L) to see the values take effect.

28

C.11 Lesson #11 Stormy Weather


If you are building the Maze Runner game while you read this guide, we are now going to add some rain, lightning, and thunder to our scene. The game is meant to have a "cartoon spooky" theme, and these element will add to that.

C.11.1. Adding the rain.


1. Start up your prototype, run the "Maze Runner" mission and start the Creator tool. 2. Select a precipitation object (Mission Objects -> Environment -> Precipitation), giving it the object name "MazeRunnerRain" and choosing the datablock BaseRain (Figure C.11.1).

Figure C.11.1. Adding rain. 3. Open the Inspector and give the new rain object the settings in Table C.11.1. Table C.11.1. Settings for rain.
Parameter minSpeed maxSpeed rotateWithCameraVel numDrops boxWidth boxHeight doCollision all others Value 1 1.5 true 2000 200 100 0 Use defaults

29

C.11.2. Adding the lightning and thunder.


1. Go back into the Creator tool. 2. Select a lightning object (Mission Objects -> Environment -> Lightning), giving it the object name "MazeRunnerLightning" and choosing the datablock BaseLightning (Figure C.11.2).

Figure C.11.2. Adding lightning. 3. Open the Inspector and give the new lightning object the settings in Table C.11.2. Table C.11.2. Settings for lightning.
Parameter position scale strikesPerMinute strikeWidth strikeRadius color fadeColor chanceToHitTarget boltStartRadius all others Value 0 0 300 256 256 250 6 1.5 128 0.89 0.8 0.42 1 0.5 0.9 0.9 1 0 32 Use defaults

30

C.12 Lesson #12 Teleport Station Effect


If you are building the Maze Runner game while you read this guide, we are now going to create the datablocks for a set of particle emitters that will be used later to mark the position of our teleport stations. We will need three distinct versions of this emitter. So, our strategy will be to create a base ParticleData datablock and a base ParticleEmitterData datablock using the previous ParticleData datablock. Then, we will use the inheritance feature of TorqueScript to create two copies of each datablock with minor modifications. This will give us a total of six datablocks. For the ParticleEmitterNodeData datablock, we'll just use the basePEND datablock that comes with this guide.

C.12.1. Copy required files.


From the accompanying disk, please copy the file into "\MazeRunner\Lesson_012\teleporters.cs" "\MazeRunner\prototype\server\scripts\MazeRunner". Now, edit the function onServerCreated() in the file "\MazeRunner\prototype\server\game.cs" to look like this (BOLD lines are new or modified):
exec("./MazeRunner/mazerunnerplayer.cs"); // MazeRunner exec("./MazeRunner/teleporters.cs"); // MazeRunner

C.12.1.1. ParticleData (TeleportStation_PD0).


We want our particles to be nebulous particles of medium size with a red, green, or blue coloration.
datablock ParticleData(TeleportStation_PD0) { dragCoefficient = 0.0; gravityCoefficient = -0.50; inheritedVelFactor = 0.0; constantAcceleration = 0.0; lifetimeMS = 400; lifetimeVarianceMS = 100; useInvAlpha = false; textureName = "~/data/GPGTBase/particletextures/smoke"; colors[0] = "0.7 0.1 0.1 0.8"; colors[1] = "0.7 0.1 0.1 0.4"; colors[2] = "0.7 0.1 0.1 0.0"; sizes[0] = 0.1; sizes[1] = 0.3; sizes[2] = 0.3; times[0] = 0.0; times[1] = 0.5; times[2] = 1.0; };

31

As can be seen,

this particle will float upward since it has a negative gravity coefficient; it has a short lifetime between 300 and 500 milliseconds; the particle it uses is nebulous (see negative image in Figure C.12.1); it fades from medium red to dark red evenly; and it starts off small and triples in size over time. Figure C.12.1. Smoke particle.

C.12.1.2. ParticleEmmitterData (TeleportStation_PED0).


datablock ParticleEmitterData(TeleportStation_PED0) { ejectionPeriodMS = 1; periodVarianceMS = 0; ejectionVelocity = 2.0; ejectionOffset = 0.5; velocityVariance = 0.5; thetaMin = 0; thetaMax = 80; phiReferenceVel = 0; phiVariance = 360; overrideAdvance = false; particles = "TeleportStation_PD0"; };

As can be seen,

This particle emitter ejects a new particle every millisecond, meaning we'll have up to 500 particles alive at any time (per emitter); it ejects particles at 1.5 to 2.5 meters per second starting at the center to 0.5 meters out; the ejection vector will be anywhere about the center and starts from slightly upward to straight up; and of course, it uses the particle we just made.

32

C.12.1.3. Duplicate datablocks.


The last step before trying these emitters out is to duplicate them so we have three sets. As you can see when looking at the code, we have taken advantage of TGE's datablock inheritance:
datablock ParticleData(TeleportStation_PD1 : TeleportStation_PD0 ) { colors[0] = "0.1 0.7 0.1 0.8"; colors[1] = "0.1 0.7 0.1 0.4"; colors[2] = "0.1 0.7 0.1 0.0"; }; datablock ParticleEmitterData(TeleportStation_PED1 : TeleportStation_PED0 ) { particles = "TeleportStation_PD1"; }; datablock ParticleData(TeleportStation_PD2 : TeleportStation_PD0 ) { colors[0] = "0.1 0.1 0.7 0.8"; colors[1] = "0.1 0.1 0.7 0.4"; colors[2] = "0.1 0.1 0.7 0.0"; }; datablock ParticleEmitterData(TeleportStation_PED2 : TeleportStation_PED0 ) { particles = "TeleportStation_PD2"; };

We only needed to change the particle colors and to use the correct particle in our new emitters.

C.12.1.4. Testing the emitters.


We're not ready to use these emitters in our game, but we should test them. Do the following: 1. Restart the prototype. 2. Load the "Maze Runner" mission. 3. Use the Creator to place a particle emitter ("Mission Objects -> Environment -> ParticleEmitter"). 4. Give the emitter (node) any name you like. 5. Use the basePEND ParticleEmitterNodeData datablock. 6. Select one of the three ParticleEmitterData datablocks we just examined (Figure C.12.2).

Resultant Emitters ParticleEmitter Dialog Settings

33

Figure C.12.2. Testing the emitters.

C.13 Lesson #13 Celestial Bodies


If you are building the Maze Runner game while you read this guide, we are now going to create some celestial bodies to go with our game. I have to apologize, but the celestial bodies we will implement are just too darn big (in terms of code) to show in the book. Instead, I will summarize their behaviors here and allow you to look at the scripts yourself.

C.13.1. Loading the celestial bodies.


The celestial bodies example as been created for you. To add it to the Maze Runner mission, follow these steps: 1. Open the file "/MazeRunner/prototype/data/missions/mazerunner.mis" 2. Open the file ''\MazeRunner\Lesson_013\CelestialBodies.cs" and copy the contents into your copy-buffer (like you are doing a copy-paste operation). 3. Paste the data you just copied into the "mazerunner.mis" file before these lines:
}; //--- OBJECT WRITE END ---

Now, you can restart the prototype, load the Maze Runner mission, and you should see three celestial bodies in the sky.

C.13.2. Dying star.


The first celestial body is the "Dying Star". This celestial body is designed to represent a sun in our game world solar system. This sun is approaching the end of it's life and has shifted from yellow to red. To create the effect of a sun with moving sun spots, I have animated the brightness, the coloration, the size, and the rotation. Together with the image we are using for the sun, it may give the illusion of an active sun surface.

C.13.3. Far planet.


The next celestial body is the "Far Planet". This celestial body is designed to represent a distant planet in our game world solar system. It is stationary relative to the planet we are on.

C.13.4. Near moon.


The last celestial body is the "Near Moon". This celestial body is designed to represent a moon rotating about our planet. Its azimuth changes slowly over time; during this transition it rises and falls in the sky.

34

C.14 Lesson #14 Teleport Stopper


When the player runs into a teleport station, we'd like the avatar to be stopped. To do this, we can use a P-zone set up as follows:
%Pzone = new PhysicalZone() { position = vectorAdd( "1 -1 0" , %Obj.getPosition() ); rotation = "1 0 0 0"; scale = "2 2 4"; velocityMod = "0"; gravityMod = "1"; appliedForce = "0 0 0"; polyhedron = "0 0 0 1 0 0 0 -1 0 0 0 1"; };

As can be seen, this code is meant to be script driven. That is, we'll be substituting values in for position when we drop the object into the world. The key things to notice are: 1. The position is offset by a vector (we haven't discussed vectorAdd() yet, but it adds two vectors and returns the result). The reason for this is that the polygon used to define the polyhedron field is offset. Its corner is at the origin and therefore the cube is not centered. This can either be corrected either by changing the polyhedron values or by offsetting while placing. I chose the latter. 2. velocityMod is set to zero. This means that shapes entering the P-zone should stop moving. That is pretty much all for now. Later, we'll use this code in our teleporter building scripts.

35

C.15 Lesson #15 Teleport Triggers


In this lesson, we will examine the scripts needed to teleport the player from one teleport station to another. We will also look at the code that combines the prior parts of the teleport trigger components.

C.15.1. Trigger datablocks.


We could in theory use the DefaultTrigger datablock that comes with the prototype, but it would be better to define a new one so we can guarantee that we have a unique namespace to scope our methods and callbacks with. So, we will define our datablock like this:
datablock TriggerData(TeleportTrigger) { tickPeriodMS = 100; };

C.15.2. Teleport scripts.


Later, when we are writing our level-building scripts, it will be nice if we already have a method for attaching the particle effects and the physical zone to our teleporter triggers. With a little planning, this won't be that hard to do.

C.15.2.1. Teleport trigger planning.


We haven't discussed it much yet, but the user (and we) will be able to define new levels by creating simple text files. These files will have maps of the level in them made up of various numbers and letters, representing the positions of level pieces like blocks, coins, and teleporters. Knowing that our teleporters will be associated with maker letters in this file, we can plan on the teleporter trigger being a sort of parent. We will read the level file, create a trigger where it tells us to, and store information in the trigger that tells the trigger which of the three types of teleporters it is. Recall there are three types of teleporters (all of them function the same, but this allows us to have distinct sets that are connected to each other). So, let's just assume that the letters used to represent teleporters are going to be x, y, and z. Furthermore, let's assume that the trigger is created first and then the type is stored a field named "type." Lastly, we will assume the the level loader will then call our teleport builder script to add a particle emitter node and a P-zone in the same position as the trigger.

36

C.15.2.2. Teleport trigger implementation.


All of that planning and assuming gets us some code like this:
function Trigger::AttachEffect( %Obj ) { echo("\c5 Added Teleport Trigger"); %emitter[X] = "TeleportStation_PED0"; %emitter[Y] = "TeleportStation_PED1"; %emitter[Z] = "TeleportStation_PED2"; %effect = new ParticleEmitterNode() { position = vectorSub(%Obj.getWorldBoxCenter(), "0 0 2"); rotation = "1 0 0 0"; scale = "1 1 1"; dataBlock = "basePEND"; emitter = %emitter[%Obj.type]; //emitter = "TeleportStation_PED0"; velocity = "1"; }; %Obj.myEffect = %effect; %Pzone = new PhysicalZone() { position = vectorAdd( "1 -1 0" , %Obj.getPosition() ); rotation = "1 0 0 0"; scale = "2 2 4"; velocityMod = "0"; gravityMod = "1"; appliedForce = "0 0 0"; polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000"; }; %Obj.myPzone = %Pzone; }

Basically, this function creates an array of particle emitter datablock names indexed by x, y, and z. Then, it creates a particle emitter in the position of the trigger (ID passed as argument to this function) and looks at the stored type to de-reference the datablock array, getting the correct datablock name to match the type. Next, the function creates a P-zone (remembering to offset it a little) in the same position as the trigger. After this function is finished executing there will be a trigger, a particle emitter node, and a P-zone all in the same location. Viola! A teleporter station. So, how do we make the teleporter go? Let's do that next.

C.15.3. Trigger callbacks.


To make the teleporters do work for us, we need to implement the onEnterTrigger() and onLeaveTrigger() callbacks. Instead of showing you the code (which you can just load and examine), I will present the methodology used to teleport correctly.

37

C.15.3.1. onEnterTrigger().
This callback has the lion's share of the work. Initially, all triggers will start off active. When the avatar runs into a teleporter trigger, that trigger executes the following steps: 1. Check to see if it is disabled. If so, the callback aborts. 2. Enable the P-zone it owns (to be sure the avatar gets stopped). 3. Check for the existence of other triggers. When we create triggers in our level building scripts, all triggers are added to one of three trigger groups based on their type. Then, if we recall that all SimObject children can determine what group (if any) they are stored in, it will become clear that each trigger can get the group it belongs in and choose a trigger from that group until finds one that is not itself. 4. Disable the P-zone on the target trigger (so player is not stopped on exiting that trigger). 5. Disable the target trigger (to prevent teleport loops). 6. Do some fading effects and schedule a setTransform() call, moving the avatar to the location of the target trigger. Once the avatar arrives at the target trigger, it has to leave that trigger to reactivate it. Also note, the setTransform() call moves the avatar and causes the current trigger to call its own onLeaveTrigger() callback, thus reactivating it.

C.15.3.2. onLeaveTrigger().
This callback has very little to do. Basically, when the avatar leaves the trigger area, the trigger will be told to re-enable the trigger and to reactivate() the P-zone.

C.15.3.3. Tricky bits.


While examining the scripts, you may notice a couple of bits of code that we have not yet discussed. The first of these is a call to getRandomObject(). It is being called on a SimGroup. This is a method I have provided in the included systems script files (loaded when we set up our environment). This method simply iterates over a SimSet (or child) and randomly selects an entry from the set, returning the ID of the selection.
... %Trigger.getGroup().getRandomObject() ...

The second bit involves the use of the function getWords(). In this line of code, we're replacing the position part of the player's transform with a new position while retaining the player's orientation information. This is done by getting the words representing the orientation. As you will learn later, a word is any string, and words are separated by spaces. Thus, we can look at the transform as a string containing seven words. Using getWords(), we simply get the top four words and then we paste them onto a new position matrix, making a new transform:
%newTransform = %newPos SPC getWords( %oldTransform, 3 , 6);

38

C.15.4. Trigger cleanup.


It is also worth mentioning that when the trigger is destroyed, it will call its onRemove() callback , which will delete the effects attached to this trigger. Nice and clean.
function TeleportTrigger::onRemove( %DB, %Obj ) { if( isObject( %Obj.myEffect ) ) %Obj.myEffect.delete(); if( isObject( %Obj.myPzone ) ) %Obj.myPzone.delete();

39

C.16 Lesson #16 MoveMap


In this short lesson, we will examine the ActionMap that comes with the prototype and discuss some small changes to it and other scripts that will ensure the behavior we are expecting from our game.

C.16.1. Required behavior.


In our game, we want the following key mappings:
W A S D SPACEBAR Mouse Move TAB Move forward. Move left. Move backward. Move right. Jump Camera yaw and pitch. Switch 1st and 3rd POV.

One key mapping we would specifically like to disable (eventually) is:


ALT + C Free camera mode.

We don't want people using free camera mode to cheat and find coins without risking their avatar's life.

C.16.2. MoveMap.
The Maze Runner prototype comes with an ActionMap already implementing the above mappings as well as many others. The name of this action map is MoveMap. One of the things that new users find confusing is the fact that MoveMap is created in two places. It is created in the file "\MazeRunner\prototype\client\scripts\default.bind.cs" and in "\MazeRunner\prototype\client\config.cs". So, where do we go if we want to modify this ActionMap? Well, if we look in the function initClient() in the file "\MazeRunner\prototype\client\init.cs", we will see this code:
// Default player key bindings exec("./scripts/default.bind.cs"); exec("./config.cs");

It seems pretty straightforward that we should edit "config.cs". But then, perhaps it isn't so straightforward. The file "config.cs" is in fact saved every time we quit our game:
// In file: \MazeRunner\prototype\main.cs echo("Exporting client config"); if (isObject(moveMap)) moveMap.save("./client/config.cs", false);

40

This is why new folks get confused. With the MoveMap being created in two places and being automatically saved at the end of a game, one is left wondering. So, let me help clarify.

C.16.2.1. When to modify "config.cs".


If you are going to add a new key mapping to the MoveMap ActionMap, or if you are going to modify an existing mapping in the MoveMap ActionMap, you should be sure that you are not running the game and then make your changes in the "config.cs" file.
moveMap.bindCmd(keyboard, "y", "echo(\"Yo yo\");", "echo(\".. awesome yo.\");");

C.16.2.2. When to modify "default.bind.cs".


Never add new mappings or modify existing mappings (in MoveMap) in this file. The only things that should go into this file are

alternate ActionMap definitions

GlobalActionMap.bind(keyboard, "F9", cycleDebugRenderMode);

and functions that may be called from an action map.

function moveleft(%val) { $mvLeftAction = %val * $movementSpeed; }

C.16.3. Making our changes.


In our game, we are happy with current mappings, except that we wish to eventually disable free camera mode. So, when we want to do this, simply remove this line from "config.cs":
moveMap.bind(keyboard, "alt c", toggleCamera);

For now, I suggest leaving this in, but when we get ready to release our game to the public this line needs to be removed. Additionally, we might want to remove this code from "default.bind.cs":
function toggleCamera(%val) { if (%val) commandToServer('ToggleCamera'); }

41

C.17 Lesson #17 Level Loader


In this lesson, we will discuss the level loader code. We will not be listing all of the code here as it is rather lengthy. Instead, we will describe how it works and how the code is structured. Please note, the level loader is responsible for loading and starting all elements of the level. This includes the fireball shooting block, which we have not completely covered yet. Specifically, we have not discussed the fireballs themselves, nor have we spoken, of the code that fires them. If you wish, you may skip ahead to lesson #20 in Chapter 11 to see how they work. Not doing so will not affect the current lesson, but until we complete that lesson, the level loader won't start the fireball blocks correctly.

C.17.1. Copy required files.


From the accompanying disk, please copy the file "\MazeRunner\Lesson_012\teleporters.cs" into "\MazeRunner\prototype\server\scripts\MazeRunner". Now, edit the function onServerCreated() in the file file: "\MazeRunner\prototype\server\game.cs" to look like this (BOLD lines are new or modified):
exec("./MazeRunner/teleporters.cs"); // MazeRunner exec("./MazeRunner/levelloader.cs"); // MazeRunner

C.17.2. Levels versus layers.


In the following description, we will be using the words level and layer. A level comprises one or more layers of game elements. A level may have any number of layers.

C.17.3. Level files.


The premise of this level loader is quite simple. Our goal is to load a single mission and then, at any time we wish, load the components that make up a level. By using a level map and a level loader we may define as many levels as we want without needing to hand create an entire mission and then load the mission (which is generally slower than placing items by script for single-player games). The first thing we need to do is define the parts of a level file.

C.17.3.1. Level file format.


We want to be able to make levels with multiple elements and multiple layers. To do this, the level file cannot be constrained to a fixed length. Instead, it must by dynamic. After some thought I came up with the syntax for this file shown in Table C.17.1.

42

Table C.17.1. Line/Element Tokens.


Line/Element Line 0 This will increment the current elevation at which blocks and other elements are being placed by 4 meters. This will decrement the current elevation at which blocks and other elements are being placed by 4 meters. This indicates to the level loader that the next line in the file will specify a layer type. LAYER_DEFINE This indicates to the level loader that the next 16 lines will contain a map that designates where blocks are placed. This indicates to the level loader that the next 16 lines will contain a map that designates where obstacles (teleport stations and fireball blocks) are placed. This indicates to the level loader that the next 16 lines will contain a map that designates where pickups (coins) are placed. This indicates to the level loader that the next 16 lines will contain a map that designates where the player will be dropped at the beginning of the mission. Meaning This line is used to store the numeric ID of the level that follows this one.

LAYER_UP

LAYER_DOWN

BLOCKS

OBSTACLES

PICKUPS

PLAYERDROPS

That is it for the syntax. Now, let's designate what letters mean in the actual layer definitions (those 16 lines).

C.17.3.2. Tokens.
Each layer definition is composed of 16 lines of 16 characters, meaning that each layer definition may have up to 256 elements in it. Because we have a multitude of things to place (and because we want to leave room for expansion), we will be reusing letters (tokens) between layer types. Table C.17.2 lists what individual tokens mean in the various layer contexts.

43

Table C.17.2. Definitions of tokens.


Layer Type/Token BLOCKS A-J These designate one of the level blocks we discussed in lesson #5. These are the fade blocks. The number specifies the number of seconds until the block fades. We will discuss how this fading works in the next chapter. Meaning

0-9 OBSTACLES X,Y,Z

One of these will produce a teleport station.

0-9

These are the fireball blocks, where each number is a block firing in a specified direction. For example: 0 North, 1 NorthEast, ..., 7 East , 8 NorthEast, and 9 is random.

PICKUPS C PLAYERDROPS P A player drop point. The player is dropped at the first point found. A coin.

C.17.4. Level loader mechanics.


The mechanics of the loader are pretty straightforward. It will consume whatever file it has been told to load until it has placed all of the contents or until it hits some kind of error in the level file.

C.17.5. Level loader definition.


So, we have some rules upon which to base our level building, and thus we have rules upon which to base the design of the loader. Furthermore, we know the loader must read the file until it is consumed, regardless of how many layers are defined in the file. Let's get started. Go ahead and load up the "levelloader.cs" file in your favorite browser and then follow along as we discuss it here.

C.17.5.1. Elevations and level increments.


The first thing we do in our loader is define some global variables with which to track:

$BaseElevation Beginning elevation for every new level (not layer). $LevelIncrement Level up/down step size. $CurrentElevation Current elevation we are building at (current layer).

C.17.5.2. Classifying tokens.


We are dealing with a lot of different tokens. We will need to categorize these tokens into groups to minimize our code size. Because we don't want to waste time doing multiple comparisons to determine if any one token is a teleporter, a fireball block, etc., we need a way to reduce the effort required to categorize tokens. The trick is to create an array where the index of the array is the expected token and the value in the array gives us the information we need. For example: 44

$BLOCKCLASS[A] = NORMAL; $BLOCKCLASS[B] = NORMAL; $BLOCKCLASS[C] = NORMAL; // ... $BLOCKCLASS[0] = FADE; $BLOCKCLASS[1] = FADE; $BLOCKCLASS[2] = FADE; // ...

In the above code, we're saying that any token "A..C" will correspond to a normal block while 0..2 will be a fade block. So, we don't want to of write the code like this:
if( ( %token $= A ) || ( %token $= B ) || ( %token $= C ) ) { // Normal Block Code Here } else if if( ( %token $= 0 ) || ( %token $= 1 ) || ( %token $= 2 ) ) { // Fade Block Code Here }

We can write it like this instead:


switch$( $BLOCKCLASS[%token] ) { case NORMAL: // Normal Block Code Here case FADE: // Fade Block Code Here }

As you can see, this code is not only more elegant, but also significantly faster than the multiple comparison case before (and that was with only 6 of the 20 possible block cases shown).

C.17.5.3. BuildLevel().
We've prepared the globals and helper variables we'll need now let's write the loader function. The buildLevel() function takes a single argument containing the numeric ID of the level to load. The function assumes that all level files are stored in the directory "\MazeRunner\prototype\data\Missions\LevelMaps" Given the number 0, the loader will attempt to open a file named "\MazeRunner\prototype\data\Missions\LevelMaps\levelNum0.txt"

45

If the level loader successfully opens this file, the first thing it will do is read the first line, which contains the numeric ID of the level that follows this one. If no next level is defined, the loader fails out. So far, nothing mysterious has been done, but the next bit of code may seem strange. For several lines, you will see bits of code like this:
if( isObject( gameLevelGroup ) ) gameLevelGroup.delete(); MissionGroup.add( new SimGroup( gameLevelGroup ) ); gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( gameLevelGroup.add( new new new new new new new new SimGroup( SimGroup( SimGroup( SimGroup( SimGroup( SimGroup( SimGroup( SimGroup( mazeBlocksGroup ) ); fadeGroup ) ); FireBallMarkersGroup ) ); TeleportStationGroupX ) ); TeleportStationGroupY ) ); TeleportStationGroupZ ) ); TeleportStationEffectsGroup ) ); CoinsGroup ) );

Remember, we are building our levels dynamically. As part of this effort, we are destroying the prior level if it exists. Also, to make our lives simple, we will be tracking all of our objects in named SimGroups. This is ideal because much of the processing our game does is of an iterative nature, and it is easy to iterate over a SimGroup. So, the above statement and the remainder like it in the function are merely removing the last level's SimGroups (if they exist) and then creating these named SimGroups:

gameLevelGroup This is the big daddy of all level SimGroups. It will contain all of our subsequent SimGroups for this level. Thus, deleting just this group kills all the children groups and their contents. mazeBlocksGroup All normal blocks are stored in this group. fadeGroup All fade-able blocks are stored here. Later we will iterate over this group to maintain the fadeblocks' behaviors. fireBallMarkersGroup All fireBall blocks are stored here. Like the fadeGroup, we iterate over this group to keep the fireBall blocks firing. TeleportStationX .. TeleportStationZ These three sets are used to store the three types of teleporter. Teleport stations stored in the same group will target each other. TeleportStationEffectsGroup Although we have an onRemove() method that deletes the Pzone and Particle Emitter Node attached to a trigger when the trigger is deleted, I prefer to store the IDs of these effects in a SimGroup, too. That way, there is no question that they will get deleted on level load (or on mission exit). CoinsGroup This last group stores all coins (that have not been picked up). We will use this later to determine when the level is complete and it is time to load the next one.

Next, we will see the beginning of the level loader's main processing loop:
while(!%file.isEOF() ) {

46

From this point on, the level loader will read in lines from the file until the file is empty or an error occurs. Upon first entering this loop, the function reads a line and checks to see what task it represents, LAYER_UP, LAYER_DOWN, or LAYER_DEFINE. For a LAYER_UP or a LAYER_DOWN, we increment/decrement and then go back to the top of the loop (by using continue) to get the next task. If the task does not match any known task type, the function aborts. Eventually, the task we get will be a LAYER_DEFINE. This tells the loader that the next line will be a LAYER_TYPE. So, the level loader reads the next line and decides which layer type it is, BLOCKS, OBSTACLES, PICKUPS, or PLAYERDROPS. If it is none of these, the function fails out. Assuming the function read a valid layer type, it will use another of those system scripts supplied with the prototype and load the next 16 lines into an arrayObject (a scripted class I created so that we may create arrays that can be passed between functions and methods). After reading in the next 16 lines (into our arrayObject), the level loader will then pass this array to a specialized function that does the layout for that level type:

layOutBlocks() - This lays out normal blocks and fade blocks. layOutObstacles() - This lays out fireball blocks and teleport stations. layOutPickups() - This lays out coins. playerDrop() - This will drop the player into the level at a specified point.

After the current layout pass, the loader goes back to the top of the file-reading loop and continues until the end of file (or error). Eventually, the file will be empty and we will drop out of the loop. At this point in the code, you will see a function (in two places) that may not yet be familiar to you:
if( fadeGroup.getCount() ) fadeGroup.schedule( 5000 , fadePass ); if( FireBallMarkersGroup.getCount() ) FireBallMarkersGroup.schedule( 1500 , firePass );

In both of the above statements, the script is telling the engine schedule an event to occur in, one in 5000 milliseconds and one in 1500 milliseconds. The first event is the calling of the function fadePass(). It will be called like this:
fadeGroup.fadePass();

The second event is the calling of the function firePass(). It will be called like this:
FireBallMarkersGroup.firePass();

47

Each of these statements will cause a special function (not yet covered) to iterate over the specified SimGroup and to "do something" to each entry. We will cover this in the next chapter.

C.17.5.4. LayOutBlocks().
We will talk about the first of the four layout functions, and then I will leave you to examine the other three on your own. They are all designed quite similarly so there should be no mystery. This function has the responsibility for creating content in the world based on the values in the arrayObject it has been passed. To do its jobs, the function uses a nested loop and reads every token of every line and parses these tokens by category (using the trick we discussed at the beginning of this lesson) and then by specific type. It is assumed that every token represents a world space of "4 4 4". Thus, the current position is incremented by "4 4 0" to keep us on the current layer. When a token is found that matches a known category, an object in that category is created. Being smart, we named our files and datablocks in such a way that we can merely append the token to a generic version of the filename or datablock name when loading a file or building an object from a datablock. Once this function has consumed all of the lines in the arrayObject, it deletes the object.

C.17.6. Temporary spawn point.


One side effect of destroying a level is that the player will fall into the lava because there is no place to stand. So, to solve this problem, while the level loads, we should create a place for the player to stand temporarily. This can be accomplished by editing the file "\MazeRunner\prototype\data\Missions\mazerunner.mis" and adding the following to the end of the mission file (BOLD lines are new):
new TSStatic() { position = "0 0 295"; rotation = "1 0 0 0"; scale = "1 1 1"; shapeName = "~/data/MazeRunner/Shapes/MazeBlock/blockA.dts"; };

}; //--- OBJECT WRITE END ---

48

Additionally, to get the spawn to work properly, we need to move the spawn point. So, in the same file, locate our spawn point and change the position to this:
new SimGroup(PlayerDropPoints) { new SpawnSphere() { position = "0 0 300"; rotation = "1 0 0 0"; scale = "1 1 1"; dataBlock = "SpawnSphereMarker"; radius = "1"; sphereWeight = "100"; indoorWeight = "100"; outdoorWeight = "100"; locked = "False"; lockCount = "0"; homingCount = "0"; };

};

Now, when we start the mission, the player will be high above the cauldron and on subsequent loads the avatar will be moved here temporarily.

C.17.7. Testing the level loader.


At this point, you should be able to test the level loader. Simply start the prototype, open the "Maze Runner" mission, open the console, and type "buildLevel( 0 );" Your player should be moved to the extra block we just inserted for a few seconds, and then it should drop onto a new level.

49

C.18 Lesson #18 Game Events


In this lesson, we will examine the scripts used to fade blocks in and out, and we will examine the functions used to shoot fireballs on a regular basis. Now that we have covered the scheduling, string manipulation, and scripted math, we should be ready to examine how these gameplay scripts work. Please note: This lesson depends on lesson #4.

C.18.1. Fade blocks.


There are three blocks of code we are interested in for the fade blocks. The first of these is in the file "/MazeRunner/prototype/server/scripts/MazeRunner/levelloader.cs" At the end of the function BuildLevel(), there is a little snippet of code that checks to see if there are any fade blocks in the fadeGroup SimGroup. If there are, the loader schedules a fadePass() in 5000 milliseconds:
if( fadeGroup.getCount() ) fadeGroup.schedule( 5000 , fadePass );

C.18.1.1. fadePass().
This function has the task of coming back every $stepTime (1000) milliseconds and updating all of the fade blocks. The motivation for updating all the blocks simultaneously is that it gives us greater control over the behavior of the blocks than if each block scheduled its own maintenance. Also, by maintaining a single entry and exit point, we only use one schedule, thus reducing overhead.
function SimSet::fadePass( %theSet ) { %theSet.forEach( fadeStep , true ); } %theSet.schedule( $stepTime , fadePass );

As can be seen, this function merely iterates over the blocks in the set and runs fadeStep() on each of them.

C.18.1.2. FadeStep().
This function has the responsibility for advancing the fade status of an individual fade block by one time period. A fade block can be in one of three states: 1. waitToFadeOut The block is waiting to begin a fade. 2. waitToFadeIn The block is faded out and waiting to begin fading in. 3. wait The block is in a dead-cycle waiting for all other blocks to complete the current fade cycle. A fade cycle is always 10 seconds long (as implemented in "fadeblocks.cs"). During a single fade cycle, every single fade block will fade out, fade in, and wait for its peers to finish their fade cycle.

50

By using this method instead of allowing blocks to fade in, fade out, fade in, ad infinitum, without synchronizing, we avoid chaos. The game would be no fun if the blocks faded in and out chaotically. But, because we can rely on a cycle always taking 10 seconds and then repeating itself, the player can plan ahead after observing a cycle or two. However, enough talking. Let's look at the code:
function StaticShape::fadeStep( %theBlock ) { %theBlock.timer = %theBlock.timer - $stepTime; // Check for flip-time if( %theBlock.timer <= 0 ) { switch$(%theBlock.action) { case "waitToFadeOut": %theBlock.timer = $basePauseTime; %theBlock.startFade( $fadeTime , 0 , true ); %theBlock.schedule( $fadeTime , setHidden , true ); %theBlock.action = "waitToFadeIn"; case "waitToFadeIn": %theBlock.timer = $basePauseTime; %theBlock.setHidden( false ); %theBlock.startFade( $fadeTime , 0 , false ); %theBlock.action = "wait"; case "wait": %theBlock.timer = %Obj.maxTime; %theBlock.action = "waitToFadeOut";

As we can see, individual blocks have an internal timer containing some predefined value. When that timer gets down to (or below) zero, it is time to change the block's state and to do some work. Initially, all blocks will have these values:

timer This value will be between 1000 and 10,000 milliseconds. maxTime This value will be the same as timer. The value in this field is never changed after the block is implemented. action All blocks start out executing the action waitToFadeOut.

Now, if we restrict our discussion to just one block and assume that the block has a timer and maxTime of 1000 milliseconds, over time, we will see the behavior described in Table C.18.1.

51

Table C.18.1. Fade behavior of one block.


Time (ms) Action(s) timer = timer 1000 (0 <= 0 continue executing) (block is visible) action == "waitToFadeOut" Block starts to fade out. Block schedules a hide. action = "waitToFadeIn" timer = 10000

1000 2000 3000 4000 5000 6000 7000 8000 9000

- timer = timer 1000 ( 9000 > 0 skip) (block is invisible) - timer = timer 1000 ( 8000 > 0 skip) (block is invisible) - timer = timer 1000 ( 7000 > 0 skip) (block is invisible) - timer = timer 1000 ( 6000 > 0 skip) (block is invisible) - timer = timer 1000 ( 5000 > 0 skip) (block is invisible) - timer = timer 1000 ( 4000 > 0 skip) (block is invisible) - timer = timer 1000 ( 3000 > 0 skip) (block is invisible) - timer = timer 1000 ( 2000 > 0 skip) (block is invisible) - timer = timer 1000 ( 1000 > 0 skip) (block is invisible) timer = timer 1000 (0 <= 0 continue executing) (block is invisible) action == "waitToFadeIn" Block unhides. Block starts to fade in. action = "wait" timer = 1000

10000

11000 ...

- timer = timer 1000 (0 <= 0 continue executing) (block is visible) - action == "wait" - timer = 1000 Sequence repeats

The important thing to note about this behavior is that the fade blocks support up to ten blocks with incrementing (by 1000 milliseconds) fade times to be placed in order. Subsequently, these blocks will fade out in order. Then, one second after the last block fades out, the first block will start to fade back in. Thus, the fade in-and-out is deterministic and cyclic, allowing a player to observe a pattern and to memorize it.

C.18.2. Fire balls.


file There are three blocks of code we are interested in for the fireball blocks. The first of these is in the "/MazeRunner/prototype/server/scripts/MazeRunner/levelloader.cs" At the end of the function BuildLevel(), there is a little snippet of code that checks to see if there are any fireball blocks in the FireBallMarkersGroup SimGroup. If there are, the loader schedules a firePass() in 5000 milliseconds:
if( FireBallMarkersGroup.getCount() ) FireBallMarkersGroup.schedule( 5000 , firePass );

52

C.18.2.1. firePass().
This function has the task of coming back every $stepTime (1000) milliseconds and checking each fireball block to see if that fireball block should fire a new fireball. Again, controlling fireballs this way (as with fade blocks) allows us to use a single schedule event to handle all of our fireball blocks. This is easy to understand, and efficient.
function SimSet::firePass( %theSet ) { %theSet.forEach( doFire , true ); } %theSet.schedule( $fireTime , doFire );

C.18.2.2. doFire().
Again, we have created a function that will operate on individual blocks to enact each block's action if it is time to do so. Here is a summarized listing of the function:
function StaticShape::doFire( %marker ) { if( isObject( %marker.bullet ) ) return; // Handle random fire marker case %firePath = ( %marker.type == 9 ) ? getRandom( 0 , 9 ) : %marker.type ;

switch( %firePath ) { // // NORTH // case 0: %marker.shootFireBall( FireBallProjectile , "0 1 0" , 20 ); // ... similar code for case 1 .. 7 // // DOWN // case 8: %marker.shootFireBall( FireBallProjectile , "0 0 -1" , 20 ); } }

We have not examined the shootFireBall() method, but when this method executes, it will create a projectile and store the ID of that projectile in the block's bullet field. When a projectile strikes an object, the projectile will explode and then self-delete. So, our doFire() method first checks to see if this block has a bullet by seeing if the value in the bullet field is still an object. If it is, then we do not yet need to fire another bullet and the method exits. If there is no current bullet, the method will next check to see if this is a random block. In the case that this block shoots in a random direction, it will get a random value between 0 and 8 and then continue. 53

Having selected a firing direction (or going with the fixed direction) we now enter a long case statement that shoots a new fireball by calling shootFireBall() and passing in the following information (in this order):

Projectile datablock This is the projectile to shoot. Direction This is the direction to shoot in. Velocity This is the velocity we want the fireball to move with.

Please note, we will examine the method shootFireball() in lesson #20.

54

C.19 Lesson #19 FireBall Explosion


In this lesson, we will examine three datablocks that are supplied with the prototype. These datablocks are used to implement the explosion that occurs when a projectile (see lesson #20) explodes. If you look in file "\MazeRunner\prototype\server\scripts\MazeRunner\FireBall.cs", you will find three datablocks:

FireBallExplosionParticle This datablock defines the particles that are used in the explosion. FireBallExplosionEmitter This datablock defines the pattern for the explosion emission. FireBallExplosion This datablock defines the way in which the emitter is played and the effects that the explosion has on the surroundings.

C.19.1. FireBallExplosionParticle.
Let's look at the code for this emitter:
datablock ParticleData(FireBallExplosionParticle : baseSmokePD0 ) { lifetimeMS = 750; lifetimeVarianceMS = 200; colors[0] colors[1] sizes[0] sizes[1] }; = "1 0.2 0.2 1.0"; = "1.0 0.6 0.2 0.0"; = 1.5; = 3.5;

We will first notice that it is inheriting from datablock baseSmokePD0. This is very important for the following reasons: 1. A large variety of effects can be created using a small set of particle textures. 2. The prototype included with this guide comes with a variety of predefined particle datablocks as well as emitters. You should use these as the base (through inheritance or good old cut-copypaste) for your own particle effects and tweak just the parts that you need.

55

3. A large variety of effects can be created using a small set of particle textures. Yes, I just said this, but I want to drive the point home. You don't need to go crazy and create a ton of textures. Instead, tweak the datablock fields, and you will be surprised at the number of effects you can achieve. In this case, we are inheriting a basic smoke particle and then adjusting the fields in Table C.19.1. Table C.19.1. Fields being adjusted.
Fields lifeTimeMS lifeTimeVarianceMS Purpose of Change The base particle has a rather long life, but we want our explosion particles to live for a shorter time.

colors[0] colors[1]

We're trying to get a reddish explosion that fades to a dark orange.

sizes[0] sizes[1]

The particle should start off fairly big and rapidly grow to a little more than double its original size.

C.19.2. FireBallExplosionEmitter.
Next, we must define an emitter. In this case, our emitter is new and does not inherit from a base emitter.
datablock ParticleEmitterData(FireBallExplosionEmitter) { ejectionPeriodMS = 7; periodVarianceMS = 0; ejectionVelocity = 1; velocityVariance = 1; ejectionOffset = 0; thetaMin = 0; thetaMax = 60; phiReferenceVel = 0; phiVariance = 360; overrideAdvances = false; particles = "FireBallExplosionParticle"; };

The above datablock will produce an emitter that will create a large number of particles in a short period. These particles will be ejected at between 1 and 2 meters per second with no offset. The direction of the emitter will vary from straight up to just above horizontal. Additionally, particles will be ejected in a complete circle about the up-vector at the point of explosion. Lastly, this emitter uses the particle we just defined.

56

C.19.3. FireBallExplosion.
This last datablock uses the prior two to define the actual explosion:
datablock ExplosionData(FireBallExplosion) { lifeTimeMS = 2000; particleEmitter = FireBallExplosionEmitter; particleDensity = 50; particleRadius = 0.2; faceViewer = true; // Dynamic light lightStartRadius lightEndRadius lightStartColor lightEndColor = = = = 0; 6; "1 0.2 1"; "1 0.6 0.2";

};

This explosion will live for 2 seconds, emitting particles the entire time. It uses the emitter we just defined and limits the number of simultaneous particles to just 50 at any one time. It varies the point of ejection randomly by up to 0.2 meter about the point of explosion. The particles are made to face the viewer at all times, thus making sure that the clouds of particles are always nice and uniform. The explosion will produce light in a 6 meter radius that starts off reddish and ends a dark orange. Please note, because the blocks are self-illuminating, this effect will not be very visible. You may wish to reexport the blocks without self-illumination enabled to see if the effect is more pleasing this way.

57

C.20 Lesson #20 The FireBall


In this lesson, we will examine three of the six datablocks that are supplied with the prototype. These datablocks are used to implement the projectile representing the fireball. If you look in the file: "\MazeRunner\prototype\server\scripts\MazeRunner\FireBall.cs", you will find three datablocks:

FireBallParticle This datablock defines the particles that are used for the projectile's trail. FireBallEmitter This datablock defines the pattern for the trail. FireBallProjectile This datablock defines the projectile itself and uses the above two datablocks as well as the three we discussed in lesson #19 (FireBallExplosionParticle, FireBallExplosionEmitter, and FireBallExplosion) which are used for the explosion.

C.20.1. FireBallParticle.
Again, we have chosen to implement our particle datablock by using inheritance, but this time many parameters have been modified:
datablock ParticleData(FireBallParticle : baseSmokePD0 ) { dragCoeffiecient = 0.0; gravityCoefficient = 0.0; inheritedVelFactor = 0.0; lifetimeMS lifetimeVarianceMS = 350; = 50;

spinRandomMin = -30.0; spinRandomMax = 30.0; colors[0] colors[1] colors[2] sizes[0] sizes[1] sizes[2] times[0] times[1] times[2] = = = = = = = = = "1 0.7 0.7 1.0"; "1 0.7 0.7 1.0"; "1 0.7 0.7 0"; 0.5; 0.7; 1.0; 0.0; 0.3; 1.0;

};

The particles this produces will not be affected by drag or by gravity, nor will they inherit any velocity from the emitter. This means, they will just hang in the air where they are produced. They have a pretty long lifetime between 300 and 400 milliseconds. As they hang in the air, they will spin back-and-forth between minus 30 and 30 degrees. Lastly, the smoke will start as medium sized off-white poofs and end as large gauzy white poofs.

58

C.20.2. FireBallEmitter.
The emitter datablock is fairly short because it doesn't have a lot to do for smoke trails:
datablock ParticleEmitterData(FireBallEmitter) { ejectionPeriodMS = 20; periodVarianceMS = 5; ejectionVelocity = 0.25; velocityVariance = 0.10; thetaMin thetaMax }; particles = 0.0; = 180.0; = FireBallParticle;

This emitter will produce a new particle every 15 to 25 milliseconds meaning that the trail may be a little spotty (the projectile is moving at 20 meters per second if you will recall from lesson #18). The particles themselves have very little velocity when ejected, and they are all ejected between straight up and straight down (we could make this range smaller to create a more narrow trail). Lastly, the emitter uses the particle datablock we just discussed.

C.20.3. FireBallProjectile.
This datablock brings all of the work in the prior lesson and this one together to create the fireball:
datablock ProjectileData(FireBallProjectile) { projectileShapeName = "~/data/MazeRunner/Shapes/Projectiles/projectile.dts"; explosion particleEmitter armingDelay lifetime fadeDelay }; isBallistic = FireBallExplosion; = FireBallEmitter; = 0; = 5000; = 4800; = false;

This particle uses a mesh that is provided with the prototype. It is nothing more than a very small elongated pyramid with a simple texture applied (Figure C.20.1).

Figure C.20.1. Fireball projectile.

59

It uses the explosion datablock and the (smoke trail) emitter defined above. There is no arming delay, so the projectile will explode as soon as it strikes an object. The projectile will live for 5 seconds and begin to fade at 4.8 seconds. At the end of its lifetime, it will automatically be deleted if it has not already impacted upon something. It is nonballisitic and will travel in a straight line along the path on which it is fired.

C.20.4. ShootFireBall().
We deferred our discussion of the fireball shooting method until this chapter so we would have the proper context. The main thing to understand is that when we create a projectile and put it into the world, it starts with an instantaneous velocity and direction (as specified at creation time):
function StaticShape::shootFireBall( %marker, %projectile , %pointingVector , %velocity) { %bullet = new Projectile() { dataBlock = %projectile; initialVelocity = vectorScale( vectorNormalize(%pointingVector) , %velocity ); initialPosition sourceObject sourceSlot theMarker = %marker.getWorldBoxCenter(); = -1; = -1; = %marker;

};

%marker.bullet = %bullet; MissionCleanup.add(%bullet);

The most important things to see in the above code are: 1. The initial velocity is a combination of a direction and a magnitude. 2. The projectile can have any initialPosition, and we are choosing the centroid of the FireBall block. This is important, because it demonstrates that collision detection only occurs for penetrations of a collision mesh, not for objects or rays leaving the mesh, as is the case with this projectile.

60

C.21 Lesson #21 Game Sounds


In this lesson, we will examine the different methods available to create audioDescription and audioProfile objects. This work will subsequently be used in Section 14.7 Finishing the Prototype to add sound to our game interfaces and game world. For our game, we will need audioDescriptions and audioProfiles to play the following sounds:

Splash Screen Music We'd like to add some music to our splash screen when it is shown. This is a non-networked non-looping 2D sound. Button-Over and Button Press Sounds For Main Menu We want our buttons to provide feedback when the mouse hovers over them and when we click on of them. These are both non-networked non-looping 2D sounds. In-game Music We'd like some background music while playing our game, preferably a ambient loop of some sort. This is a non-networked looping 2D sound. Fireball Firing and Explosion Sound It doesn't make much sense for our fireball blocks to shoot a fireball silently, and the explosion when the fireball collides with something should not be silent either. These are both networked non-looping 3D sounds.

C.21.1. The Audio Descriptions


In order to create audio profiles, we need to create audio descriptions first. Why? Because, the audioProfile object uses the audioDescription object. In our list (above), we have three non-networked non-looping 2D sounds, one non-networked looping 2D sound, and a two networked non-looping 3D sounds. In total, this equates to a requirement for three different audio descriptions.

C.21.1.1. Non-Networked Non-Looping 2D Audio Description


new AudioDescription( MazeRunnerNonLooping2DADObj ) { volume = 1.0; isLooping is3D }; type = false; = false; = $GuiAudioType;

Using the new keyword, we have created an instance of AudioDescription descriptively named MazeRunnerNonLooping2DADObj. Audio profiles using this description have the following attributes:

Is non-networked. It is a normal object, not a datablock. Plays at full volume for the channel the sound is using. Is non-looping. Is non-3D. Is assigned to the $GUIAudioType channel and will thus be attenuated by changes to that channel.

61

C.21.1.2. Non-Networked Looping 2D Audio Description


new AudioDescription( MazeRunnerLooping2DADObj ) { volume = 1.0; isLooping loopCount is3D }; type = true; = -1; = false; = $GuiAudioType;

Using the new keyword, we have created an instance of AudioDescription descriptively named MazeRunnerLooping2DADObj. Audio profiles using this description have the following attributes:

Is non-networked. It is a normal object, not a datablock. Plays at full volume for the channel the sound is using. Is looping. Loops infinitely. (We assigned -1 to loopCount, but we could have left it unspecified as well since the default value is -1.) Is non-3D. Is assigned to the $GUIAudioType channel and will thus be attenuated by changes to that channel.

C.21.1.3. Networked Non-Looping 3D Audio Description


datablock AudioDescription( MazeRunnerNonLooping3DADDB ) { volume = 1.0; isLooping is3D = false; = true;

ReferenceDistance = 2.0; MaxDistance }; type = 20.0; = $SimAudioType;

62

Using the datablock keyword, we have created an instance of AudioDescription descriptively named MazeRunnerNonLooping3DADDB. Audio profiles using this description have the following attributes:

Is networked. It is a datablock. Plays at full volume for the channel the sound is using. Is non-looping. Is 3D. Plays at max volume between 0 and 2 world units and attenuates to nearly zero at a distance of 20 world units, from the source position of the 3D sound. Is assigned to the $SimAudioType channel and will thus be attenuated by changes to that channel.

C.21.2. The Audio Profiles


Now that we have our three audio descriptions, we can create our audio profiles. In this case, we need one each for the sounds, but since several of these sounds, are similar except for the sound file played, we will only examine one from each category.

C.21.2.1. The Non-Looping GUI Sounds (Splash Screen and Buttons)


new AudioProfile(MazeRunnerGGSplashScreen) { filename = "~/data/GPGTBase/sound/gui/GGstartup.ogg"; description = MazeRunnerNonLooping2DADObj; };

Using the new keyword, we have created an instance of AudioProfile descriptively named MazeRunnerGGSplashScreen. This audio profile will be used when the GarageGames Splash Screen is shown and has the following attributes:

It plays the GarageGames startup sound from the demo kit. (This sound file was renamed to GGStartup.ogg from startup.ogg and included with GPGT base data for your use). It uses our Non-Looping 2D Audio Description Object: MazeRunnerNonLooping2DADObj.

C.21.2.2. The Looping GUI Sound (In-game Music)


new AudioProfile(MazeRunnerLevelLoop) { filename = "~/data/GPGTBase/sound/gui/levelLoop.ogg"; description = MazeRunnerLooping2DADObj; };

63

Using the new keyword, we have created an instance of AudioProfile descriptively named MazeRunnerLevelLoop. This audio profile will be used for in-game music and has the following attributes:

It plays a short ambient loop provided on the accompanying disk. It uses our Looping 2D Audio Description Object: MazeRunnerLooping2DADObj.

C.21.2.3. The Networked Sounds (Fireball Firing and Explosion)


datablock AudioProfile(MazeRunnerFireballExplosionSound) { filename = "~/data/GPGTBase/sound/GenericExplosionSound.ogg"; description = MazeRunnerNonLooping3DADDB; };

Using the datablock keyword, we have created an instance of AudioProfile descriptively named MazeRunnerFireballExplosionSound. This audio profile will be used for the sound effect attached to a fireball explosion and has the following attributes:

It plays a generic explosion sound that is included on the accompanying disk for your use. This sound is derived from the Crossbow_explosion.ogg found in the TGE Demo. It uses our Non-Looping 3D Audio Description Datablock: MazeRunnerNonLooping3DADDB.

C.21.3. Using The Audio Profiles


All of the above audio descriptions and audio profiles are provided on the accompanying disk. We will be using them later when we follow the instructions in Section 14.7 Finishing the Prototype. However, the question of use should at least be addressed. How does one use these new sounds? The sounds we created are used in three ways: 1. Attached to a GUI Control The button over and press sounds above will be used by a GUI button control. As you will discover while reading Chapter 12, this attachment is achieved using GUI profiles. 2. Attached to a special effect Our explosion sound is used by the explosion object. As we saw in Section 11.3 Explosions, we can assign an AudioProfile datablock to the ExplosionData soundProfile field. When an explosion is created with this datablock it will automatically play the sound specified by our AudioProfile datablock.

datablock ExplosionData( FireballExplosion ) { // ... soundProfile = MazeRunnerFireballExplosionSound; }; // ...

64

3. Played Manually Lastly, we can play sounds manually. We simple call alxPlay() and pass it the name or ID of a non-networked 2D sound AudioProfile.

// Play the GG Splash Screen Sound alxPlay( MazeRunnerGGSplashScreen );

65

C.22 Finishing the Prototype


Thus far, you have probably been working your way through the guide, learning about various features of the Torque Game Engine. Along the way, we have stopped to do little lessons that created one or more game elements to be used in the game. At this point we don't really have a playable game. We have just a short distance to go before our game reaches the playable prototype stage. To get our game ready for play testing we must do the following two things: 1. Finish gameplay code. At this point, we can start the Maze Runner mission and then manually load a level, but our player doesn't get moved to the right spot on the level and there is pretty much no interaction. We need to change this. Specifically, we need to make the levels load automatically, have the player die when struck by a fire ball or after falling into the lava, load the next level when all the coins are collected, and aware our player with a new life on a successful level completion. 2. Improve feedback. With the final mechanics in place, we need to provide just a little bit more feedback to the player. Specifically, we need to update the playGUI to show how many lives we have, how many coins we've collected (score), and how many coins are left for a level. Also, while we are about this, we will add sounds for the fire ball firing and explosions, and then add some GUI sounds and music to make if feel like a whole package.

C.23 Finish Gameplay Code


By this point you should be feeling pretty comfortable with TorqueScript and with navigating the prototype directory structure. So, the kid gloves are coming off. In the next few pages, we will run through some terse discussions. We will examine newly added scripts and modifications to scripts we discussed in prior lessons.

C.23.1. Copy Required Files


Before we continue, please: 1. Copy "\MazeRunner\MazeRunner_Post_Finishing_the_Prototype\prototype2" into "\MazeRunner\". 2. Copy "\MazeRunner\MazeRunner_Post_Finishing_the_Prototype\main.cs" into "\MazeRunner\". The new main.cs file points to the newly added prototype2 mod director. prototype2 contains all of the change we are about to discuss and is ready to play if would like to try it before continuing.

C.23.2. Breaking the Law


The first thing we will do is break the law. OK, we're not breaking the law, but we are doing something that I warned you NOT to do earlier. Namely, we are going to make a global variable for tracking the ID of the player. Then, we are going to use it to implement gameplay scripts, and later to keep our interfaces up to date. We are, in effect, ignoring the client-server divide. This is both good and bad. It is good because it makes writing the scripts for our single-player game simple. It is bad because it ties us to a singleplayer game ONLY. If later we decide to make this game support multiple players we will experience at least some pain while we modify our scripts to handle this new mode.

66

So, why are we doing this? Well, first I know that in this book we will only ever play this game in singleplayer mode. Second, the game is simple enough that later, if you do convert this to multiplayer, the pain won't be too bad and it will serve as an excellent object lesson in making good decisions. Excuses and reason aside, we must implement this change. To do so, I have modified the method GameConnection::createPlayer() in game.cs to look like this (BOLD lines are newcode):
function GameConnection::createPlayer(%this, %spawnPoint) { // Create the player object %player = new Player() { dataBlock = MazeRunner; // Change this line client = %this; }; MissionCleanup.add(%player); $Game::Player = %player; //MazeRunner

Now, whenever we want the player's ID, we can just reference the '$Game::Player' global.

C.23.3. Automatic Startup


To this point, we have been manually loading missions by typing "buildLevel(0);". That is just fine for testing purposes, but we really need the game to load when the mission is loading.

C.23.3.1. Experiments in loading.


If we examine the "game.cs" file closely, we will see that it has a variety of functions and methods. Among these are some promising-sounding places to put a script for automatically loading our first level:

onMissionLoaded() - Hmmm... this sounds good. The mission is loaded, so we should be good to go. startGame() - This sounds good, too. I mean we do want to start the game, right? GameConnection::createPlayer() - OK, maybe you wouldn't think of this one. This is a hint, actually.

Great, we have some possible places to do the level loading, but what are the steps we need to follow in order to load our level? Can we simply put a buildLevel() call in one of these? Why don't we try it? Add this code to the end of onMissionLoaded() (BOLD lines are new code):
startGame(); } buildLevel(0);

After restarting the game and reloading the mission, this may work, or it may work partially, or the game may hang. It depends.

67

At this point in the game startup process, there is some ambiguity in timing due to latencies that can vary from run to run. This means that any of the following actions can occur: 1. The game starts correctly and the player is on the correct spawn point. This is what we want. Unfortunately, this doesn't always happen. 2. The level loads and the player gets dropped on the safe spawn point, end of story. Now we're stuck. 3. If timing conspires against you, all the resources that need to have been loaded won't be ready and the loading code will just hang. This is the worst possibility. So, what is happening here? Well, the mission was loaded, but the player had not been created yet, so our scripts for moving the player can't work. They have no object to move. (If you're curious, you can see the player moving script by looking at the playerDrop() function in levelloader.cs.) Since putting buildLevel() after startGame() didn't work, that pretty much rules out our placing the function call in startGame() too. What about GameConnection::createPlayer() then? Let's try that next:
%this.player = %player; %this.setControlObject(%player); } BuildLevel(0); //MazeRunner

Perfect! This is guaranteed to work properly every time. The mission is always loaded prior to the player being created, so the scripts have valid object IDs to work with.

C.23.4. Dying
Another problem with our prior revision of this game was that we didn't get killed by the lava or fireballs. Let's remedy that now.

C.23.4.1. KillZone.
To be killed by the lava, we need some way to know we're in it. Now, we could make our water block into a lava block by changing the water type. However, as part of our game design, we chose to make the player invincible, so this won't really help. I mean, we could in theory make our player have a very low damage level, make it damageable, and then maybe, just maybe falling in the lava would kill him. The thing is we don't really want the player to die. We just want to lose a life and move to the spawn point. When a player dies (getState() returns "dead"), the player will no longer move or take move commands until it is replaced with a new instance. This is by design and is not what we want in this instance.

68

So, long story short, we get creative. Let's create a really big trigger (named KillZone) and place it in the lava. Then, we can just write an onEnterTrigger() callback that will take away a life and move us to the spawn point. Perfect!
datablock TriggerData(KillZoneTrigger) { tickPeriodMS = 100; }; function KillZoneTrigger::onEnterTrigger(%DB , %Trigger , %Obj) { %Obj.loseALife(); }

The above code defines the datablock for this trigger, and the callback calls the method loseALife() (described below) on the object entering the trigger, but what about placement? This code will do the placement:
function buildKillZone() { new Trigger(KillZone) { position = "-256 256 40"; rotation = "1 0 0 0"; scale = "512 512 25"; dataBlock = "KillZoneTrigger"; polyhedron = "0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000"; }; } MissionGroup.add( KillZone );

Then we can add a call to this code in onMissionLoaded() to do the creation (BOLD lines are new code):
function onMissionLoaded() { buildKillZone(); // MazeRunner } startGame();

So, what about that loseALife() thing?

C.23.4.2. Player::loseALife().
The easiest way to handle removing lives is to make a method scoped to the Player class (so it can be called on the Player object) that handles all of the bookkeeping. This simplifies things greatly. Yes, right now only two things can kill the player, but later you might add more, and having killing code all over the place would be very bad.

69

Here is the code (located in mazerunnerplayer.cs"):


function Player::loseALife( %player ) { // 1 %player.lives--; // 2 if( %player.lives <= 0 ) { schedule( 0 , 0 , endGame ); return; } // 3 %player.setVelocity("0 0 0"); %player.setTransform(%player.spawnPointTransform);

This code does the following: 1. It decrements the player's life counter. (Yes, we haven't talked about this yet. It's coming up soon.) 2. It checks to see if all of our lives are gone and then schedules a call to endGame() (in game.cs) to unload the mission, destroy the player, disconnect the client from the server, and get us back into the main menu. You may wonder why we don't call endGame() directly. 3. If the game is not over, the player is moved back to its last spawn point. This information is stored in the player by playerDrop() in levelloader.cs":
$Game::Player.spawnPointTransform = (%actX SPC %actY SPC $CurrentElevation);

C.23.4.3. Initial lives.


In order to take away lives, we must have lives to take. The best place to add initial lives to the player is either in its onAdd() method, or at the location where we create it. I chose the onAdd() method (in "mazerunnerplayer.cs"; BOLD lines are new code):
function MazeRunner::onAdd( %DB , %Obj ) { Parent::onAdd( %DB , %Obj ); %Obj.lives = 3; }

70

C.23.4.4. Fireballs.
OK, we got a little off topic there, but we're back now. The next question is; how do fireballs kill? The projectile object has an onCollision() callback that is called for collisions with any world object. So, if we write a version of this callback in the namespace of our projectile, we can have that callback check to see if the player was hit and call loseALife():
function FireBallProjectile::onCollision( %projectileDB , %projectileObj , %collidedObj , %fade , %vec , %speed ) { if (%collidedObj.getClassName() $= "Player") { %collidedObj.loseALife(); } }

In the above callback (located in fireballs.cs"), the engine is asked to get the class name for the collided-with object. It then compares this to "Player". If the comparison comes back true, loseALife() is called on the collided-with object.

Alternate solution #1.


There is an alternate way to write this code that would actually work in more cases (i.e., for Player and aiPlayer):
// Alternate implementation function FireBallProjectile::onCollision( %projectileDB , %projectileObj , %collidedObj , %fade , %vec , %speed ) { if (%collidedObj.getType() $= $TypeMasks::PlayerObjectType ) { %collidedObj.loseALife(); } }

This alternate implementation uses the getType() method to get a bitmask for the collided-with object. The bitmask contains bit settings for all classes from which the object is derived as well as for the class itself. So, as I alluded to, if the collision occurred against an aiPlayer (which is derived from Player), this comparison would still work, whereas the prior code would not. In this game, we don't have that worry, so let's leave it as is.

71

Alternate solution #2.


Originally, as I wrote this code for the book, I was using a bleeding edge version of the engine (1.4 before release) and I ran into a bug (that has since been fixed) where %collidedObj was always getting "1". For a moment, I thought I was stuck. Then, it occurred to me that there are other ways to solve the identification problem, and I wrote this code:
%Offset = vectorSub( %vec , $Game::Player.getWorldBoxCenter() ); %Len = vectorLen( %offset ); if( %len < 1.7 ) { $Game::Player.loseALife(); }

This code used the position of the projectile's collision and then compared it to the position of the player's centroid. If the distance between them was small (1.7 meters or less), in all likelihood the object that had been hit was the player and I called loseALife(). This solved my temporary problem, and on the occasional time when the player wasn't hit but was just close to the collision point, the difference was not noticeable. The lesson here is that TGE is very flexible and you can often solve the same problem in many ways. So, don't let one dead end stop you.

C.23.4.5. Out of lives.


At some time, after all this losing of lives, the player will be out of lives. According to our initial rules list, this means the game is up, time to go home. As we have already seen (above) the loseALife() method handles this case and ends the game for us.

C.23.5. Moving On
The last thing we need to fix with regards to gameplay is moving on to the next level and getting our extra life.

C.23.5.1. Last coin.


The rules stated that when the last coin is picked up, the current level should be unloaded and the next level should be loaded. So, how do we do this? If you recall, the inventory system has a callback called onPickup(). When we discussed this callback, I said that you might want to override it to implement special behaviors. This is one of those times.

72

If you will look in "coins.cs", you will find this implementation of onPickup():
function Coin::onPickup( %pickupDB , %pickupObj , %ownerObj ) { // 1 %status = Parent::onPickup( %pickupDB , %pickupObj , %ownerObj ); // 2 if (CoinsGroup.getCount() == 0 ) { buildLevel($Game::NextLevelMap); } $Game::Player.lives++;

// 3 return %status;

This callback does the following: 1. It takes advantage of the previously written pickup code by calling the Parent:: version. 2. It then checks to see if the SimGroup CoinsGroup is empty. In the case that it is empty, buildLevel() is called with the stored numeric ID of the next level, and a new life is added to our player. 3. Last, but not least, it returns the return status from the Parent call. This is important because the method/callback that called onPickup() in the first place might care if the pickup was successful or not.

C.23.6. Gameplay Scripting Completed


We are officially done with the gameplay scripting now. The game is now in a playable state, and we could define some levels and ship it off to our testers at this point. If this were a business venture, that would be the plan, but since we're learning about Torque and not running a gaming business, let's continue.

73

C.24 Improve Feedback


To make the game easier to play, we should provide some information to the player so s/he knows how many lives are remaining, what the score is, and how many coins are left on a level. Also, adding sounds to the fireballs will make them a little easier to detect. Lastly, if we add some sounds and music we will have a nicely rounded prototype.

C.24.1. Copy Required Files


Before we continue, please: 1. Copy "\MazeRunner\MazeRunner_Post_Improve_Feedback\prototype3" into "\MazeRunner\". 2. Copy "\MazeRunner\MazeRunner_Post_Improve_Feedback\main.cs" into "\MazeRunner\". The new main.cs file points to the newly added prototype3 mod director. prototype3 contains all of the change we are about to discuss and is ready to play if would like to try it before continuing.

C.24.2. New playGUI HUDs


If you start the game and run the Maze Runner mission you will see that the new and improved playGUI has the three HUDS at the top of the screen (Figure C.24.1.) Figure C.24.1 New HUDs.

The three HUDs are:


Lives Counter (Upper-Left) Shows number of lives the player has left. Score (Upper-Middle) Shows number of coins thus far recovered. Remaining coins for level (Upper-Right) Shows coins left till end of level.

74

These HUDS should look quite familiar. They are the same counters we discussed in Chapter 13, being put to good use in our prototype game. To make your life easier I have created a completely new playGUI containing these HUDS and placed it and all the scripts and content associated with it in ~/client/ui/PlayGUIs/. To get this new playGUI interface loaded instead of the old one, I changed the initClient() function in ~/client/init.cs as follows:
function initClient() { // ... //exec("./ui/PlayGui.gui"); // Prior to Maze Runner exec("./ui/PlayGUIs/PlayGui.cs"); // MazeRunner (Load My GUI) // ... //exec("./scripts/playGui.cs"); // Prior to Maze Runner } // ...

This change simply tells the function NOT to load the old PlayGUI.gui and PlayGUI.cs and to load my PlayGUIs/PlayGui.cs instead. This new script will automatically load the remainder of the scripts required to build the new playGUI. Now, let's talk about how these HUDs are hooked up.

C.24.2.1. Hooking up the lives HUD.


The lives counter is initialized in MazeRunner::onAdd(), from mazerunnerplayer.cs (BOLD lines are new code):
function MazeRunner::onAdd( %DB , %Obj ) { Parent::onAdd( %DB , %Obj ); %Obj.lives = 3; livescounter.setCounterValue(%Obj.lives); }

It is decremented in Player::loseALife() , from mazerunnerplayer.cs (BOLD lines are new code):


function Player::loseALife( %player ) { // 1 %player.lives--; livescounter.setCounterValue(%player.lives); } // ...

75

It is incremented in Coin::onPickup(), from coins.cs (BOLD lines are new code):


function Coin::onPickup( %pickupDB , %pickupObj , %ownerObj ) { // ... if (CoinsGroup.getCount() == 0 ) { // ... livescounter.setCounterValue($Game::Player.lives); } } // ...

C.24.2.2. Hooking up the score HUD.


The score counter is initialized in GameConnection::createPlayer(), from ~\server\scripts\game.cs (BOLD lines are new code):
function GameConnection::createPlayer(%this, %spawnPoint) { // ... BuildLevel(0); scorecounter.setCounterValue(0); }

It is incremented in Coin::onPickup(), from coins.cs (BOLD lines are newcode):


function Coin::onPickup( %pickupDB , %pickupObj , %ownerObj ) { // ... scorecounter.setCounterValue( scorecounter.getCounterValue() + 1 ); } // ...

C.24.2.3. Hooking up the remaining coins HUD.


The coins counter is initialized at the very end of buildLevel(), from levelloader.cs (BOLD lines are new code):
function BuildLevel( %levelNum ) { // ... } coincounter.setCounterValue( CoinsGroup.getCount() );

76

It is decrement in Coin::onPickup(), from coins.cs (BOLD lines are new code):


function Coin::onPickup( %pickupDB , %pickupObj , %ownerObj ) { // ... coincounter.setCounterValue( CoinsGroup.getCount() ); } // ...

C.24.3. Adding Sounds


To give the game a little more pizazz and to make it feel more finished, we need to add a few sounds. As you will recall, in Chapter 11, we made several audio descriptions and audio profiles. I have included all of these and a few others in to separate places. The 2D sound descriptions and profiles have been added to a new file named: ~\client\scripts\MazeRunnerGUISounds.cs. This includes:

MazeRunnerNonLooping2DADObj A non-looping 2D audioDescription object for use with audioProfile objects. MazeRunnerLooping2DADObj A looping 2D audioDescription object for use with audioProfile objects. MazeRunnerGGSplashScreen An audioProfile object to play music when the Garage Games splash screen is displayed. MazeRunnerButtonOver and MazeRunnerButtonPress Two audioProfile objects used to play button over and press sounds. MazeRunnerLevelLoop An audioProfile object used to play an ambient loop during game play.

This file is loaded by ~/client/init.cs using this code:


/// Load client-side Audio Profiles/Descriptions exec("./scripts/audioProfiles.cs"); exec("./scripts/MazeRunnerGUISounds.cs"); // Maze Runner

The 3D sound descriptions and profiles have been added to the existing fireballs.cs file at the top and include:

MazeRunnerNonLooping3DADDB A non-looping 3D audioProfile datablock for use with audioProfile datablocks. MazeRunnerFireballExplosionSound An audioProfile datablock that is played for each fireball when it is shot. MazeRunnerFireballExplosionSound An audioProfile datablock that is used by the FireBallExplosion datablock to play and explosion sound.

These sounds will now be loaded when fireballs.cs is executed.

77

Now, let's briefly discuss how each of our new sounds is used.

C.24.3.1. Adding Sound To Splash Screen


The simplest way to add a sound to the Garage Games splash screen is to play the sound when the splash screen is displayed. If we look in the file ~/client/ui/StartupGui.gui, we will find a method named loadStartup(). This method is used to display the splash screen. To have the game play a sound when the splash screen is displayed, I made these changes:
function loadStartup() { // ... //alxPlay(AudioStartup); //Before Maze Runner } alxPlay(MazeRunnerGGSplashScreen); //Maze Runner

C.24.3.2. Adding Sound To Buttons


To have the menu buttons play a sound when the mouse passes over a button and when a button is clicked, I needed to define a new GuiControlProfile object and fill in the proper fields:
if(!isObject(MainMenuButtonProfile)) new GuiControlProfile (MainMenuButtonProfile) { // ... soundButtonOver = "MazeRunnerButtonOver"; }; soundButtonDown = "MazeRunnerButtonPress";

I then made sure that each button in the main menu (~/client/ui/mainMenuGui.gui) used this new profile.
// ... new GuiButtonCtrl() { profile = "MainMenuButtonProfile"; // ...

78

C.24.3.3. Adding Ambient Loop To Game


To add the ambient loop to our game I simply added an alxPlay() statement to the onWake() callback and a reciprocal alxStop() statement to the onSleep() statement for the new playGUI. Both of these callbacks are located in ~/client/ui/playGUIs/playGUI.cs and now look like this:
function PlayGui::onWake( %this ) { $enableDirectInput = "1"; activateDirectInput(); // Activate the game's action map moveMap.push(); } %this.levelLoop = alxPlay(MazeRunnerLevelLoop); // Maze Runner

and this:
function PlayGui::onSleep( %this ) { // Pop the keymap moveMap.pop(); if(isObject ( %this.levelLoop ) ) alxStop(%this.levelLoop); // Maze Runner

Notice, that I simply stored the handle returned from alxPlay into a aptly named dynamic field levelLoop created on the fly in the playGUI control object. Later I checked to see if the handle represented a valid handle and stopped playing the sound associated with it using alxStop().

C.24.3.4. Playing Sounds When Fireballs Are Fired


To play the firing sound, we will again use the playAudio() ShapeBase method. Although we don't care in this singleplay game, by doing this, we insure that every client will hear the sound with no extra effort on our part. To do this, I modified the StaticShape::shootFireBall() console method to include this code:
function StaticShape::shootFireBall( %marker, %projectile , %pointingVector , %velocity) { // ... %marker.playAudio( 0 , MazeRunnerFireballFiringSound ); }

If you will recall, all fireballs are fired from the center position of a fireball block's world box. Thus, we can approximate the correct location for the firing sound by simply playing the firing sound using the block that marks the origin of the shot itself. In this case I merely called playAudio() and played the MazeRunnerFireballFiringSound audioProfile datablock in sound slot 0. 79

C.24.3.5. Adding Explosion Sounds To An Explosion Datablock


The last sound that was added is the explosion sound. This was accomplished by assigning the new MazeRunnerFireballExplosionSound audioProfile datablock do the existing FireBallExplosion datablocks soundProfile field.
datablock ExplosionData(FireBallExplosion) { // ... soundProfile = "MazeRunnerFireballExplosionSound"; // ... };

That's it, we now have a working prototype that we can distribute for testing. what's next?

80

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Appendix D. GPGT Lists

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Table of Contents
D.1 TGE Must Know Facts....................................................4 D.1.1. Texture Support....................................................................4 D.1.2. The TGE World......................................................................6 D.2 Suggested Reading.......................................................8 D.2.1. Graphics............................................................................... 8 D.2.2. Games.................................................................................. 9 D.2.3. User Interfaces..................................................................... 9 D.2.4. Programming......................................................................10 D.2.5. General Design...................................................................10 D.3 Favorite Resources..................................................... 11 D.3.1. Game Development Sites...................................................11 D.3.2. Engine Improvements and Scripting Resources................. 13 D.4 Tool Lists................................................................... 16 D.4.1. Interior Creation Tools........................................................ 16 D.4.2. Shape Creation Tools..........................................................18 D.4.3. Texture Creation, Generation, and Editing Tools ...............20 D.4.4. Texture Filters and Filtering Tools...................................... 20 D.4.5. Sound Recording and Editing Tools.................................... 21 D.4.6. Video Capture & Editing Tools............................................ 21 D.5 Glossary Of Terms...................................................... 22

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

his appendix is a summary listing of the extremely common factoids which all TGE users should be aware of. It lists such things as supported graphics formats, supported shape and interior formats, general facts about the TGE simulated world, metrics, orientation concepts, ...

D.1 TGE Must Know Facts

D.1.1. Texture Support


D.1.1.1. Formats

Texture Extension BMP BM8 GIF JPG

PNG

Features - Uncompressed - Supports 8-bits per channel - Supports alpha channel - Custom 8-bit color quantized texture format - Low quality (only supports 256 colors) - Lossy (variable) Compression - Supports 8-bits per channel - Does not support alpha channel - Lossless Compression - Supports 8-bits per channel - Supports alpha channel

Uses All Unknown GUIs All

All

It is important to remember that regardless of whether you use a JPG or a PNG, both formats at the same resolution will consume the same memory when loaded to the video card, but the PNG will have a much higher visual quality. The only savings involved in using the lossy compressed formats is DISK space. That is, the total size of your game, including images will be smaller on disk if you use JPG. This is, of course, good for downloaded games.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.1.1.2. Rules/Limitations

There are rules governing the dimensions of TGE supported textures and various limits apply. Dimension Rules

Textures dimensions must be a multiple of two. i.e. 2, 4, 8, ..., 256, etc. Texture dimension ratio ( width vs height ) must be no greater than 16:1. For example, if a texture is 16 pixels wide, it can be a maximum of 256 pixels high.

Limits

The minimum texture resolution is 1 x 1 pixels. The maximum texture resolution is 512 x 512 pixels (an 8MB texture) There is no limit on the number of textures that can be used. However, one should consider minimum target platform specs.
D.1.1.3. Precedence Same Prefixed Files in Same Directory

TGE allows multiple textures with the same prefix and different suffix to exist in the same directory. Furthermore, it will put each texture found in it's list of named textures. An issue arises when two textures have the same prefix, in there is ambivalence regarding which texture will be used when only a prefix is specified to name it. Fortunately, the problem isn't that hard to resolve. TGE stores the textures int the following order:

JPG PNG GIF BMP

Thus if we have a texture: 'edo.jpg' and 'edo.png', if we only specify the prefix 'edo', or if the tools do so, then TGE will use the JPG version. The moral of this story is to check your directories and only keep the version you want! 5

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.1.1.4. Precedence - Same Named Files in Different Directory

another question arises, which is, "What if I have two textures with the same name in different directories. What happens in this case?" This is really not a big problem, except in wasted space for your game. TGE will keep unique handles for each texture it finds and all the tools and TGE itself rely on hierarchy information embedded in the models other entities to choose the correct texture.

D.1.2. The TGE World


D.1.2.1. Coordinate System

In TGE, the standard treatment for each of the three axes is: Axis +X Description Left / West

+Y

Forward / North

+Z

Up

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.1.2.2. Units of Measure

Although it is perfectly reasonable to treat values as meters, I have chosen to use a more generic term, world unit. In general practice, you may treat these terms the same. Thus we have the following: Measurement Notes Standard terrain is 2048 x 2048 meters square. Terrain heightmaps (usually BMP) are 256 by 256 Terrain pixels which means that each pixel is eight meters from the next pixel in its respective row or column. Heightmap Note also, the terrain points (as rendered in TGE) are by default 8 meters apart. The standard dimension for a texture used to paint the terrain is 256 by 256 pixels. This texture will be applied at a ratio of 32 pixels per linear-meter. Thus if you need more detail, you'll have to go up to the maximum texture size of 512 by 512 pixels. This will Terrain Textures cover the same area but give a coverage of 64 pixels per linear-meter. Suggestion: Be sure you are using PNG files before trying a higher resolution. Using JPG files may result in a poor looking terrain. Also be sure to use the detailTexture. There is a special texture which can be specified on a Terrain per terrain basis. It's purpose is to add local texture detailTexture quality by blending the specified texture with the underlying texture. This texture is rendered at a ratio of 256 pixels per linear-meter. Terrain rendering and texturing is controlled by the Terrain Scale terrain.scaleFactor field. By default it's value 8, but Factor in can be adjusted to increase the size of the terrain. Increasing this reduces the pixels per linear-meter ratios listed above. Water Water Textures follow the same rules as the Terrain and are also tied to scaleFactor. Shapes Texture scaling is controlled by the tools in use. Default ratios vary. Interiors Interiors generated with Hammer, by default (pre(Hammer) scaling), apply textures at a ratio of 32 pixels per linear-meter. 7 Entities Terrain

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.2 Suggested Reading


D.2.1. Graphics
D.2.1.1. Animation

Advanced Animation and Rendering Techniques (Theory and Practice) Alan Watt / Mark Watt ISBN: 0-201-54412-1
D.2.1.2. DirectX

Direct3D ShaderX - Vertex and Pixel Shader Tips and Tricks Wolfgang F. Engel ISBN: 1-55622-041-3
D.2.1.3. General

Computer Graphics: Principles and Practice in C (2nd Edition) Foley / van Dam / Feiner / Hughes ISBN: 0-201-84840-6
D.2.1.4. Mathematics

Mathematics for 3D Game Programming & Computer Graphics Eric Lengyel ISBN: 1-58450-037-9
D.2.1.5. OpenGL

OpenGL Programming Guide, Third Edition OpenGL ARB ISBN: 0201-60458-2 OpenGL Reference Manual, Fourth Edition OpenGL ARB ISBN: 0-321-173-83X

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.2.1.6. Rendering

Real-Time Rendering (Second Edition) Tomas Akenine-Mller, Eric Haines ISBN: 156881-182-9

D.2.2. Games
D.2.2.1. General

Games Programming Gems Series 1 thru 6. Various Authors Game Programming Tricks of the Trade Andrew LaMothe ISBN: 1-931841-69-1 Tricks Of The Windows Game Programming Gurus (2nd Edition) Andre LaMothe ISBN: 0-672-323-699
D.2.2.1. Architecture

Game Architecture and Design Andrew Rollings / Dave Morris ISBN: 1-57610-425-7

D.2.3. User Interfaces


User Interface Design for Programmers Joel Spolsky ISBN: 1-893-115-941

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.2.4. Programming
Debugging Applications John Robbins ISBN: 0-735-608-865 Code Complete Steve McConnell ISBN: 1-55615-484-4 Large-Scale C++ Software Design John Lakos ISBN: 0-201-63362-0 Software Project Survival Guide Steve McConnell ISBN: 1-57231-621-7

D.2.5. General Design


The Design of Everyday Things Donald A. Norman Search at Amazon ISBN: 0-46506-710-7

10

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.3 Favorite Resources


D.3.1. Game Development Sites
GarageGames, Inc.

http://www.garagegames.com http://tdn.garagegames.com/ GarageGames is a unique Internet publishing label for independent games and game makers. They are a band of professional game makers committed to publishing truly original and exciting titles on their own terms.

Torque Community Engines: TGE, TSE, T2D Assets and Resources Torque Developers Network. Much more ...

Hall Of Worlds, LLC.


http://gamers.hallofworlds.com An independent game studio and developer of educational products. The Hall Of Worlds For Gamers site is dedicated to games and the individuals that develop them.

Articles Books Newsletter Assets and Resources Research Center

Gamasutra

http://www.gamasutra.com Gamasutra.com delivers an international audience of over 290,000 registered members with purchasing power. Over 150,000 of the members are from outside the USA. Part of the 'CMP Game Group' http://www.cmpgame.com/.

Daily news. Art articles. Technical articles. Postmortems. Job listings. Developers Directories.

11

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GameDev.net

http://www.gamedev.net A leading online community for game developers of all levels, from the green beginner to the seasoned industry veteran. Host to Neon Helium: http://nehe.gamedev.net/.

Daily news. Art articles. Technical articles. Forums. Job listings. Developer Sites. Image Of The Day.

Flipcode

http://www.flipcode.com/ After more than six and a half years of game development goodness, flipCode closed its doors. Although they no longer accept submissions of articles, images, news, etc., their article archives will remain online as reference material for the foreseeable future.

Articles. Tutorials. Tips and Tricks. Code Of The Day. Q&A Answer Column. Knowledge Base.

12

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.3.2. Engine Improvements and Scripting Resources


To find these resources, simply type all or part of the listed title into the GarageGames search engine. Title 8 terrain textures instead of 6 A better vehicle/simulations control thru the keyboard Add Gamepad support to Torque Adding multiple modes to your weapons Adding Reactive Mission Regions Adding Relative Pathing Adding Watercraft to TGE Advanced Camera Advanced Options Dialog AdvancedCam Orbitmode Camera/Player Independant Rotation AI Senses Air Control Animated Menus using guiAnimatedBitmapCtrl Artificial Horizon GUI Control Basic TerrainDeformer Object for Torque Charge Jump Click n Pick Client-side radar Colour Filter Effect (Night Vision / Colourization) Conform Player to Terrain Constraining camera to terrain / interiors Continuous Laser Cryptainer Different Explosion when projectile hits a player object. Distance based Particle Reduction DragDropGui Control Dynamic and Individualized Gravity Dynamic MemStream Enabling doppler effects in Torque Flamethrower , bullets that dont disappear on impact with creatures Flexible rain/snow texture in Torque 13

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Title FMod for torque Fractal Cloud Generator fxDecal fxGrassReplicator v. 1.5 fxGuiSnooper fxStarfieldReplicator Object fxSwarm Guided or Seeker Projectiles. guiObjectView - Mountable Animated DTS Viewer GuiObjectView Update guiTeleportCtrl Hand to Hand Combat, Locational Damage and Collision and More. Hiding nodes in a model How to get damage emitters in a Player Image State Manual Transition Improved Particle Emitters In-game Music Increase Terrain Texture Support Interior (.dif) Transparency Jet pack love... Map HUD Map Triggers that send you to another server Mission Area Bounds Mission Area Forcefield V2 MountImage with mount point. Mouse controlled player movement Moveable, Destroyable, Mission Area Aware ITEMS Movie like camera system Multiple Viewports MySQL for TGE Object moving along path Object moving along path PathShape Persistant Character Server 14

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Title Projectile ballistic coefficients (drag factors) Projectiles affected by wind Protecting Art Assets With Encryption Quick fix to add flight stick for Torque Racing Wheel, Flight Stick and Gamepad Support for Torque (Windows) Realistic shotgun script with multiple projectiles and impulse blast Render To Texture Rigid Shape Class RPGDialog Saving Inventory Between Missions Scrolling Map, Radar, Compass gui Control Server Side Melee System Single Player Level exits Skinnable GUI Controls in TGE Slightly advanced bot scripting SQLite Integration for Torque Subscription Based Message Router TGE dynamic lights on interiors fix TgeLobby v1.2 Turret & AITurret classes, Version 1.20 twSurfaceReference Object Using a Separate Camera Object

15

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.4 Tool Lists


D.4.1. Interior Creation Tools

16

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

*: This is a plugin and requires the purchase/ download of another application. ***: WorldCraft used to be available for Torque owners, but since the release of Hammer, the license authorizes it for use on Half-life based titles only. Thus, it is not legal to use for any commercial Torque project. 1. Runs on Mac OS X under X11 emulation. 2. Includes tutorials on modeling convex polygons for interiors, and a library of convex shapes. 3. Omni light only. Original matrix can be found at: http://www.alexswanson.com/torque/dif/

17

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.4.2. Shape Creation Tools

18

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Requires Max 4 Multires Plugin in Max 5. 1. - Note Removed 2. Exporter GUI can auto-generate detail markers. 3. DSQ Exporter auto-generates .cs files for the shape. 4. Supports only 2 bones per vertex. 5. Supported via Armatures. 6. Supported, but requires implementation found at http://www.vtk.org 7. This has not yet been compiled and tested, but should work. 8. This works, but the IFL animation does not show up in the application. 9. Only supports skinning to armatures, not meshes linked directly to individual bones. 10.Mesh visibility data is not saved on application close, must be set up each time before export. *. This exporter's development is in-progress with the support of GarageGames. Original matrix can be found at: http://www.alexswanson.com/torque/dts/ 19

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.4.3. Texture Creation, Generation, and Editing Tools


Tool Name Adobe Photoshop Web-Site http://www.adobe.com/ Cost $649

http://www.corel.com Corel Paint Shop Pro

$99

GIMP

http://www.gimp.org/

Free

Spiral Graphics Genetica 2 http://www.spiralgraphics.biz/

$129

Texture Maker http://texturemaker.thegamecreators.com/

$70

D.4.4. Texture Filters and Filtering Tools


Tool Name Alien Skin Web-Site http://www.alienskin.com/ Cost Varies

Flaming Pear

http://www.furbo-filters.com/

Varies

Plugin Commander

http://thepluginsite.com/products/picopro/

$50

Redfield Plugins http://www.redfieldplugins.com/

Varies

20

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.4.5. Sound Recording and Editing Tools


Tool Name Audacity Web-Site http://audacity.sourceforge.net/ Cost Free

Sound Forge

http://www.sonymediasoftware.com

$299

D.4.6. Video Capture & Editing Tools


Tool Name FRAPS Web-Site http://www.sonymediasoftware.com Cost $37

VirtualDub

http://www.virtualdub.org/

Freeware

21

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

D.5 Glossary Of Terms


Meaning The players view is such that the player 1st-Person (1st POV) sees through the eyes of his or her avatar. (The avatar is not normally seen, (3D Perspective) although appendages and currently held items may be visible.) A sound that has no apparent origin, and 2D Sound when played, will play equally loud from the left and the right speaker (assuming you have only two). A sound with an origin associated with it which is then transformed and attenuated 3D Sound based on the listener's location relative to the sound's source. That is, 3D sound may play more loudly from one speaker than the other(s) The players view is that of a camera, 3rd-Person (3rd POV) usually attached to an avatar by an invisible tether. The distance and angle (3D Perspective) between the camera and the avatar can and may vary depending on game play needs. Action (Genre) Fast paced games requiring accuracy and quick reflexes. A genre in which the focus is on graphics, character development, and/or story. The Adventure (Genre) player usually has to solve a series of puzzles while progressing through a story. The players normal role is that of the protagonist. AI Artificial Intelligence An optional channel in some texture file Alpha Channel formats that defines the transparency of the textures pixels. A bounding box, encompassing all points Axis Aligned Bounding Box on a model aligned to the world axes. This is easier to calculate than, but less efficient than an Oriented Bounding Box (AABB) for collision detection calculations. 22 Term

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term Baked Textures Billboard

Bit Depth

Bitmap Blended Animation

Bounding Box

BPP (Bits Per Pixel)

BSP

Callback

Centroid

Meaning Textures, into which, lighting and other information has been combined (i.e. 'baked'). A texture that always maintains it's alignment to the view. The number of bits used used for each of a pixels' color channels. For example, a RGBA texture with a bit depth of 8 would use 32 bits per pixel (BPP), 8 bits per the Red, Green, Blue, and Alpha channels. A texture. A type of skeletal animation whereby a blended animation can be applied to the results of subsequent animations of the skeleton to produce an additive result. A simplified approximation of the volume of model. In other words a rectangular cube encompassing all points on a model. This box may be aligned to the world as an Axis-Aligned-Bounding Box (AABB), or aligned to the object as an OrientedBounding-Box (OBB). The number of bits used to represent a single pixel. Binary Space Partition. An algorithm, whereby 3D space is divided by 2D planes in order to decrease sorting and culling times. Also used for collision detection. Any console method (scripted function associated with object in game world) that is automatically (or directly) called by the engine (or scripts) in response to some event. These callbacks are part of what drives a game. The center of an object.

23

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Meaning A game system organized such that the server is responsible for making most if Client-Server Architecture not all game play related decisions while the client is responsible for acting on those decisions. Collision Detection The process of detecting when two or (COLDET) more objects (in the simulated world) come into contact with each other. Compile The conversion of a high-level computer language into machine language. The opposite of Convex. A mesh with a dimple in its geometry. A shape where one or more line segments Concave on the mesh, when extended infinitely in both directions, pass through the interior of the mesh. Concave meshes cannot be used for collision meshes. The opposite of Concave. A mesh with no dimples. A shape where, when any and all line Convex segments on the mesh are extended infinitely in both directions, they do not pass through the interior of the mesh. Only convex meshes can be used for collision meshes. A Convex brush is a single instance of some regular convex geometry. Convex Convex Brush brushes are combined to create models that can then be converted into an interior. Culling The act of removing unseen polygons from a scene. Cyclic Animation An animation that repeats endlessly in a loop. Datablock A special object in Torque created to contain static data and for scoping. DIF (.dif) Dynamix Interchange Format. See Interiors 24

Term

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term DTS (.dts)

Meaning Dynamix Three Space Format. See Shapes. Lighting that is updated every frame.

Dynamic Lighting The purpose of these games is to increase knowledge and/or improve skills of a student in some area of academia. Can be made with TGE. A technique, whereby a pre-defined texture (bitmap) is applied to a mesh to give the impression that the mesh is reflecting its surroundings. Area that the viewer (camera) can see. A shooter played in 1st POV. Same as Frames Per Second. Number of frames rendered, on average, every second. An area that defines the limits of what the viewer can see. It is shaped like a pyramid, with the point of the pyramid beginning at the eye (camera) position and the base of the pyramid is at right angle to the eye-vector (a vector originating a the eye and extending in the direction the eye is looking). Makers of the Torque Game Engine (TGE), Torque 2D Engine (T2D), and Torque Shader Engine (TSE). Become a member of the community at: http://www.garagegames.com. A class of games The instance of an object that has been duplicated on a client and represents an object on the server. A ghost will behave as it is told to by the server based on what the server object does.

Educational (Genre)

Environment Mapping

Field Of View (FOV) First Person Shooter Frame Rate Frames Per Second

Frustum

Garage Games, Inc.

Genre

Ghost

25

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term GUI

Meaning Graphical User Interface. The authors' company website. A shameless plug to get you to visit: http://www.hallofworlds.com A (grayscale) texture used as a displacement map to define the topography of the polygons. Heads-Up-Display. A type of animation applied to the texture(s) on an object, where the texture being animated is replaced with a new texture (as specified in the list) at regular intervals. This may by cyclic or nonrepetitive. A class, frequently referred to simply as Interior(s), is used to display models that represent any structural object, to include such things as buildings, bridges, walls, and other large structures. Also see DIF. The process of determining from two or more values what the "in-between" values should be. Often confused with 3rd POV, in reality, this is a 2D method that gives the impression of being 3D. Level of Detail pertains to the complexity of a 3D model relative to the current viewing distance to that model. This complexity increases or decreases as the camera (the viewer) moves nearer to or farther from a shape, accordingly. A texture whose values represent the light contribution for a texture. These are often blended/baked with regular textures to create static lighting effects. Geometry of a model.

Hall Of Worlds, LLC.

Height Map HUD

Image File List (IFL)

Interiors

Interpolation Isometric (2D Perspective)

Level of Detail (LOD)

Light Map

Mesh

26

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term Mission (.mis) Mixed (Play Style)

Node

Object

Opaque

Oriented Bounding Box

Pitch Pixel Platform (2D Perspective)

Polygon

Meaning A special file containing all of the statically defined (placed) object in a game or game level. A combination of real-time and turn-based gameplay. A point in a mesh, where one or more bones is connected. Every bone in a model has two nodes, one for each end. Any instance of a game class accessible from within the game world or from scripts. A point in a texture that is completely filled with color and has an alpha value equal to 100%. The opposite of transparent. A bounding box, encompassing all points on a model aligned to the object axes. This is more expensive to calculate than, but more efficient than an Oriented Bounding Box for collision detection calculations. To rotate an object frontwards or backwards (i.e. as if you tilted your the the front or back). Picture element. Describes any action game where the play field is set up as a series of floors, levels, or platforms for the player to navigate. Platform games usually require a small bit of strategy and/or puzzle solving. Examples: Prince of Persia, Dark Castle, etc. A series of vertices that define a plane. Two common polygons are Triangles and Quads.

27

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term

Portal

Prototype Quad

Racing / Driving (Genre) Ray

Ray Casting

Real-time (Play Style) Render Right Angle

Role-Playing Game (RPG) (Genre)

Roll

Meaning A type of rendering where the world is split up into regions. Each is divided from the other regions by one or more portals. Conceptually, you could consider portals to be the doors within doorways, except in the 3D world portals are not rendered and do not collide with object. The most basic test version of a game concept. A square polygon, having four vertices and forming a plane. A game where one or more players race against, a clock or each other. Normally, the race takes place on some kind of track (not-necessarily on the ground). Can be made with TGE. A line. The projection of an imaginary point in 3D space, between two separate points. Often used as part of a collision detection test. If the ray pierces or in any way impinges on an object, a collision has occurred. A game that proceeds constantly in which the player needs to respond actively as events and the game world changes from second to second. To transform 3D (in the game world) data into 2D images (on the screen). A 90-degree angle. A misnomer for most games in this category, these games are usually based on controlling one or more avatars in order to accomplish a series of quests, hackand-slash foes, and/or to gaining experience points and items. Can be made with TGE. To rotate an object leftwards or rightwards (i.e. as if you tilted your head left or right). 28

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term Shape(s)

Side-Scrolling (2D Perspective)

Simulation (Genre)

Skeletal Animation

Sports (Genre)

Static Lighting

Strategy (Genre)

Meaning A model created using a polygon (or equivalent) editor. See also DTS. Used to describe any game where the main setting of game play involves the player moving from one side of the play field to the other horizontally for a length of time; so named because the player character stays in the same place onscreen, but the entire play field scrolls left or right to accommodate keeping player movement on-screen at all times. Usually used as a modifier in describing action games; "shooters", etc. Not to be confused with Platform games, which may or may not scroll. These games aim to simulate a specific activity with a high degree of reality. These games will normally take into account real-world details normally glossed over in other game, for example realistic physics in a bridge building simulation. Can be made with TGE. An animation technique utilizing 'bones'. Each bone has two nodes. These nodes may optionally contribute to the animation of one or more vertices on a mesh. Thus, moving a bone (or bones) in a model using skeletal animation will move one or more vertices in the model's mesh, thereby animating them. A game that emulates some real-world or made-up sport, such as football, tennis, or Rollerball. Can be made with TGE. Lighting that is calculated only once and the applied in the same way every frame, or baked in. See baked textures. A game in which the focus is on planning and resource management in order to achieve a goal. Can be made with TGE.

29

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term Texture Ticks

Tile

Top-Down (2D Perspective)

Transform

Transforms

Translucent

Transparent Triangle

Turn-based (Play Style) Vertex View Sorting

Meaning An image file. A bitmap. A measure of time in Torque, by default equal to 32 milliseconds, but which may be tuned to your needs (if you own the source). To repeat the same texture more than once across a surface. Used to describe any game where the main setting of game play is represented by a top-down" view of the play field; used in describing both shooters and adventure games. In TGE, Translation and Rotation are combined into a single Transform, while scaling is separated into its own representation. See Transforms. Translation, Rotation and Scale. See Transform. A point in a texture that is completely may be partially filled with color and/or has an alpha value greater than 0% and less than 100%. A point in a texture that is completely devoid of color or has an alpha value equal to 0%. A polygon having three vertices and forming a plane. A turn-based game, is a game where each player takes a turn. Once each player has taken a turn, the results of these actions are executed. A point in 3D space. The action of determining in which order objects will render. Most systems render objects back to front in order to present the correct view on a 3D world.

30

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Term Yaw

Meaning To rotate on object about the up axis (i.e. as if you rotated your head left or right while keeping your head completely upright).

31

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

Appendix E. Maze Runner Lessons (Steps Only)

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

Table of Contents
Please Read Me First!!!!............................................................................................................. 5 E.0 Getting Started....................................................................................................................... 6 E.1 Lesson #1 Terrain for Our Game .......................................................................................... 9 E.2 Lesson #2 Loading Datablocks............................................................................................ 10 E.3 Lesson #3 Game Coins....................................................................................................... 11 E.4 Lesson #4 Fade and Fireball Blocks...................................................................................... 12 E.5 Lesson #6 Simplest Player................................................................................................. 13 E.6 Lesson #8 Lava in the Cauldron........................................................................................... 14 E.7 Lesson #9 Starry Night....................................................................................................... 15 E.8 Lesson #10 Low Lighting.................................................................................................... 16 E.9 Lesson #11 Stormy Weather............................................................................................... 17 E.10 Lesson #12 Teleport Station Effect.................................................................................... 19 E.11 Lesson #13 Celestial Bodies.............................................................................................. 20 E.12 Lesson #17 Level Loader................................................................................................... 21 E.13 Finish Gameplay Code.......................................................................................................... 23 E.14 Improve Feedback............................................................................................................... 24

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

Please Read Me First!!!!


This Document This document contains summary steps for achieving the construction of the MazeRunner prototype. Do not expect the steps to match those found in the printed book. Additionally, it has special steps for Windows and OSX users so that both sets of users can use the same guide. Folders vs. Directories Throughout this guide, I will be using the word folder instead of directory. For you Windows and Linux guys and gals, just remember. Folder == Directory Copying Folders, Files, File Contents, Disk Contents, etc. Throughout this guide, I will be asking you to copy a variety of 'things' from one place to another. I have tried to be clear in my instructions but nonetheless, you may wonder if I have goofed a step here or there. Rest assured, I have walked through every step in this appendix. So, if I say, Copy folder X. Then paste it into folder Y, this is exactly what I intend for you to do. If I should ever wish you to copy just the contents of a folder (or file), I will explicitly state, Copy the contents of folder X. Copy-Paste Operations All copy operations in this document are achieved in two steps. First, a copy to the copy buffer and then a paste to some folder (or file), as specified in the instructions you are reading at the time. This was done to make the steps easier to write and to understand. OSX Differences The Key to start the Mission Editor is CTRL F11. This removes a possible conflict with other default MAC key mappings. In OSX 10.4, F11 is tied to Expose' by default. The Key to start the GUI Editor is CTRL F10. This removes a possible conflict with other default MAC key mappings. In OSX 10.4, F10 is tied to 'Applications Windows' by default (this shows all open windows for the current application.)

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.0 Getting Started


E.0.1 Copy Accompanying Disk To Hard Drive (IMPORTANT).
As your first step, please copy the entire contents of accompanying disk to a folder named GPGT. You may place this folder on your desktop, or in a location of your choice. Please just remember where you have placed it. For the remainder of this appendix, I will simply be referring to this folder as GPGT.

E.0.2 Create MazeRunner directory Windows Users ONLY.


If you do not already have the Torque demo installed, please do so at this time. A copy of the installer is located here: GPGT\TorqueDemoInstallers\TorqueDemo-Installer-1-4.exe Please follow these steps: 1. Locate the directory where you installed TGE (by default this is C:\Program Files\Torque Game Engine Demo). 2. Copy the entire Torque Game Engine Demo directory. 3. Paste this directory to your desktop. 4. Rename the resulting directory to: MazeRunner. For the remainder of this appendix, I will refer to this directory as MazeRunner.

E.0.3 Create MazeRunner directory OSX Users ONLY.


Be sure that you have visited the Hall Of Worlds GPGT Support Page: http://gamers.hallofworlds.com/support/gpgt/ And downloaded the OSX Maze Runner Starter ZIP file: http://gamers.hallofworlds.com/downloads/Support/GPGT/OSXStarter.zip Please follow these steps: 1. Copy the OSXStarter.zip file. 2. Paste it to your desktop. 3. Double-click the new copy of OSXStarter.zip to unzip it. 4. Rename the resulting folder to: MazeRunner. For the remainder of this appendix, I will refer to this folder as MazeRunner.

Result of Step 3: http://www.garagegames.com 6

Result of Step 4: http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.0.4 Copy prototype into MazeRunner.


1. Copy the folder GPGT/MazeRunner/A_SettingUp/prototype. 2. Paste it into "MazeRunner/prototype/"

E.0.5 Edit main.cs.


1. Open the file MazeRunner/main.cs 2. Look at the top of the file, on about line 6 and you will see this statement:
$defaultGame = "demo";

3. Please change it to this:


$defaultGame = "prototype";

E.0.6 Add systems scripts.


1. Copy the folder GPGT/base/scripts/EGSystems. 2. Paste it into the folder "MazeRunner/prototype/". 3. Open the file MazeRunner/prototype/main.cs. 4. Locate the onStart() function. 5. Modify the top of this function to contain this code:
function onStart() { // Maze Runner Changes Begin --> exec("./EGSystems/SimpleInventory/egs_SimpleInventory.cs"); exec("./EGSystems/SimpleTaskManager/egs_SimpleTaskManager.cs"); exec("./EGSystems/Utilities/egs_ArrayObject.cs"); exec("./EGSystems/Utilities/egs_Misc.cs"); exec("./EGSystems/Utilities/egs_Networking.cs"); exec("./EGSystems/Utilities/egs_SimSet.cs"); exec("./EGSystems/Utilities/egs_String.cs"); // <-- Maze Runner Changes End //.. leave remaining code alone

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.0.7 Add Maze Runner data.


1. Copy the folder "GPGT/Base/Data/GPGTBase" 2. Paste it into the folder "MazeRunner/prototype/data/" 3. Copy the folder "GPGT/MazeRunner/A_SettingUp/MazeRunner" 4. Past it into the folder "MazeRunner/prototype/data/"

E.0.8 Create Maze Runner scripts directory.


Please create a new folder named: "MazeRunner/prototype/server/scripts/MazeRunner".

E.0.9 Testing Windows User Only.


In the directory MazeRunner, please locate the file demo.exe. Double click this to start the prototype of MazeRunner. For the remainder of this appendix, when I say to 'start your prototype' this is what I mean.

E.0.10 Testing OSX Users Only.


In the directory MazeRunner, please locate the file Torque Demo OSX. Double click this to start the prototype of MazeRunner. For the remainder of this appendix, when I say to 'start your prototype' this is what I mean.

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.1 Lesson #1 Terrain for Our Game


E.1.1. Copy files.
1. Copy the folder "GPGT/MazeRunner/Lesson_001/heightFields" 2. Paste it into the folder "MazeRunner/prototype/data/".

E.1.2. Generate new terrain.


1. Start your prototype. 2. Run the "Maze Runner" mission. 3. Start the Terraformer. 4. Use the 'Bitmap' operation to generate a terrain using the file "prototype/data/heightFields/mazerunner.png".

Terrain Preview 5. Save the mission.

Terraformer Settings

E.1.3. Adjust spawn point.


Please use the inspector to change the position of the spawn point to: 0 0 100.

http://www.garagegames.com

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.2 Lesson #2 Loading Datablocks


E.2.1. Copy files.
1. Copy the folder "GPGT/Base/Scripts/GPGTBase". 2. Paste it into the folder "MazeRunner/prototype/server/scripts/".

E.2.2. Edit game.cs.


Open the file "MazeRunner/prototype/server/game.cs" and edit the function onServerCreated() to look like this (BOLD lines are new or modified):
exec("./markers.cs"); exec("./player.cs"); exec("./GPGTBase/loadGPGTBaseClasses.cs"); // MazeRunner

E.2.3. Testing.
1. Start your prototype. 2. Load the Maze Runner mission. 3. Start the creator tool. 4. Verify that you the creator tree contains the highlighted shapes (as shown in image to right).

http://www.garagegames.com

10

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.3 Lesson #3 Game Coins


E.3.1. Copy required files.
1. Copy the file "GPGT/MazeRunner/Lesson_003/coins.cs". 2. Paste it into the folder "MazeRunner/prototype/server/scripts/MazeRunner/.

E.3.2. Edit game.cs.


Open the file "MazeRunner/prototype/server/game.cs" and edit the function onServerCreated() to look like this (BOLD lines are new or modified):
exec("./GPGTBase/loadGPGTBaseClasses.cs"); // MazeRunner exec("./MazeRunner/coins.cs"); // MazeRunner

E.3.3. Testing.
1. Start your prototype. 2. Open the Maze Runner mission. 3. Start the Creator. 4. Look under Shapes and find the folder GameItems. 5. Open the GameItems folder to find a new placeable shape named Coin. If this did not work, check your console for errors (typos, files not found, etc).

http://www.garagegames.com

11

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.4 Lesson #4 Fade and Fireball Blocks


E.4.1. Copy required files.
1. Copy the file GPGT/MazeRunner/Lesson_004/fadeblocks.cs. 2. Paste it into the folder MazeRunner/prototype/server/scripts/MazeRunner/. 3. Copy the file GPGT/MazeRunner/Lesson_004/fireballs.cs. 4. Paste it into the folder MazeRunner/prototype/server/scripts/MazeRunner/.

E.4.2. Edit game.cs.


Open the file "MazeRunner/prototype/server/game.cs" and edit the function onServerCreated() to look like this (BOLD lines are new or modified):
exec("./MazeRunner/coins.cs"); // MazeRunner exec("./MazeRunner/fadeblocks.cs"); // MazeRunner exec("./MazeRunner/fireballs.cs"); // MazeRunner

E.4.3. Testing.
1. Start your prototype. 2. Open the Maze Runner mission. 3. Start the Creator. 4. Look under Shapes and find the folders FadeBlocks and FireBallBlocks. If this did not work, check your console for errors (typos, files not found, etc).

http://www.garagegames.com

12

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.5 Lesson #6 Simplest Player


E.5.1. Copy required files.
1. Copy the file "GPGT/MazeRunner/Lesson_006/mazerunnerplayer.cs". 2. Paste it into the folder "MazeRunner/prototype/server/scripts/MazeRunner/".

E.5.2. Edit game.cs.


Open the file "MazeRunner/prototype/server/game.cs" and edit the function onServerCreated() to look like this (BOLD lines are new or modified):
exec("./MazeRunner/coins.cs"); // MazeRunner exec("./MazeRunner/fadeblocks.cs"); // MazeRunner exec("./MazeRunner/fireballs.cs"); // MazeRunner exec("./MazeRunner/mazerunnerplayer.cs"); // MazeRunner

E.5.3. Use the new player.


Still in the file "MazeRunner/prototype/server/game.cs", locate the createPlayer() method and modify the player creation code to look like this (BOLD lines are new or modified):
function GameConnection::createPlayer(%this, %spawnPoint) { //... // Create the player object %player = new Player() { dataBlock = MazeRunner; client = %this; }; //...

E.5.3. Testing.
1. Start your prototype. 2. Open the Maze Runner mission. 3. Press TAB You should be able to see the player and it should be a yellow ball. If it is, good job! If not, check the logs for errors, then go back and check your steps.

http://www.garagegames.com

13

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.6 Lesson #8 Lava in the Cauldron


E.6.1. Create new water block.
1. Start your prototype. 2. Run the "Maze Runner" mission. 3. Start the Creator Tool. 4. Create a new water block (Mission Objects -> Environment -> Water), proving the information shown in the image below.

5. Start the Inspector. 6. Modify the attributes of our new water block (MazeRunnerWater) to match the settings the the table below.

Parameter position scale UseDepthMask surfaceTexture shoreTexture specularMaskTex specularColor specularPower All dynamic fields All others

Value -256 -256 55 512 512 15 true prototype/ data/GPGTBase/water/lava.png prototype/ data/GPGTBase/water/lava.png prototype/ data/GPGTBase/water/lavaspecmask.png 1 1 1 0.2 12 Remove All Of These Use defaults

7. Save the mission.

http://www.garagegames.com

14

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.7 Lesson #9 Starry Night


E.7.1. Configure the sky object.
1. Start up your prototype. 2. Run the "Maze Runner" mission. 3. Start the Inspector. 4. Change the DML file for the Sky Object to point to this one: "prototype/data/GPGTBase/skies/starrynight/starry_sky.dml". 5. Now, modify the remaining settings as shown in the following table.

Parameter cloudHeightPer[0] cloudHeightPer[1] cloudHeightPer[2] cloudSpeed1 cloudSpeed2 cloudSpeed3 visibleDistance fogDistance fogVolume1 fogVolume2 fogVolume3 all others

Value 0.5 0 0 0.0005 0 0 1000 2000 550 0 300 000 000 Use defaults

6. Save the mission.

http://www.garagegames.com

15

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.8 Lesson #10 Low Lighting


E.8.1. Configure the sun object.
1. Start up your prototype. 2. Run the "Maze Runner" mission. 3. Start the Inspector. 4. Modify the Sun Object settings as shown in the following table.
Fields elevation azimuth color ambient Values 90 90 0.5 0.3 0.3 1 0.2 0.2 0.2 1

5. Relight the mission (ALT + L). 6. If the lighting is not satisfactory, feel free to adjust it further. 7. Save the mission.

http://www.garagegames.com

16

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.9 Lesson #11 Stormy Weather


E.9.1. Adding the rain.
1. Start your prototype. 2. Run the "Maze Runner" mission. 3. Start the Creator. 4. Create a precipitation object (Mission Objects -> Environment -> Precipitation) using the settings shown in the image below.

5. Start the Inspector. 6. Modify our newly created Precipitation object (MazeRunnerRain) to have the settings shown in the following table.

Parameter minSpeed maxSpeed rotateWithCameraVel numDrops boxWidth boxHeight doCollision all others

Value 1 1.5 true 2000 200 100 0 Use defaults

7. Save the mission.

http://www.garagegames.com

17

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.9.2. Adding the lightning and thunder.


1. Start the Creator. 2. Create a lightning object (Mission Objects -> Environment -> Lightning) using the settings shown in the image below.

3. Start the Inspector. 4. Modify our newly created Lighting Object (MazeRunnerLightning) to have the settings shown in the following table.
Parameter position scale strikesPerMinute strikeWidth strikeRadius color fadeColor chanceToHitTarget boltStartRadius all others Value 0 0 300 256 256 250 6 1.5 128 0.89 0.8 0.42 1 0.5 0.9 0.9 1 0 32 Use defaults

5. Save the mission.

http://www.garagegames.com

18

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.10 Lesson #12 Teleport Station Effect


E.10.1. Copy required files.
1. Copy the file "GPGT/MazeRunner/Lesson_012/teleporters.cs". 2. Paste it into the folder "MazeRunner/prototype/server/scripts/MazeRunner/".

E.10.2. Edit game.cs


Open the file "MazeRunner/prototype/server/game.cs" and edit the function onServerCreated() to look like this (BOLD lines are new or modified):
exec("./MazeRunner/mazerunnerplayer.cs"); // MazeRunner exec("./MazeRunner/teleporters.cs"); // MazeRunner

E.10.3. Testing the emitters.


1. Start you prototype. 2. Load the "Maze Runner" mission. 3. Use the Creator to place a particle emitter ("Mission Objects -> Environment -> ParticleEmitter"). 4. Give the emitter (node) any name you like. 5. Use the basePEND ParticleEmitterNodeData datablock. 6. Select one of the three ParticleEmitterData datablocks from this lesson.

ParticleEmitter Dialog Settings

Resultant Emitters

http://www.garagegames.com

19

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.11 Lesson #13 Celestial Bodies


E.11.1. Loading the celestial bodies.
1. Open the file "MazeRunner/prototype/data/missions/mazerunner.mis" 2. Open the file ''GPGT/MazeRunner/Lesson_013/CelestialBodies.cs" 3. Select the contents of CelestialBodies.cs and copy it into your copy buffer (CTRL+C for Windows and +C OSX). 4. Go back to the mazerunner.mis file and scroll to the bottom. 5. Paste (CTRL+V for Windows and just before these lines:
}; //--- OBJECT WRITE END ---

+V OSX) the data you just copied into your copy buffer

E.11.3. Testing the celestial bodies.


1. Start you prototype. 2. Load the "Maze Runner". 3. Press ALT+C 4. Look around. At this time, the three celestial bodies should be visible.

http://www.garagegames.com

20

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.12 Lesson #17 Level Loader


E.12.1. Copy required files.
1. Copy the file "GPGT/MazeRunner/Lesson_017/levelloader.cs" 2. Paste it into the folder "MazeRunner/prototype/server/scripts/MazeRunner".

E.12.2. Edit game.cs.


Open the file "MazeRunner/prototype/server/game.cs" and edit the function onServerCreated() to look like this (BOLD lines are new or modified):
exec("./MazeRunner/teleporters.cs"); // MazeRunner exec("./MazeRunner/levelloader.cs"); // MazeRunner

E.12.3. Add temporary spawn point.


1. Open the file "MazeRunner/prototype/data/Missions/mazerunner.mis" 2. Add the following code at the bottom of the file (BOLD lines are new):
new TSStatic() { position = "0 0 295"; rotation = "1 0 0 0"; scale = "1 1 1"; shapeName = "~/data/MazeRunner/Shapes/MazeBlock/blockA.dts"; };

}; //--- OBJECT WRITE END ---

3. In the same file, locate the Spawn Point (PlayerDropPoints) and modify it to have the following position (BOLD line is modified):
new SimGroup(PlayerDropPoints) { new SpawnSphere() { position = "0 0 300"; // ... }; };

http://www.garagegames.com

21

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.12.4. Testing the level loader.


1. Start you prototype. 2. Load the "Maze Runner". 3. The player should now drop onto a block (approximately parallel to rim of cauldron).

http://www.garagegames.com

22

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.13 Finish Gameplay Code


E.13.1. Copy required files.
1. Copy the folder "GPGT/MazeRunner/MazeRunner_Post_Finishing_the_Prototype/prototype2" 2. Paste it into "MazeRunner/" 3. Copy the file "GPGT/MazeRunner/MazeRunner_Post_Finishing_the_Prototype/main.cs" 4. Paste it into "MazeRunner/" With this modification we are now running the code found in the folder "MazeRunner/prototype2" . This folder includes ALL of the (final) changes we will examine while reading section 14.8. Finish Gameplay Code Do not modify the code, even if the instructions in the book tell you to. Simply read along instead.

http://www.garagegames.com

23

http://www.hallofworlds.com

Copyright GarageGames.com

Product of Hall Of Worlds, LLC.

E.14 Improve Feedback


E.14.1. Copy required files.
1. Copy the folder "GPGT/MazeRunner/MazeRunner_Post_Improve_Feedback/prototype3". 2. Paste it into the folder "MazeRunner/". 3. Copy the folder "GPGT/MazeRunner/MazeRunner_Post_Improve_Feedback/main.cs". 4. Paste it into the folder "MazeRunner/". With this modification we are now running the code found in the folder "MazeRunner/prototype3" . This folder includes ALL of the (final) changes we will examine while reading section 14.9. Improve Feedback Do not modify the code, even if the instructions in the book tell you to. Simply read along instead.

http://www.garagegames.com

24

http://www.hallofworlds.com

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Errata Corrections as of 17 May 2006

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Table of Contents
Page 90 MainMenuGui.gui vs. MainMenu.gui.......................................................................................... 3 Page 91 MainMenu.gui Path Is Wrong................................................................................................... 4 Page 94 Incorrect Path........................................................................................................................ 5 Page 107 Missing Commas................................................................................................................... 6 Page 110 Incorrect Example Output Given............................................................................................ 7 Page 119 Sample Unclear.................................................................................................................... 8 Page 129 Note Block Sentence Incomplete............................................................................................ 9 Page 132 Incorrect Instructions in Lesson #2...................................................................................... 10 Page 133 Lesson #2 Contains Unclear Steps....................................................................................... 11 Page 133 BT18(a) and BT18(b).......................................................................................................... 12 Page 143 Missing Image?.................................................................................................................. 13 Page 181 Caption Missing Ending Text................................................................................................ 14 Page 186 Expert Tip (Reloaders)........................................................................................................ 15 Page 187 Syntax Error In Sample Code............................................................................................... 16 Pages 223 & 226 Maze Runner Lesson #6 Has Double Copy................................................................. 17 Page 246 Book vs. Kit Code Mismatch................................................................................................. 18 Page 257 Book vs. Kit Code Mismatch................................................................................................. 19 Page 278 Spelling Error...................................................................................................................... 20 Page 285 cloudHeightPer................................................................................................................... 21 Page 296 Thunder Missing From Lesson #11....................................................................................... 22 Page 325 Bad Table Reference........................................................................................................... 23 Pages 329 thru 331 Table Numbers Off By -1....................................................................................... 24 Page 372 Wrong File Reference Lesson #17........................................................................................ 25 Page 384 TS10()............................................................................................................................... 26 Page 574 Incorrect Path..................................................................................................................... 28 Page 576 Confusing Directions Section 14.4.4...................................................................................... 29 Page 576 Incorrect Filename.............................................................................................................. 30

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 90 MainMenuGui.gui vs. MainMenu.gui


Bug Credit: Hokuto Description: @ Ed Maurina: I think there is a problem in Chapter 3 - Page 90/91 Page.90 Last bullet point says: "File->SaveGUI, select MainMenuGui.gui". That won't work, I think you should save to "mainMenu.gui' instead, then it will work. Response: Hokuto is correct. The Main Menu interface in the GPGT lesson kit has a top-level GUI named MainMenu. Therefore, the engine will assume you want to save it in the file MainMenu.gui. In fact, I did save it in the file MainMenu.gui. This problem is a result of my switching to using the GPGT lesson kit as the example base versus using starter.fps. If you are using the starter.fps MOD as a base, the Main Menu interface has a top-level GUI named MainMenuGui. Therefore, the engine will assume you want to save the file as MainMenuGui.gui. So, if you are playing around with the GPGT lesson kit, save your file as MainMenu.gui. If you are playing around with the FTS Starter Kit (starter.fps), save the Main Menu as MainMenuGui.gui.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 91 MainMenu.gui Path Is Wrong


Bug Credit: Hokuto Description: Page.91

1. Open the file "gpgt\client\Interface\mainMenu.gui"


it should be:

"gpgt\client\Interface\mainMenu\mainMenu.gui"
Response: Yes, I moved files around a bit during the design of the kit and it seems I missed this path update in the book. The Main Menu interface for the GPGT lesson kit is located under: "gpgt\client\Interface\mainMenu\mainMenu.gui"

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 94 Incorrect Path


Bug Credit: Roto Description: Roto notes that the following statement (on page 94 near the top of the page) is incorrect:

wake it. This loading is done in various places. The organization of GUI loading scripts is beyond the scope of this section, but for completeness, Ill show you how to get your new GUI loaded. Open the file egt\client\init.cs and search for the following code.

Correction: The last line should read: Open the file "GPGT LessonKit\gpgt\client\init.cs" and search for the following code.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 107 Missing Commas


Bug Credit: Hokuto Description: In Chapter 4, on page 107, there is a syntax error in two lines of code: //bt06(); $a = "This is a regular string"; $b = 'This is a tagged string'; echo("Regular String: " $a); echo("Tagged String: " $b); There is a comma missing in both of the bold lines of code above. Correction: The code should read as follows: //bt06(); $a = "This is a regular string"; $b = 'This is a tagged string'; echo("Regular String: ", $a); echo("Tagged String: ", $b);

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 110 Incorrect Example Output Given


Bug Credit: Justin Mosiman Description: In Chapter 4, on page 110, A snippet of code is provided: //bt12(); $srcRay = "1.0 0.0 1.0"; $destRay = "1.0 6.0"; echo ( VectorAdd( $srcRay , $destRay ) ); Below this, I say that the output will be: 1 6 1 This is incorrect. Correction: The correct output for this code snippet will be: 2 6 1

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 119 Sample Unclear


Bug Credit: Hokuto Issue Description: Hokuto notes the following:

If I attempt to follow what the book asks to do on one of the GPGT lesson Kit's missions, the code won't work. When you type: $Player_id = $Player_name.getID(); //second line of code I get <input> (0): unable to find object: 'myGuy' attempting to call function 'getId'

Issue Cause: The instructions are probably a little unclear. As well, if you are skimming (which is perfectly reasonable) you might miss the instructions a few lines above. Issue Resolution: To get this example to work, you must first do the following: 1. Open the mission editor (F11) and switch to the Inspector (F3). 2. Toggle to 'Free Camera' mode (ALT+C) 3. Move a little ways back so you can see the player. 4. Select the player. 5. Name the character by placing the string MyGuy next to the Apply Button in the inspector and then clicking Apply. (see image below).

6. Now, you may proceed with the sample.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 129 Note Block Sentence Incomplete


Bug Credit: Justin Mosiman Issue Description: Justin notes the following about the note block on the right side of the page: It reads:

"For example, a p-zone can be used to change the gravity and/or apply a force and/or modify an object's current velocity when the object passed into or through the area encapsulated by the"
and then it stops. Correction: The sentence should read:

"For example, a p-zone can be used to change the gravity and/or apply a force and/or modify an object's current velocity when the object passed into or through the area encapsulated by the physical zone's bounds."

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 132 Incorrect Instructions in Lesson #2


Bug Credit: Hokuto Description: Hokuto found that on page 132 (in Maze Runner Lesson #2), there is the following mistake:

It says:
"2. Now, edit the function onServerCreated() in the file "\MazeRunner\prototype\server\game.cs"

It should say:
"\MazeRunner\prototype\server\scripts\game.cs"

Correction: As can be seen, Hokuto has supplied us with the correction. The supplied path (in the book) is in fact missing the scripts portion of the path.

10

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 133 Lesson #2 Contains Unclear Steps


Bug Credit: Hokuto Issue Description: Hokuto notes that the following statement is misleading:

pg. 133, paragraph 2 (after code at top of page) "To test for successful load, simply start the kit and load the "MazeRunner"

Issue Cause: This statement may well be unclear because the statement uses the word 'kit'. The use of the word kit seems to imply the use of either the lesson kit or maybe the starter.fps kit. In fact, you are supposed to start your prototype. Issue Resolution: 1. Be sure that you have followed all of the directions in section 14.4 Setting Up Or Workspace, starting on page 574 and ending on page 577. If you have not created a working area for your prototype, none of the lessons will work. 2. Be sure that you followed lesson #1 on page 72. 3. For all subsequent numbered lessons be sure to run your prototype, not the GPGT lesson kit.

11

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 133 BT18(a) and BT18(b)


Bug Credit: Mark Barner Description: These two scripts, as supplied on the accompanying and shown on page 133 do not run correctly, producing errors similar to these:
==>bt18(a); ~~~~~~~~~~~~~~~~~~~~~~ Object 'SimpleTarget1' is not a member of the 'GameBaseData' data block class gpgt/SampleScripts/stsBasicScripting.cs (461): Register object failed for object CoolTarget of class StaticShape. gpgt/SampleScripts/stsBasicScripting.cs (466): Unable to find object: '0' attempting to call function 'DoIt'

==>bt18(b); ~~~~~~~~~~~~~~~~~~~~~~ Object 'SimpleTarget1' is not a member of the 'GameBaseData' data block class gpgt/SampleScripts/stsBasicScripting.cs (461): Register object failed for object CoolTarget of class StaticShape. gpgt/SampleScripts/stsBasicScripting.cs (470): Unable to find object: '0' attempting to call function 'DoIt' Calling StaticShape::DoIt() ==> on object 0 gpgt/SampleScripts/stsBasicScripting.cs (474): Unable to find object: 'CoolTarget' attempting to call function 'DoIt' gpgt/SampleScripts/stsBasicScripting.cs (476): Unable to find object: 'CoolTarget' attempting to call function 'DoIt' gpgt/SampleScripts/stsBasicScripting.cs (485): Unable to find object: '0' attempting to call function 'getId' Syntax error in input. gpgt/SampleScripts/stsBasicScripting.cs (491): Unable to find object: '0' attempting to call function 'getId' <input> (0): Unable to find object: '' attempting to call function 'DoIt' gpgt/SampleScripts/stsBasicScripting.cs (497): Unable to find object: '0' attempting to call function 'getId' Calling StaticShape::DoIt() ==> on object

Problem: The sample code on the disk does uses a datablock that was not defined. Resolution: 1. Open file /gpgt/sampleScripts/stsBasicScripting.cs in text editor. 2. Locate function bt18( %sampleCase ) 3. Modify code as shown below:

//bt18(a); %myTarget = new StaticShape( CoolTarget ) { position = "0 0 0"; // dataBlock = "SimpleTarget1"; dataBlock = "BaseStaticShape"; // Uses base static shape datablock. };

12

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 143 Missing Image?


Bug Credit: Roto Issue Description: Roto notes: Page 143 the sentence just before section 5.1 SimObject:

Please note that the dotted line in Figure 5.1 indicates that there is a class between the two connected classes that we are not discussing
There is no Figure 5.1. In fact there is not Figures in chapter 5 at all, only tables. Response: Roto is correct. We pulled that image at the last minute and the text did not get updated. Here is the image:

13

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 181 Caption Missing Ending Text


Bug Credit: Mark Barner Description: The Note Caption on page 181 ends in an incomplete sentence. This is quite useful, as such mounted shapes can temporarily shield an item from contact and thus from Problem: It looks like some words got cut off in copy-editing. Resolution: The sentence should read: This is quite useful, as such mounted shapes can temporarily shield an item from contact and thus from pickup. The idea is that you can temporarily keep items from being picked up by mounting objects or shields onto them. This isn't particularly elegant, but it could have some interesting uses.

14

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 186 Expert Tip (Reloaders)


Bug Credit: Mark Barner Description: Mark asks, The caption reads,
function rldfb() { exec("./fadeblocks.cs"); }

but the code shows,


function rldfade() { exec("./fadeblocks.cs"); }

Can you name the function anything you want? Resolution: Yes. You may name reloaders anything you want as long as you remember what they are. :) Obviously, I forgot what I named that one. In all seriousness, I use reloaders while testing code in SinglePlayer mode. This saves me a lot of time without going to the trouble of hooking up a script debugger.

15

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 187 Syntax Error In Sample Code


Bug Credit: Mark Barner Description: Mark found this syntax error:
%object = new TSStatic() { position = "0 0 0" <--- is missing ";" in book rotation = "1 0 0 0"; scale = "1 1 1"; shapeName = "~/data/Shapes/Lessons/GeneralLessonShapes/egg.dts"; };

16

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Pages 223 & 226 Maze Runner Lesson #6 Has Double Copy
Bug Credit: Mark Barner Description: Mark finds this problem:
Quote (from page 223):

Copy Required Files From the accomanying disk, please copy the file "\MazeRunner\Lesson_006\mazerunnerplayer.cs" into "\MazeRunner\prototype\server\scripts\MazeRunner". ... exec("./MazeRunner/mazerunnerplayer.cs"); //MazeRunner

Quote (from page 226)

To get the entire datablock, please copy "\MazeRunner\Lesson_006\MazeRunnerPlayer.cs". to the "\MazeRunner\prototype\server\scripts\MazeRunner" directory. Loading the Datablock ... exec("./MazeRunner/MazeRunnerPlayer.cs"); // MazeRunner

Problem: The statement on page 226 can be ignored. If you followed the directions on page 223, you already have the file in place. No further changes are needed at this point in the construction of Maze Runner. Resolution: Ignore all text on page 226 between: 'To get the entire datablock' and ' ...\MazeRunner directory.'

17

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 246 Book vs. Kit Code Mismatch


Bug Credit: Mark Barner Description: Mark notes: Chapter 7, section 7.7.1, page246 - 248. Says to follow along with the script "SimpleInventoryGeneral.cs". The variable in the book is "%theSimpleInventory" and in SimpleInventoryGeneral.cs the variable is "%theInventory". Problem: I caused this issue by renaming some files just prior to shipping the Kit to the publisher. This change came about because late in the editing process, I shortened the names of some files because we ran into a issue compressing them to the ZIP format. There is a fixed limit on the length of a path and filename that can be zipped and later unzipped. There may be other instances of this type of error. Resolution: There is no resolution. I will have to correct this issue in later printings and must apologize to those who have a printing with this typo.

18

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 257 Book vs. Kit Code Mismatch


Bug Credit: Mark Barner Description: Mark notes: Chapter 7, section 7.7.5, page 257
Quote:

Take a look in the file " \MazeRunner\prototype\server\scripts\GPGTBase\Player\PlayerDataConsoleMethods.cs". It contains the...

The script file on disk is PlayerDataMethods.cs that is copied over not PlayerDataConsoleMethods.cs. Problem: I caused this issue by renaming some files just prior to shipping the Kit to the publisher. This change came about because late in the editing process, I shortened the names of some files because we ran into a issue compressing them to the ZIP format. There is a fixed limit on the length of a path and filename that can be zipped and later unzipped. There may be other instances of this type of error. Resolution: There is no resolution. I will have to correct this issue in later printings and must apologize to those who have a printing with this typo.

19

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 278 Spelling Error


Bug Credit: Mark Barner Description: Mark notes: Chapter 8, section 8.3.4 Maze Runner Lesson #8, page 278, table 8.3. Parameters: surfaceTexture, shoreTexture, specularMaskTex values = "starter.fps/data/GPGTBase/water/..." should read "prototype/data/GPGTBase/water/..." for the Maze Runner lesson. Problem: In the original lessons, I used an unmodified version of the FPS starter kit as a base. Subsequently, I chose to use a modified version and supply in under the name 'prototype'. During technical edits, we should have replaced all references of 'starter.fps' with 'prototype', but it looks like this one got by. Resolution: In all lessons, if you find the word 'starter.fps' treat it as if the word 'prototype' were used instead.

20

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 285 cloudHeightPer


Bug Credit: Mark Barner Description: Mark asks: Chapter 8, section 8.4.9 Maze Runner Lesson #9, page 285, step 3.
Quote:

3. Using the Inspector, be sure that the sky has the settings shown in Table 8.4.

Only one of the three cloudHeightPer parameters can be change in the Inspector in TGE 1.4. You have to go into the mission file and change the parameters. It must be a TGE 1.4 bug or am I missing something? Response: No, Mark you are not missing anything. This is in fact a bug with the engine/editors. The cloudHeightPer array is wrongly exposed to the inspector and thus cannot be edited correctly. The real challenge here is not in fixing the bug, but in making it backward compatible. For now, this is on my list of 'Torque Issues To Resolve/Address'. Resolution: The only resolution at this time is to hand edit your mission, or to edit the engine code. Unfortunately, I cannot post an answer in this document as that would violate the engine EULA. However, I can give you a hint. 1. Open the file sky.cc 2. Find the routine void Sky::initPersistFields() on about line 279. 3. Compare how 'mCloudSpeed[0]' ... 'mCloudSpeed[2]' are exposed vs. how 'mCloudHeight' is exposed. During this examination, you may wonder why the 'mCloudSpeed[0]' ... 'mCloudSpeed[2]' is exposed as 'cloudSpeed1' ... 'cloudSpeed3'. Yup. Another legacy bug. On my own projects I always change this to 'cloudSpeed0' ... 'cloudSpeed2'. This too is on my list.

21

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 296 Thunder Missing From Lesson #11


Bug Credit: Mark Barner Description: Mark asks: Chapter 8, section 8.6.6 Maze Runner Lesson #11, page 296, Adding the Lightning and Thunder. Was there supposed to be thunder added in this lesson or is it going to be discussed in a later chapter? Response: There should be lighting added in that lesson. I'll check into this when I receive my author's copies. Meanwhile, take a look at the 'Lightning' 3D Lesson in the GPGT Lesson Kit if you need to learn about adding thunder.

22

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 325 Bad Table Reference


Bug Credit: Mark Barner Description: Mark notes: Chapter 8, section 8.9.10, page 325, Other Culling Features.
Quote:

... When an object is at the perimeter of the outer sphere it will begin to fade, fading completely out at the perimeter of the inner sphere. See Table 8.25 and Figure 8.27.

It should read Table 8.26. Response: Thanks Mark. I'll get this fixed in a subsequent revision.

23

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Pages 329 thru 331 Table Numbers Off By -1


Bug Credit: Mark Barner Description: Mark discovered that starting on page 330, the tables in chapter 8 are incorrectly numbered for two pages. Specifically, the tables on pages 330 and 331 are numbered 8.26 thru 8.32. This means chapter 8 has two tables numbered 8.26 and is completely missing 8.33. This is further confused by the statement on page 329:

Animations provide the parameters in Table 8.27. Table 8.28-8.33 list the specific parameters for color, brightness, rotation, size, azimuth, and elevation, respectively.
Correction: Never fear. The tables you need are all present. The tables on pages 330 and 331 should be numbered: 8.27 thru 8.33. The quote from page 329 is in fact referring to the tables on pages 330 and 331.

24

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 372 Wrong File Reference Lesson #17


Bug Credit: Mark Barner Description: Mark notes: Chapter 9, section9.5.10 Maze Runner Lesson # 17,page 372, Copy Required Files.
Quote:

From the accompanying disk, please copy the file "\MazeRunner\Lesson_12\teleporters.cs" into .... .... exec("./MazeRunner/levelloader.cs"); //MazeRunner

Lesson_12\telporters.cs should be Lesson_17\levelloader.cs. Response: Thanks Mark. I'll get this fixed in a subsequent revision.

25

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 384 TS10()


Bug Credit: Mark Barner Description: This script fails to execute, producing an error like this:
==>ts10(); ~~~~~~~~~~~~~~~~~~~~~~ gpgt/SampleScripts/stsAdvancedScripting.cs (470): Unable to find object: '' attempting to call function 'delete'

Problem: The sample depends on the presence of a datablock that has no longer available. The GPGT Lesson Kit script stsAdvancedScripting.cs requires that the '3D Lessons' mission be running to execute the samples. This is required because datablocks are used in some examples, which requires certain engine features to be active. Running a mission activates these parts of the engine. The problem exists because the script stsAdvancedScripting.cs is loaded when the application starts. At this time, the datablock 'myTestDatablock' is created. Subsequently, when you start the '3D Lessons' mission, the kit first deletes all datablocks in preparation to load new ones. If you consider this, you will understand that by the time the mission starts 'myTestDatablock' has been destroyed. This causes the lesson to fail. Resolution: The resolution to this problem is simple, but can be misleading. So, please the caution at the end of this fix. 1. Open file /gpgt/sampleScripts/stsAdvancedScripting.cs in text editor. 2. Locate function ts18( ) 3. Modify code as shown below:
// Comment the old datablock definition //datablock StaticShapeData( myTestDatablock : BaseStaticShape ) { // category = "LessonShapes"; //}; function ts10() { if( ! sscls() ) return; // Copy the same datablock definition into the body of the sample function datablock StaticShapeData( myTestDatablock ) { category = "LessonShapes"; }; %obj = new StaticShape( testObject ) { datablock = "myTestDatablock"; }; } %obj.delete();

26

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

With these fixes you should now get:


~~~~~~~~~~~~~~~~~~~~~~ A new object: testObject was created with the datablock: myTestDatablock Deleting: testObject created with the datablock: myTestDatablock

Cautionary Note: First, this is and advanced topic, so if yo have not read about datablocks yet and if you have not done some samples on your own, this may not mean a lot to you. In the above fix, we declared a datablock within the body of a function. Doing so is completely legal, but will have certain sideffects, depending upon the game type you are writing. If you are writing a SinglePlayer game, then the engine will automatically find and use this new datablock. However, if you are writing a MultiPlayer game and you create a new datablock after datablocks have already been transmitted to all clients, then ONLY THE SERVER will be aware of new datablock and creating objects with it will crash the clients. I have ascertained (experimentally) that it is possible to create datablocks on the fly after a mission load and then to force them to be transmitted to clients. Again. This is an advanced topic, and I won't be discussing the exact technique until I have verified it works consistently.

27

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 574 Incorrect Path


Bug Credit: Roto Description: Roto notes that the following statement (on page 574 near the top of the page) is incorrect:

he 3rd bulleted item reads" "\LessonKit". This directory contains the

Correction: Instead, this should read:

he 3rd bulleted item reads" "\GPGT LessonKit". This directory contains the

28

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 576 Confusing Directions Section 14.4.4


Bug Credit: Roto Description: Roto noted that the instructions about updating main.cs are ambiguous. Specifically, there is the possibility of confusing main.cs in our game's root directory with the one in the main.cs in the prototype directory. Clarification: In the pages prior to section 14.4.4 we have done the following: 1. Installed a fresh copy of the Torque Game Engine Demo. Let's assume we installed it in C:\TGE. 2. Copied the entire installation to a new directory. In my instructions, I said that we could call it: C:\MazeRunner. 3. Wrote some cleanup scripts. 4. Copied the mod directory from in the included disk. Assuming that our CD drive is Z:, in section 14.4.3, we would copy Z:\MazeRunner\A_SettingUp\prototype into C:\MazeRunner. At the end of step 4 (above) we will have something like this structure on our C:\ drive: C:\MazeRunner | |--main.cs (edit this main.cs while reading 14.4.4) | |--\prototype | |--main.cs In section 14.4.4 on page 576, I say to edit main.cs and change: $defaultGame = demo; to $defaultGame = prototype; The main.cs file you should be edit is C:\MazeRunner\main.cs.

29

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Page 576 Incorrect Filename


Bug Credit: Roto Description: Roto notes that the filename provided for the simple task manager is incorrect: In the book, it is: exec("./EGSystems/SimpleTaskMgr/egs_SimpleTaskMgr.cs"); , when it should be: exec("./EGSystems/SimpleTaskManager/egs_SimpleTaskManager.cs");

30

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

GPGT FAQ as of 21 JUL 2006

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Table of Contents
Question Question Question Question Question Question Question Question #1 #2 #3 #4 #5 #6 #7 #8 Why won't XYZ run?...................................................................................................... 3 What do I do if I have a problem with my CD?................................................................... 4 Why Do GUI's Move When Editing?.................................................................................... 5 Which Executable Do I Run?............................................................................................. 9 Why Doesn't The Tilde (~) Key Work In The 'Sample Script Console'?................................. 11 Why Doesn't The 3D Lesson Lightning Stop?..................................................................... 13 Why Can't I Manually Add A 'BoxHover' Vehicle................................................................. 14 Why Does The 3D Lesson Sound Stop?............................................................................. 16

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #1 Why won't XYZ run?


Question Credit: General Problem Description: This is a pretty easy mistake to make. Many people have tried to run the GPGT lesson kit or one of the other applications directly off of the CD. Problem Resolution: Copy the entire contents of the CD to your hard-drive. Now, run the applications there. This is necessary because the applications (GPGT Lesson Kit and Maze Runner Advanced) need to create DSO files, which they cannot do on the CD.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #2 What do I do if I have a problem with my CD?


Question Credit: General Description: I have received a handful of direct e-mails from individuals who either did not get a disk with their books, or when the did get a disk, the disk was damaged. Note: This has only happened for 0.3% of all books, that is about 3 in 1000 book/cd combos have a problem. Nonetheless, if you are one of those 3 in 1000 people, this can be really frustrating and disappointing. Problem Resolution: AK Peters has been very good about resolving this problem. Simple visit this webpage: http://www.akpeters.com/contactus.asp Describe your problem by giving this information: 1. Product The Game Programmer's Guide to Torque (ISBN 1-56881-284-1) 2. Problem Missing or Damaged Disk. 3. e-mail contact Info: your_email_address 4. mailing address: address to mail replacement CD to. Now, submit! Depending on where you are, you should receive a new disk in about a week or less. If you encounter ANY problems in this process, please feel free to contact me via e-mail at: [email protected]

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #3 Why Do GUI's Move When Editing?


Question Credit: Matthew Johnson Issue Description: Matt has noticed that sometime while previewing his work GUI controls will shift. He has further noticed that this is true when he uses a horizSizing and/or vertSizing of 'relative'. The specific problem Matt is seeing is this: 1. Open an interface using the GUI Editor (press F10). 2. Manipulate a control that uses 'relative' sizing or change the control to use this sizing method. 3. Preview your work by pressing F10. 4. Open the editor and edit some more. 5. Repeat steps 3 and 4. At some time in this editing process, you may see that controls that use horizSizing are moving away from the position they were originally placed at. So, what is going on here? Issue Cause: What you're seeing is the effect of floating-point to integer rounding errors. C programmers will recall that when a floating point value is stuffed into an integer, the entire mantissa is dropped. It is isn't rounded, it is just dropped. As of 1.4, TGE doesn't account for this dropping vs. rounding behavior when scaling the integer values of pixel sizes/positions. Let me elaborate further in case the above statement is unclear. Below I'll talk in general about what is happening and the provide some solutions. Parent Controls Resize While Editing/Previewing When editing interfaces, we often use one resolution for our game/session. Meanwhile, we run the GUI editor at a lower resolution. For example, I often run TGE at 1024 x 768 and edit my GUIs at 800 x 600. So, when I start the GUI Editor, the interface I am editing is resized. Then, when I preview, the interface is resized again. Pixel Size/Position Truncation Errors When a parent control is resized it has the job of resizing and repositioning all of its children. If you are using a HorizSizing and/or VertSizing of relative, you may experience truncation errors that cause controls to translate and resize every time you rescale. To see this 'in action', let us do a hypothetical example.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Example Bad Resizing/Repositioning In our example, the following things are true:

Our interface consists of a button (GuiButtonCtrl) contained in a GuiControl. The button is positioned at < 100 , 100 > The button size is < 50 , 18 > The button has a HorizSizing and a VertSizing of 'relative'. The TGE application is being run at 1024 x 768. The interface will be edited at 800 x 600.

In the first step of this example, we will (hypothetically) start the GUI Editor by pressing F10. This will cause the parent control and therefore our button to be resized. It will also cause the button to be repositioned. Because the factor between these two resolutions when reducing resolution is 0.78125, the engine will calculate size and position as follows:

Calculated Size: 0.78125 * < 50 , 18 > == < 39.0625 , 14.0625 > Calculated Position: 0.78125 * < 100 , 100 > == < 78.125 , 78.125 >

Because stuffing floating-point values into integers causes the mantissas to be truncated, the actual values we will get are:

Truncated Size: < 39 , 14 > Truncated Position: < 78 , 78 >

Fine, this probably won't be all that noticeable. However, when we turn off the editor by pressing F10, the button is going to be resized and repositioned again. This time, the multiplying factor will be 1.28:

Truncated Size: 1.28 * < 39 , 14 > == < 50 , 18 > Truncated Position: 1.28 * < 78 , 78 > == < 99 , 99 >

Hey, what the heck is going on here? It looks like we recovered our previous size, but lost a pixel off the horizontal and vertical position. In fact, what has occurred is a truncating error. If we were to switch back and forth a couple more times (between editing and previewing), we would find that we lost a pixel off horizontal and vertical positions each time: < 98 , 98 >, < 97 , 97 >, ...

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Can We Fix This? So, the questions that arise are, Is there a way to fix this problem?, and, Are there save pixel sizes and positions? The answer to both of these questions is a definitive, Yes! Fix Without Engine Changes If we decide to fix this problem without modifying the engine, we simply have to find pixel values that will not experience rounding issues when we toggle back and forth between two resolutions. That is, we need to choose a viewing resolution and and editing resolution and then consistently use these resolutions while we edit our interfaces. Then, armed with a list of X and Y pixel values that will not be affected by rounding errors, we can edit to our heart's content. The first step is to determine which resolutions are best for viewing and editing. To do this we will calculate the scaling factors (both up and down) for the most commonly used resolutions: 640 x 480, 800 x 600, 1024 x 768, and 1280 x 1024. This first table shows the X resolutions and the factor by which values are scaled from any one X resolution to another. The values were obtained by dividing columns by rows. Pixel Size 640 800 1024 1280 640 1 0.8 0.625 0.5 800 1.25 1 0.78125 0.625 1024 1.6 1.28 1 0.8 1280 2 0.5 1.25 1

This second table shows the Y resolutions and the factor by which values are scaled from any one Y resolution to another. The values were obtained by dividing columns by rows. Pixel Size 480 600 768 1024 480 1 0.8 0.625 0.46875 600 1.25 1 0.78125 0.5859375 768 1.6 1.28 1 0.75 1024 2.133... 1.7066... 1.33... 1

As should be readily apparent, the lowest and highest resolutions will not be appropriate because they produce ratios that will obviously not produce many values that do not round.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

So, having only two resolutions left, we shall view our work at 1024 x 768 and edit at 800 x 600. Now, we need to find a list of numbers between (0 and 800) that will not experience rounding errors when multiplied by 1.28 and then by 0.78125. The easiest way to do this is to simply write a script to do it for us:
function bfValueListNoFix( %max , %upFactor , %downFactor ) { for( %count = 0 ; %count <= %max; %count++) { %up = mFloor( %count * %upFactor ); %down = mFloor( %up * %downFactor ); if( %count == %down) %valueList = %valueList SPC %count; } echo( %valueList ); }

If we run the above code with these arguments:


bfValueListNoFix( 800 , 1.28 , 0.78125 );

, we will get this list of values:


0 25 50 75 100 125 150 175 200 225 250 275 300 325 350 375 400 425 450 475 500 525 550 575 600 625 650 675 700 725 750 775 800

We can safely use any of these values for either a control's size and/or position and it will not migrate while we are editing and previewing using a previewing resolution of 1024 x 768 and an editing resolution of 800 x 600. Hmm... You might be thinking that this isn't all that great. i.e., You'd rather have a more robust solution. Well, to get that solution you'll have to edit the engine. Fix With Engine Changes The better solution to this problem is to, fix problem at its root. Because I cannot post source-code in a public purview (EULA restrictions), you can find a complete description of the engine fix in the TGE private forums here: http://www.garagegames.com/mg/forums/result.thread.php?qt=42489

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #4 Which Executable Do I Run?


Question Credit: Roto Issue Description: Roto posts:

I am frustrated at the errors/glossing over important steps in the book. ... I have found a few paths in the books that do not exist on the CD. ... Right off the bat, Step #2. It does not tell you how to start the Maze Runner Mission. Which file do I double click to start this mission, or do I load something and then search for this mission, which by the way is close to impossible to find since many of the same name exist.

As the author, I hear you and agree with your frustration. That said, let's see if I can't help clarify a few things. Issue Cause: This book was written to cover a lot of ground. It was designed to do all of the following: 1. Teach new users how to use Torque. 2. Teach any user how to write a game (Maze Runner) using Torque. 3. Act as a reference (printed material and e-appendicies). 4. Provide samples and examples of (almost) all topics discussed (GPGT Lesson Kit). While attempting to cover all this ground, I will have your run either the GPGT lesson kit, or your Maze Runner prototype. However, sometimes it is very unclear which one should be run. What is needed is a rule of thumb for using this book and the accompanying materials.

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Rules Of Thumb: #1 WHEN TO USE Maze Runner PROTOTYPE If you are examining numbered lesson #1 through #21, you should always be running the game prototype we set up (following the directions in section 14.4). #2 WHEN TO USE GPGT If you are reading through any of the printed material (besides numbered lessons), use the GPGT lesson kit. #3 WHEN TO USE STARTER.FPS Never. At one time, this book used the starter.fps for all of its lessons, and discussions, but I abandoned it because the terrains was changing under my feet. Thus I created the GPGT lesson kit instead. Certainly, you can use the starter.fps on occasion and many examples will work there, but while you are following samples in the book, it is suggested that you follow rules #1 and #2 above. Before Starting The Numbered Lessons During the course of this book you may optionally create a game (Maze Runner). However before you can do this, you need follow all of the directions in section 14.4 Setting Up Or Workspace, starting on page 574 and ending on page 577. If you have not created a working area for your prototype, none of the numbered lessons will work. A Suggestion (For Very New Users) I strongly suggest, if you are new to Torque, that you read the book first and SKIP all of the numbered lessons. Then, when you feel ready, go back and do them in this order:

Set-up Steps (Section 14.4 pages 574 577) #1 ... #21

I suggest this, because in the lessons we apply skills we just discussed in a specific chapter. To be fair, using these skills may require a bit of time, so reading the book, goofing around a bit, and then doing the lessons will probably be easier. A Second Suggestion If you are doing the Maze Runner Lessons, you may find it much easier to print the file: \Appendicies\Appendix C - Combined Maze Runner Lessons.pdf This file (found on the accompanying disk) contains all of the steps you need to follow IN ORDER to create the Maze Runner prototype.

10

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #5 Why Doesn't The Tilde (~) Key Work In The 'Sample Script Console'?
Question Credit: Mark Barner Description: Mark asks, ... when you hit "~" [it] sends you into the console mode.... Is there a special way to enter the "~" in without it going into the console mode?. Resolution: The tilde (~) key is tied to the console function 'toggleConsole()'. It is the job of this function to open and close the console window. Furthermore, when the tilde (~) key is bound to an action, both the tilde (~) key and the grave (`) key produce the same action. i.e., We lose the grave (`) key when we bind the tilde (~) key. To resolve this issue, you can simply do the following: 1. Open the file /gpgt/client/interfaces/SampleScriptConsole/SampleScriptConsole.cs 2. Page down to the end of this file and you will see an implementation of 'onWake()':
function SampleScriptConsole::onWake( %theControl ) { //ssConsole.position = "2 2"; //ssConsole.extent= "800 800"; } // cls();

As can be seen, I was experimenting and didn't clean up after myself. Tsk...tsk... Well, it doesn't matter, because our next step will wipe this all away. 3. Remove the old onWake() implementation. Yes, delete it. 4. Add these two code snippets:
function SampleScriptConsole::onWake( %theControl ) { GlobalActionMap.unbind(keyboard, "tilde"); } function SampleScriptConsole::onSleep( %theControl ) { GlobalActionMap.bind(keyboard, "tilde", toggleConsole); }

5. Save your changes. 6. Delete all .dso files. 7. Test the change (see 'Testing The Change') below.

11

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

What we have done here is to temporarily remove the binding for the tilde(~) key whenever the SampleScriptConsole tool is open. Then, when the tool gets closed, we simply re-add the binding. If you're curious, the original binding is done in the file 'default.bind.cs'. Testing The Change: Now, the tool should work as expected. To test if the fix is working, you can do the following: 1. Add a file named /gpgt/test.cs with this contents:
echo(it works!);

2. Open the 'GPGT Lesson Kit' and run the 'Sample Script Console Tool'. 3. In the left window, type: echo( ~/test.cs); 4. Click on the 'cls()' button. 5. Click on the 'exec()' button. 6. Viola!

12

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #6 Why Doesn't The 3D Lesson Lightning Stop?


Question Credit: Mark Barner Description: Mark is very fast. He noted this problem before I had time to release the updated lesson. Basically, this lesson, once selected, does not stop. The lightning continues the run in all lessons thereafter. Problem: I modified this lesson about a day before we shipped the book off for printing and did not add the correct cleanup code in the lesson. The lesson manager should delete all 'objects' associated with the lesson on exit, but I did not record the ID of the lightning object and thus the manager cannot delete it. Resolution: We need to delete the current lightning object when the lesson is shutdown (deleted). To do this, we will make the following change to the lesson: 1. Open the file . 2. Find the function 'LightningLesson::onRemove()'. Note that the body is empty. 3. Modify the body of the function to look like this:
function LightningLesson::onRemove(%this) { if( isObject(LightningLesson.CurrentLightning) ) { LightningLesson.CurrentLightning.delete(); } }

4. Save your changes. 5. Delete all .dso files. 6. Re-run the 'GPGT Lesson Kit' and test the 3D Lesson. We have simply forced the 'CurrentLighting' object to be deleted when the lesson is deleted. Easy.

13

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #7 Why Can't I Manually Add A 'BoxHover' Vehicle


Question Credit: Mark Barner Description: Mark has found that you cannot manually add a 'BoxHover' Vehicle using the Creator. Problem: Before spelling out the error, let me help you (the reader) learn how to fish. i.e., How to debug this kind of problem. Because this kind of problem is commonly encountered when adding new 'creatable' objects, let's figure out my mistake together. 1. Run the 'GPGT Lesson Kit' 2. Click on the 'Start Mission...' button. 3. Click on the 'Launch Mission' button. 4. Wait for the mission to load. 5. Press F11 followed by F4 to start the Creator. 6. Open the console and type: cls(); 7. Close the console. 8. Attempt to add a 'BoxHover' (found under Shapes->baseVehicles). 9. Open the console: You should see a message like this:
gpgt/server/scripts/GPGTBase/Vehicles/HoverVehicle/HoverVehicleDataMethods.cs (16): Register object failed for object (null) of class HoverVehicle.

This message is telling us that the script HoverVehicleDataMethods.cs has some bad code it in. Furthermore, this code tried to register an object with an ID of 0. Huh? OK, in English: 1. There is a bug in the script file. 2. This particular bug results from a failed call to new. OK, let's investigate further. 1. Open the file \gpgt\server\scripts\GPGTBase\Vehicles\HoverVehicle\HoverVehicleDataMethods.cs 2. Examine the code and you should see this:

14

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

function HoverVehicleData::create(%HoverVehicleDB) { %obj = new HoverVehicle() { dataHoverVehicleDB = %HoverVehicleDB; }; %obj.mountable = true; return(%obj);

It may not be obvious at first, but the line in bold should have the word 'datablock' in front of the assignment operator. This function is supposed to create a new hovervehicle object and initialize it using the datablock that is passed in. This initialization is accomplished by assigning the ID of the datablock to the object's datablock field. As you can see I must have typoed this code, or replaced it with a global search-and-replace. Resolution: To resolve this problem, simply change your code to look like this:
function HoverVehicleData::create(%HoverVehicleDB) { %obj = new HoverVehicle() { datablock = %HoverVehicleDB; }; %obj.mountable = true; return(%obj);

15

Copyright 2000-2006 GarageGames

Product of Hall Of Worlds, LLC.

Question #8 Why Does The 3D Lesson Sound Stop?


Question Credit: Rex Description: Rex noted that when you run the 'ShapeBase Sounds' 3D lesson from the GPGT lesson kit, that switching back and forth between sounds does not work. Specifically, when you start the 'ShapeBase Sound' 3D Lesson, you are presented with this dialog:

However, if one follows the directions and presses 2 followed by pressing a 1, suddenly the sound stops playing. Correction: This is on my TBD list. I have not yet determined if this is an engine bug or (more likely) a bug in my lesson. I'll update this answer when I have a resolution. (02 MAY 2006 TBD)

16

http://www.garagegames.com

http://www.hallofworlds.com

Synopsis.....................................................................................................................................................3 History Of GPGT..................................................................................................................................... 4 Like Many Of You.................................................................................................................. 4 Pre-Indie Indies ...................................................................................................................... 4 Options: 1. Make a Game Engine 2. Make a Game Choose One....................................4 GarageGames Breaks The Mold............................................................................................. 5 Torque Notes Is Born.............................................................................................................. 7 What About A Book?..............................................................................................................8 Essential Guide to the Torque Game Engine Announced................................................... 9 EGTGE Hits 350 Pages...........................................................................................................9 Ken (Finney) Releases His Book............................................................................................ 9 You're Not Going To Get Rich..........................................................................................10 Draft Version Completed...................................................................................................... 11 GPGT Jam Session #1...........................................................................................................11 GPGT Jam Session #2...........................................................................................................12 Please Make It Stop!............................................................................................................. 12 First Printing..........................................................................................................................13 Summary Of Effort................................................................................................................14 Mission Accomplished?........................................................................................................ 14 Project Analysis .....................................................................................................................................15 Project Data........................................................................................................................... 15 Project Ratings 1 (poor) to 10 (outstanding).........................................................................18 Project Questions...................................................................................................................19 What went right? ............................................................................................................. 19 What went wrong?............................................................................................................20 Lessons Learned..................................................................................................................................... 22 Limit Your Scope.................................................................................................................. 22 You're Not Going To Get Rich............................................................................................. 22 Collaborate Using a Private Website.................................................................................... 22 Create Your Own Base 'Kit'.................................................................................................. 23 GPGT Lesson Kit ................................................................................................................. 23 Know What You're Going To Write Before You Write It. #1..............................................23 Know What You're Going To Write Before You Write It. #2..............................................23 Stick To Your Plan................................................................................................................23 Know Your Tools..................................................................................................................24 About The Table Of Contents (TOC)................................................................................... 24 About The Index....................................................................................................................24 Use Test Readers From The Start......................................................................................... 25 About Art and Assets............................................................................................................ 25

http://www.garagegames.com

http://www.hallofworlds.com

Synopsis

Part I

After much deliberation, I have decided to ignore the trend to write a postmortem using the standard 3-3-3 format (three things that went wrong, three things that went right, and three things learned). So, consider yourself warned, this postmortem is a little long. I am writing this postmortem for the community and for myself. It is my hope that those who read it will be able to take away some useful bits of information. This postmortem contains the following three sections: 1. History Of GPGT In this first section of the postmortem I traverse the entire history of this project, highlighting the important events that occurred along the way. The purpose of this is to show how a whim and a hobby can turn into something more serious. 2. Project Analysis In this second section of the postmortem I will do a detailed analysis of the project, providing a more formal look at the different parts of writing this book. I will discuss such details as schedule, budget, tools, etc. 3. Lessons Learned In this third and final section I examine some specific things that I learned along the way.

http://www.garagegames.com

http://www.hallofworlds.com

History Of GPGT

Part II

The history of The Game Programmer's Guide to Torque is both a history of the book and to a large extent, a history of my time (thus far) as an independent game developer. Like Many Of You Like many of you, I have played computer games most of my life, and also like many of you, I played around writing my own 'games' of sorts. Up until about 1996, the idea of making games was nothing more than a dream and I pursued it like a hobby. Pre-Indie Indies In 1996, I had just finished my degree in Computer Engineering and had also gotten a great job at Intel Corp. Jerry Shaw, a prior classmate of mine, was also hired by Intel. We ended up working together and formed a friendship. Jerry would end up being my cohort in crime for many years to come. Amazingly, we were both of like mind when it came to making games. So, we decided to team up in our spare time and to work seriously on a 3D game engine. Once we 'knocked that out', we would move on to making games. Using our combined programming skills, the results of much research (thanks NeHe, GameDev, GamaSutra, Romaka, Sulaco, OpenGL Org, Flipcode, et cetera!), and a little ingenuity, we quickly cobbled together our first attempt at a 3D game engine. Our first attempt was fine as a tech demo, but it wasn't a real game engine. We accepted this and decided to try again. Options: 1. Make a Game Engine 2. Make a Game Choose One. Having learned much from our first attempt at developing a 3D game engine, we started over again on Nascent. This, we decided, would be our first 'serious' attempt at a complete 3D game engine. Work on Nascent went on for about a year. We met every 2 or 3 weeks (when we could spare the time), compared our latest advancements, and assigned each other new tasks to work on. The project went well and feature after feature was added to the engine. Wedgie Madness!

http://www.garagegames.com

http://www.hallofworlds.com

At the end of this effort, we had an engine with the following features:

A fully hierarchical set of game classes. An embedded TCL scripting engine with full access to the C++ core components. A ROAM based terrain engine, utilizing multi-texturing and pseudo bump-mapping. Rudimentary game recording and playback. Support for scripted shape rendering (i.e. defined in script), billboard rendering, and full Quake 2 style shape rendering and animation. A multi-pass collision detection and response system. A limited Physics Engine. Hierarchical view-culling. Dynamic render scheduling. Multiple camera support. Etc.

However, we realized that there was still a long ways to go before we could start making a game. Nascent still needed a ton of features. Inevitably, this led us to a very significant conclusion:

You can write a game engine, or you can make a game, but you can't do both (with a small team).
Decidedly dejected and plain tired of the entire effort, we both returned to our careers and lives, letting Nascent die a quiet death. We did however continue to play games and quietly aspire to write our own. GarageGames Breaks The Mold In 2001, still working at Intel and still playing games (after work of course), my current favorite game was Tribes 2. After passing the 100 hour mark playing this addictive game, I learned that the engine used to make this this very cool game had been released under an extremely relaxed and inexpensive license. So, in August of 2001, I signed up and bought a license to the V12 engine.

http://www.garagegames.com

http://www.hallofworlds.com

At this time, the V12 engine (AKA Torque) was quite unique. Certainly, there were other engines available but none of them had V12's combination of history, features, and price. Also, none of them had the stellar and dedicated GarageGames crew behind them. After getting my copy of the engine, I quickly jumped into the demo and examined the current docs. I was both excited and somewhat daunted. This thing was huge! Now, I had worked on projects of this size before, but still, wrapping my head around the engine and grokking it was no mean feat. I think perhaps that this summarized excerpt from the original 'Known Issues' disclaimer says it best:

.... This is not for the timid. GarageGames did not receive any documentation ... you will have to hone your code perusing skills. In the process of removing the Tribes2 intellectual property ... functionality was impaired

In addition to the exceptions noted in the original disclaimer, V12 did not come with all of the tools/exporters that we all take for granted now. For me, the real clincher was the lack of a Milkshape Exporter. Being a 'doit-it-on-the-cheap' guy, I didn't have 3DS Max and could not see myself spending that kind of money on what was still, only a hobby.

http://www.garagegames.com

http://www.hallofworlds.com

V12 In All Its Glory! I quickly realized that I didn't have the time to work with V12 in its current state. So, I put it down for about a year. Torque Notes Is Born In early 2002, I came back to the GarageGames site to discover some great things. Wow! These guys had really made some progress:

Torque was up to release 1_1_2, The site had forums, resources, member home pages, a cool newsletter, and There was DOCUMENTATION.

This is more like it, I thought to myself. Jumping in head first, I read through the docs, downloaded release 1_1_2, and set to work on a game. I started work on an MMO* and learned another valuable lesson. Start small. My game idea was way too big for one guy. However, it did force me to learn a lot about Torque.

http://www.garagegames.com

http://www.hallofworlds.com

While I worked on my game idea, I quickly exhausted the easily located documentation and started to read thread after thread, digging deeper and deeper to find information. Eventually, I had compiled a pretty big collection of notes and Torque related links. So, I decided to share this information with the rest of the community in the form of a monolithic web page. Thus, 'Torque Notes' was born.

http://www.hallofworlds.com/pages/Torque/TorqueNotes

What About A Book? Torque Notes was an instant success, getting 1000s of unique hits every month and quickly delivering folks to topics they were seeking. However, the page began to sprawl and to become very dated. In fact, it soon became completely unmanageable. So, I made a hard choice and stopped maintaining it. Interestingly, by this time, a new thought was starting to tickle my brain,

Hmmm, there might be a book in this somewhere.

http://www.garagegames.com

http://www.hallofworlds.com

After thinking about this for some time, I decided that some kind of short reference would be a great idea. I figured,

A 100 page quick reference ought to be real popular.

Essential Guide to the Torque Game Engine Announced In September 2002 I officially announced that I was writing a reference guide for the engine and gave it the working title: Essential Guide to the Torque Game Engine (EGTGE). Nobody took any notice, but I was inspired and that was enough.

EGTGE Hits 350 Pages March 2003 came and went. EGTGE hit and passed the 350 page mark. At this time, I was still thinking that I would be done soon. The work continued. In fact, the rest of 2002 and a good part of 2003 came and went, and the work still continued.

Hmmm. This is harder than I thought it would be.

Ken (Finney) Releases His Book In May of 2004, Ken Finney's book 3D Game Programming All In One was released. In my mind, this effectively quashed my plans for EGTGE. Severely disappointed, I released the lite-edition of EGTGE as a community resource and called it quits. I took a long delayed vacation, thinking that it was time to move onto a new project. But, I couldn't have been more wrong. It seems, I had not accounted for the fact that Torque Notes, and the promise of EGTGE had started to draw a following. http://www.garagegames.com 9 http://www.hallofworlds.com

The community response was rapid and overwhelming. People took turns, either lambasting me or encouraging me, but the gist of the message was the same from everyone:

Keep working on your book!.

A few days later, GarageGames contacted me and offered to publish my book if I would just finish it. You're Not Going To Get Rich September 2004 came rolling along. The book was at the 824 page mark. Somewhere along the way, Josh and I had decided that the book would need to be printed in two volumes. Great! Now, I had two books to finish. This was getting out of hand. It felt like it should be done, but I didn't feel like everything was in place yet. Soon, IGC'04 arrived and it was time to head to Eugene. During the conference, Jeff Tunnell and I huddled up for a talk about EGTGE. I no longer remember everything that was said, but one thing did stick. Jeff told me (paraphrased):

Ed, you're not going to get rich making this book. So, just finish it up and get it out.
This was excellent advise. I just didn't I know how to follow it. At the time, I was seriously wondering how I would ever finish the book, that is, the books.

http://www.garagegames.com

10

http://www.hallofworlds.com

Draft Version Completed In May 2005, I finished the draft version of the GPGT volume 1. I also had much of GPGT volume 2 ready to go. In total, I had accumulated:

Approximately 1400 pages of written material. A finished single-player game prototype. A kit containing 24 GUI samples, 8 functionally complete interface samples, 3 example HUDs, 23 3D lessons, ...

I turned this in to GarageGames and they sent it to AK Peters. GPGT Jam Session #1 By mid-September 2005, AK Peters had reviewed the draft and was asking for some changes. So, I hopped in my car and drove to Eugene to participate in a copy-editing and writing marathon (otherwise know as GPGT jam session #1). Over Saturday September 10th and part of the 11th, Josh Williams, Ben Garney, Matt Fairfax, and I read and reviewed the entire guide. We also discussed the various changes that AK Peters had requested. Basically the way this worked was that everyone got a chapter and read it. Then, they sat down with me (and sometimes with Josh) to review any issues and to have me answer any questions about questionable sections. When we got done with the session I had the following list of things to do:

Incorporate fixes for all 14 chapters. Replace the original 'Torque Man' (a 3D Pac-Man clone) with 'Maze Runner', thereby avoiding any possibility of legal issues with Namco Limited. Remove the game prototype creation chapter and instead append 'lessons' to the end of chapters, thereby building the game as the reader progresses through the guide. Put together a description to go on the back cover. Incorporate professional art for a finished version of the game (AKA Maze Runner Advanced).

http://www.garagegames.com

11

http://www.hallofworlds.com

All of this work needed to be done in about a week. So, I drove home and got to work immediately. Josh did me a huge favor and wrote the back-cover text. Then, he hooked me up with Christophe Canon of FroGames to put the pro-art into Maze Runner Advanced.

Note: Christophe was a real pleasure to work with.

http://www.frogames.net

GPGT Jam Session #2 Exactly three months later, in mid-December 2005, AK Peters had re-examined the book. As a result of their re-review, they supplied a new list of things that needed fixing, and they sent a hard-copy with hand written notations back to GarageGames for the GarageGames staff and I to update. It was time for another jam session. Over Saturday December 10th and most of the 11th, Josh Williams, Justin DuJardin, Ben Garney, Matt Langley, Jay Moore, Julie Moore, and I did another complete review. It is worth noting that this sessions was a little different than our prior one. We had no schedule leeway for completing the fixes. They all needed to be in by the following Monday to stay on schedule. It was painful, but we were successful! Please Make It Stop! After the second Jam session, I thought AK Peters would be taking over and I would get some time to breath. Wrong! In the period between December 27th 2005 and February 3rd 2006, AK Peters did the final review and layout work. Josh and I ended up being heavily involved with this process. The way this worked was as follows: 1. Josh would receive the 'current chapter' from AK Peters and forward it to me. This chapter came in layout form. That is, it now closely approximated the final look. In addition to the chapter, we also received a list of 20 to 40 questions and comments from the copy-editors and layout staff. 2. Josh and I then (independently) re-read the entire chapter, looking for any errors or technical issues. 3. Next, I would handle all of the questions/requests that came with the chapter. These were usually such things as:

Spelling Questions 12 http://www.hallofworlds.com

http://www.garagegames.com

Missing Table/Image Captions Path Questions Illustration Revision Request

4. Once I was done writing all of my own corrections down, providing responses to the questions and comments, and re-doing any artwork, I shipped all this to Josh. 5. Lastly, Josh would packages everything up, adding his own inputs, and ship it off to AK Peters. This went on for the next 39 days. During this time, I re-read every chapter at least once, I updated or re-createe 373 images, I wrote between 18,000 and 20,000 words of feedback in the form of 898 unique fixes and responses to questions, and I created an Index through a combination of concordance files and a whole lot of hand-editing. In short, this experience was enlightening, but exhausting.

First Printing In March 2006, AK Peters ran the first printing and took 90 copies with them to GDC '06. They sold out. Amazon, Barnes & Noble, GarageGames, and AK Peters all started taking and filling orders. Over the next three some odd months, the Amazon Ranking would look like this:

http://www.garagegames.com

13

http://www.hallofworlds.com

Summary Of Effort Over the past four years, I have spent about 2500 hours working on this book. In retrospect, I am somewhat surprised to find that writing was only about half of the work. Besides writing, I did all of the following tasks:

Learned the engine thoroughly, Found and fixed engine bugs, Wrote tens-of-thousands of lines of script and C++ code Created well over 1000 unique art assets (mostly illustrations, but also textures for the GPGT lesson kit and the game prototype), Designed and wrote two complete single-player games, and Participated in review and proofing sessions till I was ready to cry.

At the end of the day, I believe that the effort was worthwhile. In addition to working with lots of wonderful people in the community, I also got to know the staff of GarageGames much better. GarageGames, the community, and all three of the Torque engines rock! Mission Accomplished? The bold among you may ask,

So, was Jeff right, or did you get rich?

Yes, Jeff was right, and No, I did not get rich. However, I didn't start this project with that in mind. My original motivations for writing this book were: 1. Help grow the Torque community, 2. Help others so they won't have to go through the growing pains I had to go through, 3. Get my own company started. i.e. Build some recognition for myself and hopefully for the Hall Of Worlds Logo. 4. Hopefully make some money, I accomplished #4, but did I accomplish the other goals? You tell me. http://www.garagegames.com 14 http://www.hallofworlds.com

Project Analysis
Project Data

Part III

Project Name: The Game Programmer's Guide to Torque (GPGT) Design Overview: Originally, GPGT, known first as the Essential Guide to the Torque Game Engine (EGTGE), was intended to be a short (~100 page) quick reference discussing some tools, scripting, and a few tips and tricks. I intended to have two versions:

EGTGE Lite (a free version used as a 'loss leader') EGTGE Full (a for pay version)

will be a multi-month project, culminating in a guide containing documentation on many significant Torque topics with full references and many accompanying examples. The guide will be fully illustrated and come with an accompanying resource disk. Currently, I have not decided if this will be printed or an e-book."
- Original EGTGE Description From Torque Notes Page

"This

Intended Customer: New Game Developers, Veteran Torque Users, Teachers and Students. Product's Purpose: Teach folks how to use Torque to make games and other products. Project Start / Finish Dates (Estimated): I officially started writing in November of 2002. I estimated I would be done between June and August of 2003. Project Start / Finish Dates (Actual): I did in fact start November 2002, but the book did not actually go to press until March of 2006.

http://www.garagegames.com

15

http://www.hallofworlds.com

Budget (Estimated): None. Budget (Actual) : By the end of the project I ended up making several purchases related to the book, coming to a grand total of about $2000 dollars. Release Date: The official (bookstore) release was May 2006. Release Format: Soft cover (600 pp) with accompanying CD containing 580 (pp) reference, samples, and a lesson kit. The kit was designed to run on all Windows platforms, OSX, and in a degraded mode on Linux systems. Lines of Code: I ended up writing/generating about 42,000 lines of new script. Art Assets: > 1000 unique pieces Development Software

Windows 2000 Torque Game Engine Torque Show Tool Open Office versions 1.1 through 2.0 Beta. Visual Studio 7 Paint Shop Pro 7, 8, 9 Various Flaming Pear Filters Milkshape GameSpace Unwrap 3D Audacity Fraps Hammer

http://www.garagegames.com

16

http://www.hallofworlds.com

Development Hardware:

1.8 GHz Pentium 4 with 1GB memory and a late model nVidia video card. Laser Printer

Other Development Materials:

Book: Books, Typography, and Microsoft Word: How to Get High-Quality Type for Desktop Book Publishing, Self Publishing, and Print on Demand, or Tips on Type Book: Print-On-Demand Book Publishing: A New Approach to Printing and Marketing Books for Publishers and Authors Book: The Self-Publishing Manual

Known Bugs: About 50 errata have been discovered to date. More details can be found here: http://gamers.hallofworlds.com/support/gpgt/ , and here: http://www.garagegames.com/mg/forums/result.forum.php?qf=178 Known Compatibility Issues: There were recently some issues on OSX systems. These have been resolved. Team Members and Responsibilities:

Edward F. Maurina III Writer Teresa Tse Business Manager

http://www.garagegames.com

17

http://www.hallofworlds.com

Project Ratings 1 (poor) to 10 (outstanding)

How well did the project achieve its goals? 8

I wanted to cover more in volume 1 than was possible due to page constraints.

Was the project developed in a reasonable amount of time? 3

No. I was way over estimate.

Was this a cost-effective project? 7

I'm still in the negative, but will recover my expenses eventually when my royalty check comes in. To be completely realistic, if this project were only about making money, then the rating would be a 1. Why? Well, if I considered my time worth only $50/hr (which is low), then 2500 hours x $50 comes to $125,000. It is highly unlikely that I will earn that kind of money from this book. Fortunately, money wasn't my primary motivation.

Did development proceed smoothly? 6

The project had many jumps and starts, as well as a few complete stops. It was an enlightening but rough ride.

How well did the project meet its estimated budget? 5

Not at all, but then the original budget was non-existent which was unreasonable and unrealistic.

How well was change managed during this project? 6

I ended up doing major re-writes of sections of the guide for the following reasons:

Torque 1.2 to Torque 1.3 transition Torque 1.3 to Torque 1.4 transition False start on sample game: 'Torque Man' Writing while learning

http://www.garagegames.com

18

http://www.hallofworlds.com

Project Questions

What went right?


1. I learned a lot about the engine and making games. Remember, there is always more you can know. 2. I learned a lot about writing and publishing a book. This is big understatement, but this will become clear a bit later when I discuss some writing/publishing specific topics. 3. I hit the target (audience) sweet spot. I feel that I hit the right level of usability to enable both new and experienced users to benefit from this book. 4. Well received. The book has been well received and looks as if it may be adopted by some universities and colleges as part of their game development programs. 5. Built reputation. The book and associated efforts have helped build my reputation, however humble it may be, in the gaming community. Note: I joke, but this isn't about humility or vanity, but rather reputation and name recognition. Both of these are valuable commodities if you intend to do business in a distributed environment like the Internet. 6. Is improving Torque awareness. The book has helped and is helping to build further awareness of Torque and the GarageGames community. This is a good thing. For members of the community to have the highest likelihood of success in their endeavors, it is important that they be part of a vibrant community with a constant infusion of new members. The community doesn't necessarily need to grow, but it must not stagnate. The GarageGames community has definitely avoided stagnation. 7. Is helping new users. The book has helped and is helping new users get into using the Torque Game Engine more quickly than would otherwise be possible. Struggling for struggling's sake is no fun.

http://www.garagegames.com

19

http://www.hallofworlds.com

What went wrong?


1. The project failed to meet its estimated time line. This was due to my lack of experience as a writer and because I didn't (initially) treat the project as a job. I didn't create a schedule or a plan until I was well into the project. 2. The project was way over budget. Again, I had no idea when I got into this, just what would be involved in writing a book, and I didn't plan for any expenses. 3. Art in wrong format for printing. All the art had to be massaged into grayscale. Also, by the time I had to do this, several original pieces were lost and and to be reproduced. 4. The first 'sample' game had to be canceled due to poor legal planning (on my part). Originally, I created a Pac-Man like game in 3D. We later decided that although this was probably safe, it would be better to not take chances. So, we changed the game to 'Maze Runner', which is a pseudo-generic 3D platformer. 5. I assumed that AK Peters would 'magically' produce the index for me. Instead I ended up having to write it at the last moment. Therefore, it was written as an, "I thought you were going to do it? Oh crap!, kind of effort. I think this shows in the final version. 6. Failure to back up data. I didn't back up my data properly in the early part of the project and suffered a complete hard-drive failure in early 2004. I ended up having to use a data recovery service to get the book back. It cost me about $300 to retrieve less that 50MB of data. 7. Insidious and silent content corruption. Early on, Open Office was a little buggy. I discovered this during a review when I found data and images both missing and corrupted. Although I stayed with Open Office, I took steps to reduce the likelihood of a repeat of this issue in the future. Among these steps were: upgraded to latest Open Office version, backed up before, after, and hourly while I wrote, checked my documents at the end of the day for any corruption.

Note: After version 1.1 I never experienced this issue again.

http://www.garagegames.com

20

http://www.hallofworlds.com

8. Did not use a spell checker until after the technical review. Because there are so many non-English words (i.e. Datablock names, etc.) in the guide, I turned off the spell checker at an early date and never turned it back on. So, although I spell quite well, we did end up having to find a few misspelled words during copy-edit sessions. This was a big waste of valuable time. 9. Technical Reviews were hard work and somewhat unexpected. I learned that you should not count on the publisher to do the work for you. When it comes to writing Technical books, you have to do all the technical editing yourself. In other words, don't expect copy-editors at a publishing house to be capable of catching technical issues with your book. This is your job. This sounds obvious, but when we started the process of reviewing I was still quite nave about which party would be responsible for what part of the review. 10. Too many chapters to review in too short a time for the final technical review. Although we did get through it and there have been relatively few errata, I could have done better. The primary problem is the fact that I ended up doing a final technical review of an entire chapter along with all fixes and re-submissions of art (sometimes) in a single evening. 11. Last minute changes to directory structure of kit caused some mismatches between book and disk. At the very last moment I discovered that in some cases, a zipped file will not extract properly if the path is too long. So, I quickly renamed a few files and directories, completely failing to recognize that this caused a mismatch with some of the 'Maze Runner' lessons.

http://www.garagegames.com

21

http://www.hallofworlds.com

Lessons Learned

Part IV

This final section will be a free-form list of things that I learned while writing the guide. Limit Your Scope If you are writing a book or other document. Limit your scope from the start and be sure to keep to that limit. I started GPGT as an open-ended project and got bitten by major feature creep. You're Not Going To Get Rich Boy. Jeff was so right. No single book is going to make you rich. The trick is to create a steady flow of products, each of which contributes to your bottom line incrementally. If you can only afford the time to work on one project, and if you need to make your living from that project, then I don't suggest writing a book. You'll probably starve long before you finish it. However, if you can afford the investment of writing a book, then by all means do so. Why? Because, books have the following nice properties:

Relatively long life. If you can refresh a book and publish new revisions, you can benefit from your initial investment of time over the long term. Can Be Turned Into Franchise. No, I don't mean a literal franchise. What I mean is, often written material can be adopted to other purposes. Thus, you may be able to re-use your written material to create other products. Name Recognition. Most people (not all) still tend to recognize that writing a book is no simple task. So, finishing and publishing one cannot hurt your reputation (as long as you do a good job of it), and a good reputation is one of the pillars of success.

Collaborate Using a Private Website Early on in the project I created a private site where I could post the latest versions of chapters and supporting materials. This allowed any reviewers with access to download the materials when they wanted to. It also served as a pseudo-backup mechanism. I ended up having more than one site by the end of the project, each one dedicated to a different set of reviewers/consumers of the draft GPGT materials.

http://www.garagegames.com

22

http://www.hallofworlds.com

Create Your Own Base 'Kit' If you are writing about a programming topic or another topic that requires a starting point, then provide that starting point. In my case, I ended up relying on the 'FPS' Starter Kit as a starting point and this got me into real trouble at the end of the project. This happened because the kit itself changed from version 1.2 to 1.3 to 1.4, AND because there are slight variances in the OSX and Windows versions of the Starter Kit (as supplied in the demo). Hindsight tells me that I should have created my own starter kit and used it exclusively. GPGT Lesson Kit Although I didn't create my own base kit, I did have the GPGT lesson kit. I created this application so that I might create samples in these categories: GUI, Interface, Scripting, and 3D lessons. The kit itself was designed with a tool kit of scripts and predefined lesson file formats. This allowed me to easily create a new lesson and simply drop it into the kit, knowing that it would be properly loaded and displayed to the user. This had the added benefit of allowing me to provide new lessons after printing. Know What You're Going To Write Before You Write It. #1 By this, I simply mean know your topic matter BEFORE you write. I started this project on a whim and while I was still new to the engine. Thus, I ended up re-writing sections later when I learned of new or different engine features which required a different approach to documenting them. In some ways, this actually helped this particular document, but the overall effect was a significant slow-down. Know What You're Going To Write Before You Write It. #2 No, I'm not really repeating myself. This time, I mean you should decide what you want to write about BEFORE you write it. This may sound counter-intuitive at first, but what I am really saying is, to plan your book out before you commit any words to paper. This is not an easy process and you should expect to change your mind about organization later, but if you start without a plan, you will regret it. Stick To Your Plan. I just said that you should expect change and that is still true, but be sure that you only accept small changes. If you make broad changes to your plan, you will likely waste a significant amount of time and effort.

http://www.garagegames.com

23

http://www.hallofworlds.com

Know Your Tools. Be sure to learn how your writing tools work before getting too far into your project. For example, if you are using Open Office, learn how to use Master Documents and Templates. Also learn how the formatting system works. Although I don't encourage to you to pursue final formatting decisions in the beginning of your effort, I do suggest that you set up a custom set of formatting rules in the form of a template and then use that template for all of your documents. Later, when you want to reformat your documents, you can do so by modifying the template instead of manually modifying every chapter. Also, if you intend to use Open Office to generate your Table Of Contents and Index, learn how these features and the concordance import features work BEFORE you start writing. You can certainly do this later, but learning how to set up for an index in advance will save you a ton of time later. About The Table Of Contents (TOC) If you are using Open Office, I do suggest that you use the built in indexes and tables feature to generate your TOC. This feature works perfectly well. One thing though. Be sure to generate them with the 'Protect against manual changes' feature disabled. About The Index Regardless of what tool you are using to write your book I have these suggestions: 1. Do not use an external tool to generate your index or a concordance file. The effort required to do this is equal to or greater than doing it manually. 2. Do not use a concordance file import mechanism. In all but the simplest cases, this will generate a over-sized index that is next to impossible to trim down. 3. Plan for your index as your write. By this, I mean mark keywords and sections for inclusion in your index as your write, or during your final copy-edit session. Either will work, but the prior is best. It is best because when you are writing about a topic you are often in the proper mindset for generating the tag to go with your topic. If you do it later, you will have to ask yourself, When someone is looking for this information, how will they be thinking about it? What is the best way to label this so they can find it? I tried all of the above methods for creating my index and found the latter to be the most sensible. However, because I did create my index last, it suffered for my lack of imagination at the time, and for the extreme time crunch I was under. Believe it or not, it took me over a week to create the current GPGT index.

http://www.garagegames.com

24

http://www.hallofworlds.com

Use Test Readers From The Start I was fortunate enough to have an entire class of test readers (Michael Rogers CS481 class at Millikin University) reviewing my material while trying to use it. This was extremely useful. The bottom line is, there is no way, after reading and re-reading your own materials for the 15th time that you will catch the same errors as a new reader. About Art and Assets I got some good advice from Joe Maruschak and from others while writing my book and while working on the game prototypes that go with it. A summary of this advise would be:

Use placeholder art until the very end. - In other words, do not bother creating superb art assets (or illustrations) until the underlying book/game is done. Why? Because, changes in the book/game may very well require changes to the art, thus wasting effort. Use grayscale for all assets until the asset is finalized. - This is similar to the placeholder art idea, but the differences is that by using grayscale textures/illustrations for all non-finalized assets you can clearly see what parts of your book/game are not done yet, or at least have not made it through final approval. Also, and Joe pointed this out, when you show a demo or prototype and the art is grayscale, folks will be more accepting of changes that come later. i.e. They don't form attachments to specific assets and they expect change.

Lastly, I have discovered that while draft writing, it is best to skip art entirely, and in those cases where a placeholder is absolutely needed, just sketch something by hand, scan it, and import it. Unless you're a whiz artist (which I'm not). Don't even touch your art program(s).

http://www.garagegames.com

25

http://www.hallofworlds.com

You might also like