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

3D Game Programming All In One (2004)

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

168 Chapter 5 Game Play

vertSizing = "bottom"; position = "10 3"; extent = "50 20"; minExtent = "8 8"; visible = "1"; helpTag = "0";

text = "Score"; maxLength = "255";

};

new GuiTextCtrl(Scorebox) { profile = "ScoreTextProfile"; horizSizing = "right"; vertSizing = "bottom"; position = "50 3";

extent = "100 20"; minExtent = "8 8"; visible = "1"; helpTag = "0"; text = "0"; maxLength = "255";

};

};

PlayerInterface is the main TSControl through which the game is viewed; it also contains the HUD controls.

The object GuiCrossHairHud is the targeting crosshair. Use this to aim your weapons.

There are two GuiHealthBarHud controls, one for health and one for energy. It is essentially a vertical bar that indicates the state of health or energy of the player. Each GuiHealthBarHud is paired with a GuiBitmapCtrl, which is a bitmap that can be used to modify the appearance of the health and energy displays by overlaying on the GuiHealthBarHud.

n o t e

HUD is a TLA (Three Letter Acronym) that means Heads Up Display. The expression is adopted from the world of high-tech military aircraft. The HUD comprises information and graphics that are projected onto the canopy or a small screen at eye level in front of the pilot. This allows the pilot to continue to look outside for threats, while still having instant visual access to flightor mission-crit- ical information. In game graphics the term HUD is used for visual displays that appear in-game, in a fashion that mirrors the real-world application.

There are two GuiTextCtrl objects, one for holding the accumulated score (scorebox) and one to provide a simple label for the scores box (scorelabel). We will be modifying the value of the text property from within the control source code in another module.

Team LRN

Client Control Modules

169

control/client/interfaces/splashscreen.gui

The SplashScreen control displays an informational screen (you saw it in Figure 5.2) when the game is started from Windows. A mouse click or key press makes this screen go away. Type in the following code and save it as C:\Emaga5\control\client\interfaces\ splashscreen.gui.

new GuiChunkedBitmapCtrl(SplashScreen) { profile = "GuiDefaultProfile"; horizSizing = "width";

vertSizing = "height"; position = "0 0"; extent = "640 480"; minExtent = "8 8"; visible = "1"; helpTag = "0";

bitmap = "./interfaces/emaga_splash"; useVariable = "0";

tile = "0"; noCursor=1;

new GuiInputCtrl(SplashScreenInputCtrl) { profile = "GuiInputCtrlProfile"; position = "0 0";

extent = "10 10";

};

};

The only thing special about this module is the new control GuiInputCtrl. This control is used to accept input from the user: mouse clicks, key presses, and so on. With this control defined we can then define our own handler methods for the control's object and therefore act upon the inputs. In our case here SplashScreenInputCtrl::onInputEvent is the handler method we've defined; it's contained in the client module we talked about earlier.

control/client/misc/screens.cs

The screen.cs module is where our programmed control and management activity is located. Type in the following code and save it as C:\Emaga5\control\client\misc\- screens.cs.

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

//control/client/misc/screens.cs

//Copyright (c) 2003 Kenneth C. Finney //============================================================================

Team LRN

170Chapter 5 Game Play

function PlayerInterface::onWake(%this)

{

$enableDirectInput = "1"; activateDirectInput();

// just update the key map here playerKeymap.push();

}

function PlayerInterface::onSleep(%this)

{

playerKeymap.pop();

}

function refreshBottomTextCtrl()

{

BottomPrintText.position = "0 0";

}

function refreshCenterTextCtrl()

{

CenterPrintText.position = "0 0";

}

function LoadScreen::onAdd(%this)

{

%this.qLineCount = 0;

}

function LoadScreen::onWake(%this)

{

CloseMessagePopup();

}

function LoadScreen::onSleep(%this)

{

// Clear the load info:

if ( %this.qLineCount !$= "" )

{

for ( %line = 0; %line < %this.qLineCount; %line++ ) %this.qLine[%line] = "";

}

%this.qLineCount = 0; LOAD_MapName.setText( "" ); LOAD_MapDescription.setText( "" ); LoadingProgress.setValue( 0 );

LoadingProgressTxt.setValue( "WAITING FOR SERVER" );

}

Team LRN

Client Control Modules

171

The methods in this module are representative of the sort of methods you can use for interface controls. You will probably use OnWake and OnSleep quite a bit in your interface scripts.

OnWake methods are called when an interface object is told to display itself, either by the

Canvas's SetContent or PushDialog methods.

OnSleep methods are called whenever an interface object is removed from display via the PopDialog method or when the SetContent call specifies a different object.

When PushDialog is used the interface that is shown operates like a modal dialog control— all input events are relayed through the dialog.

