
3D Game Programming All In One (2004)
.pdf
138Chapter 4 ■ Game Programming
The stub routine OnExit() is next. A stub routine is a function that is defined but actually does nothing. The common code modules have a call to this routine, but we have nothing for it to do. We could just leave it out, but a good policy is to provide an empty stub to avoid warning messages from appearing in our log file—when the Torque Engine tries to call a nonexistent function, it generates a warning.
Then there is the ParseArgs() function. Its job is to step through the list of command line arguments, or parameters, and perform whatever tasks you want based upon what arguments the user provided. In this case we'll just include code to provide a bare-bones usage, or Help, display.
Next is the actual Usage() function that displays the Help information.
This is followed by the LoadAddOns() routine. Its purpose is to walk through the list of addons specified by the user on the command line and to load the code for each. In Emaga4 there is no way for the user to specify add-ons or Mods, but (you knew there was a but coming, didn't you?) we still need this function, because we treat our common and control modules as if they were add-ons. They are always added to the list in such a way that they get loaded first. So this function is here to look after them.
After the function definitions we move into the in-line program statements. These statements are executed at load time—when the module is loaded into memory with the Exec() statement. When Torque runs, after the engine gets itself sorted out, it always loads the root main module (this module) with an Exec() statement. All of the other script modules are loaded as a result of what this module does.
The first thing that happens is a call to the ParseArgs() function, which we saw earlier. It sets the $usageFlag variable for us, you will recall.
Next is the block of code that examines the $usageFlag and decides what to do: either display the usage Help information or continue to run the game program. If we are not displaying the usage information, we move into the code block after the else.
The first thing we do in here is check to see if there are any unused arguments from the command line. If there are, that means the program doesn't understand the arguments and there was some kind of error, which we indicate with the Error() function and a useful message.
After that we set the log mode, if logging has been enabled.
Next, we build the lists that help Torque find our add-ons. We notify Torque about the required folder paths by passing the list to the SetModPaths() function.
Then we call the main module for the common code. This will proceed to load all the required common modules into memory, initialize the common functions, and basically get the ball rolling over there. We will talk about the common code modules in a later chapter.
Team LRN

Control Main |
139 |
After that we do the same thing for the control code modules, the details of which we will cover later in this chapter.
Then we actually start loading the add-ons using the previously defined LoadAddOns() function.
Finally, we make a call to OnStart(). This will call all versions of OnStart() that appear in the add-on packages in order of their appearance in $addonList, with common being first, control next, and finally this root main module. If there is an OnStart() defined in common, then it gets called. Then the one in control, and so on.
When we get to the end of the module, the various threads initiated by the OnStart() calls are ticking over, doing their own things.
So now what? Well, our next point of interest is the control/main.cs module, which we called with the Exec() function just before we started loading the add-ons.
Control Main
The main.cs module for the control code is next on our tour. Its primary purposes in Emaga4 are to define the control package and to call the control code initialization functions. (In later chapters we will expand on the role of this module.) Following is the control/main.cs module. Type it in and save it as Emaga4\control\main.cs.
//------------------------------------------------------------------------
//control/main.cs
//main control module for 3DGPAI1 emaga4 tutorial game
//Copyright (c) 2003 by Kenneth C. Finney.
//------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
//Load up defaults console values.
//Defaults console values
//-----------------------------------------------------------------------------
// Package overrides to initialize the mod. package control {
function OnStart() //------------------------------------------------------------------------
// Called by root main when package is loaded //------------------------------------------------------------------------
Team LRN

140 |
Chapter 4 ■ |
Game Programming |
|
|
{ |
|
|
|
Parent::OnStart(); |
|
|
|
Echo("\n--------- |
Initializing control module |
---------"); |
//The following scripts contain the preparation code for
//both the client and server code. A client can also host
//games, so they need to be able to act as servers if the
//user wants to host a game. That means we always prepare
//to be a server at anytime, unless we are launched as a
//dedicated server.
Exec("./initialize.cs");
InitializeServer(); // Prepare the server-specific aspects
InitializeClient(); // Prepare the client-specific aspects
}
function OnExit() //------------------------------------------------------------------------
// Called by root main when package is unloaded //------------------------------------------------------------------------
{
Parent::onExit();
}
}; // Client package
ActivatePackage(control); // Tell TGE to make the client package active
Not a whole lot happens in here at the moment, but it is a necessary module because it defines our control package.
First, the parent OnStart() function is called. This would be the version that resides in root main, which we can see doesn't have anything to do.
Then the initialize.cs module is loaded, after which the two initialization functions are called.
Finally, there is the OnExit() function, which does nothing more than pass the buck to the OnExit() function in the root main module.
All in all, control/main.cs is a fairly lazy, though important, little module.
Team LRN

Initialization 141
Debugging Scripts Using the trace() Function
The engine adds extra commentary to the log file. Extremely useful are the notations that tell you when the engine execution has just begun executing in a particular function or is just about to leave a particular function. The trace lines include the values of any arguments used when the function is entered and the contents of the return value when leaving a function.
Here is a fragmentary example of what the trace output can look like:
Entering GameConnection::InitialControlSet(1207)
Setting Initial Control Object
Entering Editor::checkActiveLoadDone()
Leaving Editor::checkActiveLoadDone - return 0
Entering GuiCanvas::setContent(Canvas, PlayGui)
Entering PlayGui::onWake(1195)
Activating DirectInput...
keyboard0 input device acquired. Leaving PlayGui::onWake - return
Entering GuiCanvas::checkCursor(Canvas)
Entering (null)::cursorOff()
Leaving (null)::cursorOff - return
Leaving GuiCanvas::checkCursor - return
Leaving GuiCanvas::setContent - return
Leaving GameConnection::InitialControlSet - return
Entering (null)::DoYaw(-9)
Leaving (null)::DoYaw - return -0.18
Entering (null)::DoPitch(7)
Leaving (null)::DoPitch - return 0.14
Entering (null)::DoYaw(-6)
To turn on the trace function, add the following statement to the first line of your root main.cs file:
trace(true);
To turn off the trace function, insert this statement at the place in the code where you would like to turn tracing off:
Trace(false);
Initialization
The control/initialize.cs module will, in later chapters, become two different modules— one for the server code and one for the client code. Right now, we have a fairly limited amount of work to do, so we'll just house the initialization functions for the two ends in
Team LRN

142 Chapter 4 ■ Game Programming
the same module. Here is the control/initialize.cs module. Type it in and save it as Emaga4\control\initialize.cs.
//============================================================================
//control/initialize.cs
//control initialization module for 3DGPAI1 emaga4 tutorial game
//Copyright (c) 2003 by Kenneth C. Finney. //============================================================================
function InitializeServer() //----------------------------------------------------------------------------
// Prepare some global server information & load the game-specific module //----------------------------------------------------------------------------
{ |
|
|
Echo("\n--------- |
Initializing module: emaga server |
---------"); |
// Specify where the mission files are. $Server::MissionFileSpec = "*/missions/*.mis";
InitBaseServer(); // basic server features defined in the common modules
// Load up game server support script Exec("./server.cs");
createServer("SinglePlayer", "control/data/maps/book_ch4.mis");
}
function InitializeClient() //----------------------------------------------------------------------------
//Prepare some global client information, fire up the graphics engine,
//and then connect to the server code that is already running in another
//thread.
//---------------------------------------------------------------------------- |
|
|
{ |
|
|
Echo("\n--------- |
Initializing module: emaga client |
---------"); |
InitBaseClient(); |
// basic client features defined in the common modules |
// these are necessary graphics settings
Team LRN

Initialization 143
$pref::Video::allowOpenGL = true; $pref::Video::displayDevice = "OpenGL";
//Make sure a canvas has been built before any gui scripts are
//executed because many of the controls depend on the canvas to
//already exist when they are loaded.
InitCanvas("Egama4 - 3DGPAi1 Sample Game"); // Start the graphics system.
Exec("./client.cs");
%conn = new GameConnection(ServerConnection); %conn.connectLocal();
}
First is the InitializeServer() function. This is where we set up a global variable that indicates to the game engine the folder tree where the map (also called mission) files will be located.
Next, we prepare the server for operation by performing the common code initialization using the InitBaseServer() function. This allows us to get the server code running full-bore, which we can do using the createServer() call. We tell the function that this will be a singleplayer game and that we are going to load up the map control/data/maps/book_ch4.mis.
After that, we load the module that contains the game code, which is server-side code.
Then we do the client-side initialization in the InitializeClient() function. This is a bit more involved. After performing the common code initialization with InitBaseClient(), we set up some global variables that the engine uses to prepare the graphics system for start-up.
And that happens with the InitCanvas() call. The parameter we pass in is a string that specifies the name of the window that the game will be running in.
Then we load the control/client.cs module, which we'll cover next in this chapter.
We're getting warm now!
Next, we create a connection object using the GameConnection() function. This gives us an object that we will use from now on when referring to the connection.
Now we use that connection object to connect to the server using a local connection. We don't ever actually use the network or any network ports.
Team LRN

144 Chapter 4 ■ Game Programming
Client
The control/client.cs module is chock-full of good stuff. This is another module that will need to have some of its code divested when it grows in later chapters. The main activities taking place in here are as follows:
■Creation of a key map with key bindings
■Definition of a callback that gets called from with Torque to generate a 3D view
■Definition of an interface to hold the 3D view
■Definition of a series of functions that hook key commands to avatar motion
■A series of stub routines
Here is the control/client.cs module. Type it in and save it as Emaga4\control\client.cs.
//============================================================================
//control/client.cs
//This module contains client specific code for handling
//the setup and operation of the player's in-game interface.
//3DGPAI1 emaga4 tutorial game
//Copyright (c) 2003 by Kenneth C. Finney. //============================================================================
if ( IsObject( playerKeymap ) ) // If we already have a player key map,
playerKeymap.delete(); |
// delete it so that we can make a new one |
|
new ActionMap(playerKeymap); |
|
|
$movementSpeed = 1; |
// m/s |
for use by movement functions |
//----------------------------------------------------------------------------
// The player sees the game via this control //----------------------------------------------------------------------------
new GameTSCtrl(PlayerInterface) { profile = "GuiContentProfile"; noCursor = "1";
};
function PlayerInterface::onWake(%this) //----------------------------------------------------------------------------
// When PlayerInterface is activated, this function is called. //----------------------------------------------------------------------------
Team LRN

Client 145
{
$enableDirectInput = "1"; activateDirectInput();
// restore the player's key mappings playerKeymap.push();
}
function GameConnection::InitialControlSet(%this) //----------------------------------------------------------------------------
//This callback is called directly from inside the Torque Engine
//during server initialization.
//----------------------------------------------------------------------------
{
Echo ("Setting Initial Control Object");
//The first control object has been set by the server
//and we are now ready to go.
Canvas.SetContent(PlayerInterface);
}
//============================================================================ // Motion Functions //============================================================================
function GoLeft(%val) //----------------------------------------------------------------------------
// "strafing" //----------------------------------------------------------------------------
{
$mvLeftAction = %val;
}
function GoRight(%val) //----------------------------------------------------------------------------
// "strafing" //----------------------------------------------------------------------------
{
$mvRightAction = %val;
}
Team LRN

146Chapter 4 ■ Game Programming
function GoAhead(%val)
//----------------------------------------------------------------------------
// running forward //----------------------------------------------------------------------------
{
$mvForwardAction = %val;
}
function BackUp(%val) //----------------------------------------------------------------------------
// running backwards //----------------------------------------------------------------------------
{
$mvBackwardAction = %val;
}
function DoYaw(%val) //----------------------------------------------------------------------------
// looking, spinning or aiming horizontally by mouse or joystick control //----------------------------------------------------------------------------
{
$mvYaw += %val * ($cameraFov / 90) * 0.02;
}
function DoPitch(%val) //----------------------------------------------------------------------------
// looking vertically by mouse or joystick control //----------------------------------------------------------------------------
{
$mvPitch += %val * ($cameraFov / 90) * 0.02;
}
function DoJump(%val) //----------------------------------------------------------------------------
// momentary upward movement, with character animation //----------------------------------------------------------------------------
{
$mvTriggerCount2++;
}
//============================================================================ // View Functions
Team LRN

Client 147
//============================================================================
function Toggle3rdPPOVLook( %val ) //----------------------------------------------------------------------------
//Enable the "free look" feature. As long as the mapped key is pressed,
//the player can view his avatar by moving the mouse around.
//----------------------------------------------------------------------------
{
if ( %val ) $mvFreeLook = true;
else
$mvFreeLook = false;
}
function Toggle1stPPOV(%val) //----------------------------------------------------------------------------
// switch between 1st and 3rd person point-of-views. //----------------------------------------------------------------------------
{
if (%val)
{
$firstPerson = !$firstPerson;
}
}
//============================================================================
//keyboard control mappings //============================================================================
//these ones available when player is in game
playerKeymap.Bind(keyboard, up, GoAhead); playerKeymap.Bind(keyboard, down, BackUp); playerKeymap.Bind(keyboard, left, GoLeft); playerKeymap.Bind(keyboard, right, GoRight); playerKeymap.Bind( keyboard, numpad0, DoJump ); playerKeymap.Bind( mouse, xaxis, DoYaw ); playerKeymap.Bind( mouse, yaxis, DoPitch ); playerKeymap.Bind( keyboard, z, Toggle3rdPPOVLook ); playerKeymap.Bind( keyboard, tab, Toggle1stPPOV );
// these ones are always available GlobalActionMap.BindCmd(keyboard, escape, "", "quit();"); GlobalActionMap.Bind(keyboard, tilde, ToggleConsole);
Team LRN