Client Control Modules


Modules that affect only the client side of the game are contained in the control/client folder tree. The client-specific activities deal with functions like the interface screens and displays, user input, and coordinating game start-up with the server side of the game.

control/client/client.cs

Many features that were in client.cs in the last chapter are now found in other modules. The key mapping and interface screen code that were located in this module, client.cs, have been given homes of their own, as you'll see later. Type in the following code and save it as C:\Emaga5\control\client\client.cs.

 //============================================================================ // control/client/client.cs //  Copyright (c) 2003 by Kenneth C. Finney. //============================================================================ function LaunchGame() {    createServer("SinglePlayer", "control/data/maps/book_ch5.mis");    %conn = new GameConnection(ServerConnection);    %conn.setConnectArgs("Reader");    %conn.connectLocal(); } function ShowMenuScreen() {    // Start up the client with the menu...    Canvas.setContent( MenuScreen );    Canvas.setCursor("DefaultCursor"); } function SplashScreenInputCtrl::onInputEvent(%this, %dev, %evt, %make) {    if(%make)    {      ShowMenuScreen(); } } //============================================================================ // stubs //============================================================================ function onServerMessage() { } function onMissionDownloadPhase1() { } function onPhase1Progress() { } function onPhase1Complete() { } function onMissionDownloadPhase2() { } function onPhase2Progress() { } function onPhase2Complete() { } function onPhase3Complete() { } function onMissionDownloadComplete() { } 

We've added three new functions, the first of which is LaunchGame(). The code contained should be familiar from Emaga4. This function is executed when the user clicks on the Start Game button on the front menu screen of the game—the other options available on the front screen are Setup and Quit.

Next is ShowMenuScreen(), which is invoked when the user clicks the mouse or hits a key when sitting viewing the splash screen. The code it invokes is also familiar from Emaga4.

The third function, SplashScreenInputCtrl::onInputEvent(), is a callback method used by a GuiInputControl, in this case the SplashScreenInputCtrl, which is attached to the splash screen for the narrow purpose of simply waiting for user input, and when it happens, closing the splash screen. We get the user input value in the %make parameter. Figure 5.2 shows what the splash screen looks like.

click to expand
Figure 5.2: The Emaga5 splash screen.

The rest of the functions are the by-now-famous stub routines. These are mostly client/server mission (map) loading and coordination functions. These will get more attention in later chapters. You are free to leave out the stub routines, but if you do, you will end up with a ton of warning messages in the log file.

control/client/interfaces-/menuscreen.gui

All of the user interface and display screens now have modules of their own, and they reside in the interfaces branch of the client tree. Note that the extension of these modules is .gui. Functionally, a .gui is the same as a .cs source module. They both can contain any kind of valid script code, and both compile to the .dso binary format. Type in the following code and save it as C:\Emaga5\control\client\interfaces\menuscreen.gui.

 new GuiChunkedBitmapCtrl(MenuScreen) {    profile = "GuiContentProfile";    horizSizing = "width";    vertSizing = "height";    position = "0 0";    extent = "640 480";    minExtent = "8 8";    visible = "1";    helpTag = "0";    bitmap = "./interfaces/emaga_background";    useVariable = "0";    tile = "0";    new GuiButtonCtrl() {       profile = "GuiButtonProfile";       horizSizing = "right";       vertSizing = "top";       position = "29 300";       extent = "110 20";       minExtent = "8 8";       visible = "1";       command = "LaunchGame();";       helpTag = "0";       text = "Start Game";       groupNum = "-1";       buttonType = "PushButton";    };    new GuiButtonCtrl() {       profile = "GuiButtonProfile";       horizSizing = "right";       vertSizing = "top";       position = "29 350";       extent = "110 20";       minExtent = "8 8";       visible = "1";       command = "Canvas.pushDialog(SetupScreen);";       helpTag = "0";       text = "Setup";       groupNum = "-1";       buttonType = "PushButton";    };    new GuiButtonCtrl() {       profile = "GuiButtonProfile";       horizSizing = "right";       vertSizing = "top";       position = "29 400";       extent = "110 20";       minExtent = "8 8";       visible = "1";       command = "Quit();";       helpTag = "0";       text = "Quit";       groupNum = "-1";       buttonType = "PushButton";    }; }; 

What we have here is a hierarchical definition of nested objects. The outer object that contains the others is the MenuScreen itself, defined as a GuiChunkedBitmapCtrl. Many video cards have texture size limits; for some nothing over 512 pixels by 512 pixels can be used. The ChunkedBitmap splits large textures into sections to avoid these limitations. This is usually used for large 640 by 480 or 800 by 600 background artwork.

MenuScreen has a profile property of GuiContentProfile, which is a standard Torque profile for large controls that will contain other controls. Profiles are collections of properties that can be applied in bulk to interface (or gui) objects. Profiles are much like style sheets (which you will be familiar with if you do any HTML programming), but using Torque Script syntax.

The definition of GuiContentProfile is pretty simple:

 if(!IsObject(GuiContentProfile)) new GuiControlProfile (GuiContentProfile) {    opaque = true;    fillColor = "255 255 255"; }; 