There is another pair of interface display methods for other objects called just Push and Pop. These will display the interface in a modeless manner, so that other controls or objects on the screen will still receive input events they are interested in.

PlayerInterface::onWake enables capturing mouse and keyboard inputs using DirectInput. It then makes the PlayerKeymap key bindings active using the Push method. When the PlayerInterface is removed from display, its OnSleep method removes the PlayerKeymap key bindings from consideration. You will need to ensure that you have defined global bindings for the user to employ; these will take over when the PlayerKeymap isn't in use anymore.

RefreshBottomTextCtrl and RefreshCenterTextCtrl just reposition these output controls to their default location on the screen, in case you have moved them somewhere else during the festivities.

Loadscreen::OnWake is called when we want to display the mission loading progress. It closes the message interface, if it happens to be open. The Loadscreen contents are modified elsewhere for us in the mission loading process, which is covered in Chapter 6.

When Loadscreen::OnSleep is called, it clears all of its text buffers and then outputs a message to indicate that all we need now is for the server to chime in.

control/client/misc/presetkeys.cs

Key bindings are the mapping of keyboard keys and mouse buttons to specific functions and commands. In a fully featured game we would provide the user with the ability to modify the key bindings using a graphical interface. Right now we will satisfy ourselves with creating a set of key bindings for the user, which we can keep around to be used as the initial defaults as we later expand our program.

Type in the following code and save it as C:\Emaga5\control\client\misc\presetkeys.cs.

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

//control/client/misc/presetkeys.cs

//Copyright (c) 2003 Kenneth C. Finney //============================================================================

Team LRN

172

Chapter 5 Game Play

 

 

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);

 

function DoExitGame()

{

MessageBoxYesNo( "Quit Mission", "Exit from this Mission?", "Quit();", "");

}

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

//Motion Functions //============================================================================ function GoLeft(%val)

{

$mvLeftAction = %val;

}

function GoRight(%val)

{

$mvRightAction = %val;

}

function GoAhead(%val)

{

$mvForwardAction = %val;

}

function BackUp(%val)

{

$mvBackwardAction = %val;

}

function DoYaw(%val)

{

$mvYaw += %val * ($cameraFov / 90) * 0.02;

}

function DoPitch(%val)

{

$mvPitch += %val * ($cameraFov / 90) * 0.02;

}

function DoJump(%val)

{

$mvTriggerCount2++;

}

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

//View Functions //============================================================================

Team LRN

Client Control Modules

173

function Toggle3rdPPOVLook( %val )

{

if ( %val )

$mvFreeLook = true;

else

$mvFreeLook = false;

}

function MouseAction(%val)

{

$mvTriggerCount0++;

}

function Toggle1stPPOV(%val)

{

if (%val)

$firstPerson = !$firstPerson;

}

function dropCameraAtPlayer(%val)

{

if (%val) commandToServer('dropCameraAtPlayer');

}

function dropPlayerAtCamera(%val)

{

if (%val) commandToServer('DropPlayerAtCamera');

}

function toggleCamera(%val)

{

if (%val) commandToServer('ToggleCamera');

}

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

//keyboard control mappings //============================================================================

//available when player is in game

PlayerKeymap.Bind(mouse, button0, MouseAction ); // left mouse button

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(keyboard, z, Toggle3rdPPOVLook );

PlayerKeymap.Bind(keyboard, tab, Toggle1stPPOV );

PlayerKeymap.Bind(mouse, xaxis, DoYaw );

Team LRN

174Chapter 5 Game Play

PlayerKeymap.Bind(mouse, yaxis, DoPitch );

PlayerKeymap.bind(keyboard, F8, dropCameraAtPlayer);

PlayerKeymap.bind(keyboard, F7, dropPlayerAtCamera);

PlayerKeymap.bind(keyboard, F6, toggleCamera);

// always available

GlobalActionMap.Bind(keyboard, escape, DoExitGame);

GlobalActionMap.Bind(keyboard, tilde, ToggleConsole);

The first three statements in this module prepare the ActionMapobject, which we call PlayerKeymap. This is the set of key bindings that will prevail while we are actually in the game. Because this module is used in initial setup, we assume that there should not already be a PlayerKeymapActionMap, so we check to see if PlayerKeymap is an existing object, and if it is we delete it and create a new version.

We define a function to be called when we exit the game. It throws a MessageBoxYesNo dialog up on the screen, with the dialog's title set to the contents of the first parameter string. The second parameter string sets the contents of the dialog's prompt. The third parameter specifies the function to execute when the user clicks on the Yes button. The second parameter indicates what action to perform if the user clicks No—in this case nothing.

There are two other canned MessageDialog objects defined in the common code base: MessageBoxOk, which has no fourth parameter, and MessageBoxOkCancel, which accepts the same parameter set as MessageBoxYesNo.

