Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

3D Game Programming All In One (2004)

.pdf
Скачиваний:
139
Добавлен:
17.08.2013
Размер:
17.91 Mб
Скачать

118Chapter 3 3D Programming Concepts

Then it adjusts the scale value by determining if the shape is growing or shrinking. Depending on which way the size is changing, the scale is incremented, unless the scale exceeds the too large or too small limits. When a limit is exceeded, the change direction is reversed.

Next, the scale of the shape is changed to the new values using the %shape.setScale() method for the shape.

Finally, the function sets the item's transform to be the new transform values within the

%shape.setTransform() statement.

The DoAnimTest() function first inserts the new instance of the shape object using the InsertTestShape() function and saves the handle to the new object in the variable %as. It then calls the AnimShape() function, specifying which shape to animate by passing in the handle to the shape as the first argument and also indicating the discrete movement step distance, the discrete rotation angle, and the discrete size change value with the second, third, and fourth arguments.

To use the program, follow these steps:

1.Make sure you've saved the file as 3DGPAi1\CH3\animshape.cs.

2.Run the Chapter 3 demo using the shortcut in the 3DGPAi1 folder.

3.Press the Start button when the demo screen comes up.

4.Make sure you don't move your player-character after it spawns into the game world.

5.Bring up the console window.

6.Type in the following, and press Enter after the semicolon:

exec("CH3/animshape.cs");

You should get a response in the console window similar to this:

Compiling CH3/animshape.cs...

Loading compiled script CH3/animshape.cs.

This means that the Torque Engine has compiled your program and then loaded it into memory. The datablock definition and the three functions are in memory, waiting to be used.

7. Now, type the following into the console, and close the console quickly afterward:

DoAnimTest();

What you should see now is the heart dropping from the air to the ground; it then begins moving away from you toward the right. Go chase after it if you like, to get a sense of how fast it is moving.

Team LRN

3D Programming

119

Go ahead and experiment with the program. Try moving the item through several axes at once, or try changing the distance.

3D Audio

Environmental sounds with a 3D component contribute greatly to the immersive aspect of a game by providing positional cues that mimic the way sounds happen in real life.

We can control 3D audio in the scene in much the same way we do 3D visual objects.

Type the following program and save it as 3DGPAi1\CH3\animaudio.cs.

//========================================================================

//animaudio.cs

//

//This module contains the definition of an audio emitter, which uses

//a synthetic water drop sound. It also contains functions for placing

//the test emitter in the game world and moving the emitter.

//========================================================================

datablock AudioProfile(TestSound)

// ----------------------------------------------------

// Definition of the audio profile

// ----------------------------------------------------

{

filename = "~/data/sound/testing.wav"; // wave file to use for the sound description = "AudioDefaultLooping3d"; // monophonic sound that repeats

preload = false; // Engine will only load sound if it encounters it // in the mission

};

function InsertTestEmitter()

// ----------------------------------------------------

//Instantiates the test sound, then inserts it

//into the game world to the right and offset somewhat

//from the player's default spawn location.

// ----------------------------------------------------

{

// An example function which creates a new TestSound object %emtr = new AudioEmitter() {

position = "0 0 0"; rotation = "1 0 0 0"; scale = "1 1 1";

profile = "TestSound"; // Use the profile in the datablock above

Team LRN

120 Chapter 3 3D Programming Concepts

useProfileDescription = "1"; type = "2";

volume = "1"; outsideAmbient = "1"; referenceDistance = "1"; maxDistance = "100"; isLooping = "1";

is3D = "1"; loopCount = "-1"; minLoopGap = "0"; maxLoopGap = "0";

coneInsideAngle = "360"; coneOutsideAngle = "360"; coneOutsideVolume = "1"; coneVector = "0 0 1"; minDistance = "20.0";

};

MissionCleanup.add(%emtr);

// Player setup-

%emtr.setTransform("-200 -30 12 0 0 1 0"); // starting location echo("Inserting Audio Emitter " @ %emtr);

return %emtr;

}

function AnimSound(%snd, %dist)

//----------------------------------------------------

//moves the %snd by %dist amount each time

//----------------------------------------------------

{

%xfrm = %snd.getTransform();

%lx = getword(%xfrm,0); // first, get the current transform values %ly = getword(%xfrm,1);

%lz = getword(%xfrm,2); %rx = getword(%xfrm,3); %ry = getword(%xfrm,4);

%rz

= getword(%xfrm,5);

 

%lx

+= %dist;

// set the new x position

%snd.setTransform(%lx SPC %ly SPC %lz SPC %rx SPC %ry SPC %rz SPC %rd); schedule(200,0,AnimSound, %snd, %dist);

}

Team LRN

3D Programming

121

function DoMoveTest()

// ----------------------------------------------------

//a function to tie together the instantiation

//and the movement in one easy to type function

//call.

// ----------------------------------------------------

{

%ms = InsertTestEmitter();

AnimSound(%ms,1);

}

 