Basically, the object is opaque (no transparency allowed, even if an alpha channel exists in the object's source bitmap image). If the object doesn't fill the screen, then the unused screen space is filled with black (RGB = 255 255 255).

After the profile, the sizing and position information properties are set. See the sidebar titled "Profile Sizing Settings: horizSizing and vertSizing" for more information.

The extent property defines the horizontal and vertical dimensions of MenuScreen. The minExtent property specifies the smallest size that the object can have.

The visible property indicates whether the object can be scene on the screen. Using a "1" will make the object visible; a "0" will make it invisible.

The last significant property is the bitmap property—this specifies what bitmap image will be used for the background image of the object.

There are three GuiButtonCtrl objects contained in the MenuScreen. Most of the properties are the same as found in the GuiChunkedBitmapCtrl. But there are a few that are different and important.

The first is the command property.When the user clicks this button control, the function specified in the command property is executed.

Next, the text property is where you can enter the text label that will appear on the button.

Finally, the buttonType property is how you specify the particular visual style of the button.

Figure 5.3 shows the MenuScreen in all its glory.

click to expand
Figure 5.3: The Emaga5 MenuScreen.

start sidebar
Profile Sizing Settings: horizSizing and vertSizing

These settings are used to define how to resize or reposition an object when the object's container is resized.The outermost container is the Canvas; it will have a starting size of 640 pixels by 480 pixels. The Canvas and all of the objects within it will be resized or repositioned from this initial size. When you resize a container, all of its child objects are resized and repositioned according to their horizSizing and vertSizing properties. The resizing action will be applied in a cascading manner to all subobjects in the object hierarchy.

The following property values are available:

Center

The object is positioned in the center of its container.

Relative

The object is resized and repositioned to maintain the same size and position relative to its container. If the parent size doubles, the object's size doubles as well.

Left

When the container is resized or moved, the change is applied to the distance between the object and the left edge of the screen.

Right

When the container is resized or moved, the change is applied to the distance between the object and the right edge of the screen.

Top

When the container is resized or moved, the change is applied to the distance between the object and the top edge of the screen.

Bottom

When the container is resized or moved, the change is applied to the distance between the object and the bottom edge of the screen.

Width

When the container is resized or moved, the change is applied to the extents of the object.

Height

When the container is resized or moved, the change is applied to the extents of the object itself.

end sidebar

control/client/interfaces/playerinterface.gui

The PlayerInterface control is the interface that is used during the game to display information in real time. The Canvas is the container for PlayerInterface. Type in the following code and save it as C:\Emaga5\control\client\interfaces\playerinterface.gui.

 new GameTSCtrl(PlayerInterface) {    profile = "GuiContentProfile";    horizSizing = "right";    vertSizing = "bottom";    position = "0 0";    extent = "640 480";    minExtent = "8 8";    visible = "1";    helpTag = "0";       noCursor = "1";    new GuiCrossHairHud() {       profile = "GuiDefaultProfile";       horizSizing = "center";       vertSizing = "center";       position = "304 224";       extent = "32 32";       minExtent = "8 8";       visible = "1";       helpTag = "0";       bitmap = "./interfaces/emaga_gunsight";       wrap = "0";       damageFillColor = "0.000000 1.000000 0.000000 1.000000";       damageFrameColor = "1.000000 0.600000 0.000000 1.000000";       damageRect = "50 4";       damageOffset = "0 10";    };    new GuiHealthBarHud() {       profile = "GuiDefaultProfile";       horizSizing = "right";       vertSizing = "top";       position = "14 315";       extent = "26 138";       minExtent = "8 8";       visible = "1";       helpTag = "0";       showFill = "1";       displayEnergy = "0";       showFrame = "1";       fillColor = "0.000000 0.000000 0.000000 0.500000";       frameColor = "0.000000 1.000000 0.000000 0.000000";       damageFillColor = "0.800000 0.000000 0.000000 1.000000";       pulseRate = "1000";       pulseThreshold = "0.5";          value = "1";    };    new GuiBitmapCtrl() {       profile = "GuiDefaultProfile";       horizSizing = "right";       vertSizing = "top";       position = "11 299";       extent = "32 172";       minExtent = "8 8";       visible = "1";       helpTag = "0";       bitmap = "./interfaces/emaga_healthwidget";       wrap = "0";    };    new GuiHealthBarHud() {       profile = "GuiDefaultProfile";       horizSizing = "right";       vertSizing = "top";       position = "53 315";       extent = "26 138";       minExtent = "8 8";       visible = "1";       helpTag = "0";       showFill = "1";       displayEnergy = "1";       showFrame = "1";       fillColor = "0.000000 0.000000 0.000000 0.500000";       frameColor = "0.000000 1.000000 0.000000 0.000000";       damageFillColor = "0.000000 0.000000 0.800000 1.000000";       pulseRate = "1000";       pulseThreshold = "0.5";          value = "1";    };    new GuiBitmapCtrl() {       profile = "GuiDefaultProfile";       horizSizing = "right";       vertSizing = "top";       position = "50 299";       extent = "32 172";       minExtent = "8 8";       visible = "1";       helpTag = "0";       bitmap = "./interfaces/emaga_healthwidget";       wrap = "0";    };    new GuiTextCtrl(scorelabel) {       profile = "ScoreTextProfile";       horizSizing = "right";       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.

Note

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 flight- or mission-critical 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.

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 //============================================================================ 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" ); } 

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 //============================================================================ 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 //============================================================================ 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 ); 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 ActionMap object, 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.

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.

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.




3D Game Programming All in One
3D Game Programming All in One (Course Technology PTR Game Development Series)
ISBN: 159200136X
EAN: 2147483647
Year: 2006
Pages: 197

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net