Next we have a series of motion function definitions. Table 5.1 provides a description of the basic functions. These functions employ player event control triggers to do their dirty work. These triggers are described in detail in Chapter 6.

Of particular note in these functions is that they all have a single parameter, usually called %val. When functions are bound to keys or mouse buttons via a Bind method, the parameter is set to a nonzero value when the key or button is pressed and to 0 when the button is released. This allows us to create toggling functions, such as with Toggle3rdPPOVLook, which will be active only while the bound key is actually pressed.

After all the function definitions, we have the actual key bindings. With the Bind method, the first parameter is the input type, the second is the key or button identifier, and the third is the name of the function to be called.

After all the PlayerKeymap bindings, there are a few for GlobalActionMap, which is a globally predefined action map that is always available but can be overridden by other action maps. In this case we use GlobalActionMap for those bindings we want to be universally available.

Team LRN

Server Control Modules

175

Table 5.1 Basic Movement Functions

Command

Description

GoLeft and GoRight

Strafing to the left or the right.

GoAhead and BackUp

Running forward and backward.

DoYaw

Spinning or aiming horizontally by mouse or joystick control.

DoPitch

Looking vertically by mouse or joystick control.

DoJump

Momentary upward movement, with character animation.

Toggle3rdPPOVLook

Enables the "free look" feature. As long as the mapped key is pressed while

 

the player is in third person view, the player can view his avatar by moving

 

the mouse around.

Server Control Modules

Any game play features you want to implement should probably be done as a server control module, or part of one. If you are going to make a multiplayer online game, that should back there will change to a must. The only way we can ensure a level playing field and game play code security is to run the code on the server, and not on the client.

control/server/server.cs

On the server side, the server module is probably the single most influential module. It carries the server control oriented GameConnection methods for handling players and other game objects, as well as straightforward server control routines.

Type in the following code and save it as C:\Emaga5\control\server\server.cs.

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

//control/server/server.cs

//Copyright (c) 2003 by Kenneth C. Finney. //============================================================================ function OnServerCreated() //----------------------------------------------------------------------------

//Once the engine has fired up the server, this function is called

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

{

Exec("./misc/camera.cs");

Exec("./misc/shapeBase.cs");

Exec("./misc/item.cs");

Exec("./players/player.cs");

Exec("./players/beast.cs");

Exec("./players/ai.cs");

Exec("./weapons/weapon.cs");

Team LRN

176 Chapter 5 Game Play

Exec("./weapons/crossbow.cs");

}

function StartGame()

{

if ($Game::Duration) // Start the game timer

$Game::Schedule = Schedule($Game::Duration * 1000, 0, "onGameDurationEnd"); $Game::Running = true;

schedule( 2000, 0, "CreateBots");

}

function OnMissionLoaded()

{

StartGame();

}

function OnMissionEnded()

{

Cancel($Game::Schedule); $Game::Running = false;

}

function GameConnection::OnClientEnterGame(%this)

{

// Create a new camera object. %this.camera = new Camera() {

dataBlock = Observer;

};

MissionCleanup.Add( %this.camera ); %this.SpawnPlayer();

}

function GameConnection::SpawnPlayer(%this)

{

%this.CreatePlayer("0 0 201 1 0 0 0");

}

function GameConnection::CreatePlayer(%this, %spawnPoint)

{

if (%this.player > 0)//The player should NOT already have an avatar object. { // If he does, that's a Bad Thing.

Error( "Attempting to create an angus ghost!" );

}

 

// Create the player object

 

%player = new Player() {

 

dataBlock = HumanMaleAvatar;

// defined in players/player.cs

Team LRN

 

Server Control Modules

177

client = %this;

// the avatar will have a pointer to its

 

};

// owner's GameConnection object

 

%player.SetTransform(%spawnPoint); // where to put it

//Update the camera to start with the player %this.camera.SetTransform(%player.GetEyeTransform()); %player.SetEnergyLevel(100);

//Give the client control of the player %this.player = %player; %this.setControlObject(%player);

}

function GameConnection::OnDeath(%this, %sourceObject, %sourceClient, %damageType, %damLoc)

{

// Switch the client over to the death cam and unhook the player object. if (IsObject(%this.camera) && IsObject(%this.player))

{

%this.camera.SetMode("Death",%this.player);

%this.setControlObject(%this.camera);

}

%this.player = 0;

if (%damageType $= "Suicide" || %sourceClient == %this)

{

}

else

{

// good hit

}

}

//============================================================================ // Server commands //============================================================================ function ServerCmdToggleCamera(%client)

{

%co = %client.getControlObject(); if (%co == %client.player)

{

%co = %client.camera; %co.mode = toggleCameraFly;

}

else

{

%co = %client.player;

Team LRN