DoMoveTest();

// by putting this here, we cause the test to start

 

// as soon as this module has been loaded into memory

In this program, we also have a datablock, but you'll notice that it is different this time. This datablock defines an audio profile. It contains the name of the wave file that contains the sound to be played, a descriptor that tells Torque how to treat the sound, and a flag to indicate whether the engine should automatically load the sound or wait until it encounters a need for the sound. In this case, the engine will wait until it knows it needs the file.

The InsertTestEmitter function is structured the same as the earlier InsertTestShape function, but this time it creates the object with a call to new AudioEmitter, and there are quite a few properties to be set. These properties will be explained in much greater detail in Chapters 19 and 20.

Another difference to note is the last line, which is a call to DoMoveTest. This allows us to load and run the program in one go, using the exec call. After the Torque Engine compiles the program, it loads it into memory and runs through the code. In our earlier program, like the AnimShape module, Torque would encounter the datablock and function definitions. Because they are definitions, they aren't executed, just loaded into memory. The last line, however, is not a definition. It is a statement that calls a function. So when Torque encounters it, Torque looks to see if it has the function resident in memory, and if so, it executes the function according to the syntax of the statement.

To use the program, follow these steps:

1.Make sure you've saved the file as 3DGPAi1\CH3\ animaudio.cs.

2.Run the Chapter 3 demo using the shortcut in the 3DGPAi1 folder.

3.Press the Start button when the demo screen comes up.

4.Make sure you don't move your player-character after it spawns into the game world.

5.Bring up the console window.

6.Type in the following, and press Enter after the semicolon:

exec("CH3/animaudio.cs");

Team LRN

122 Chapter 3 3D Programming Concepts

You should get a response in the console window similar to this:

Compiling CH3/animaudio.cs...

Loading compiled script CH3/animaudio.cs.

You should also begin to hear the dripping sound off to the right-hand side. If you wait without moving your player in any way, not even using the mouse to turn his head, you will notice the sound slowly approach you from the left, pass over to the right in front of you, and then pass off into the distance to the left. Pretty neat, huh?

Moving Right Along

So, we've now seen how 3D objects are constructed from vertices and faces, or polygons. We explored how they fit into that virtual game world using transformations and that the transformations are applied in particular order—scaling, rotation, and then finally translation. We also saw how different rendering techniques can be used to enhance the appearance of 3D models.

Then we learned practical ways to apply those concepts using program code written using Torque Script and tested with the Torque Game Engine.

In the next chapter, we will dive deeper into learning how to use Torque Script.

Team LRN

chapter 4

Game Programming

In the preceding two chapters you were introduced to a few new concepts: programming, 3D graphics, manipulating 3D objects, and stuff like that. Most of it was fairly broad, in order to give you a good grasp of what you can do to make your game.

The next bunch of chapters get down and dirty, so to speak. We're going to muck around with our own hands examining things, creating things, and making things happen.

In this chapter we're going to hammer at the Torque Script for a while, writing actual code that will be used to develop our game. We'll examine in detail how the code works in order to gain a thorough understanding of how Torque works. The game we are going to create has the rather unoriginal name of Emaga, which is just agame spelled backward. The Chapter 4 version will be called Emaga4. Of course, you may—and probably should— substitute whatever name you wish!

Torque Script

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

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

Most of the syntax in Torque Game Engine (TGE) Script language is similar to C/C++ language, with a high correlation of keywords (see Table A.3 in Appendix A) between the two.

Although, as is often the case in scripting languages, there is no type enforcement on the

123

Team LRN

124Chapter 4 Game Programming

variables, and you don't declare variables before using them. If you read a variable before writing it, it will be an empty string or zero, depending on whether you are using it in a string context or a numeric context.

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

Strings

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

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

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

Table 4.1 Torque Script String Formatting Codes

Code

Description

\r

Embeds a carriage return character.

\n

Embeds a new line character.

\t

Embeds a tab character.

\xhh

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

\c

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

\cr

Resets the display color to the default.

\cp

Pushes the current display color onto a stack.

\co

Pops the current display color off the stack.

\cn

Uses n as an index into the color table defined by

 

GUIControlProfile.fontColors.

Team LRN

Torque Script

125

Objects

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

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

n o t e

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

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

Creating an Object

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

%handle = new InteriorInstance()

{

position = "0 0 0"; rotation = "0 0 0"; interiorFile = %name;

};

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

Team LRN

126Chapter 4 Game Programming

Using Objects

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

%handle.aproperty = 42;

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

A

MyObject.aproperty = 42;

B

"MyObject".aproperty = 42;

C

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

D

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

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

Object Functions

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

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

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

Team LRN

Torque Script

127

Namespaces

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

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

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

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

$Game::maxplayers

$Server::maxplayers

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

$Game::maxplayers

$Game::timelimit

$Game::maxscores

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

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

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

In these examples:

$myObject.maxSize

$explosion.maxSize

$beast.maxSize

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

Team LRN