3D Game Programming All in One- P25

30 472 0
3D Game Programming All in One- P25

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

$TypeMasks::TerrainObjectType | $TypeMasks::ShapeBaseObjectType; %scanTarg = ContainerRayCast(%x SPC %y SPC "500", %x SPC %y SPC "-100", %search- Masks); if(%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { %newpos = GetWord(%scanTarg,1) SPC GetWord(%scanTarg,2) SPC GetWord(%scanTarg,3) + 1; } %coin = new Item("Copper "@%i) { position = %newpos; rotation = "1 0 0 0"; scale = "5 5 5"; dataBlock = "Copper"; collideable = "0"; static = "0"; rotate = "1"; }; MissionCleanup.add(%coin); CoinGroup.add(%coin); } } The first thing this function does is to obtain the particulars of the MissionArea . For this game, you should use the Mission Area Editor (press F11 followed by F5) to expand the MissionArea to fill the entire available terrain tile. The %H and %W values are the height and width of the MissionArea box. The variables %west and %south combined make the coordinates of the southwest corner. We uses these values to constrain our random number selection. Then we set up a search mask. All objects in the Torque Engine have a mask value that helps to identify the object type. We can combine these masks using a bitwise-or opera- tion, in order to identify a selection of different types of interest. Then we use our random coordinates to do a search from 500 world units altitude down- ward until we encounter terrain, using the ContainerRayCast function. When the ray cast finds terrain, we add 1 world unit to the height and then use that plus the random coordinates to build a position at which to spawn a coin. Then we spawn the coin using the appropriate datablock, which can be found in your new copy of item.cs. Next, we add the coin to the MissionCleanup group so that Torque will automatically remove the coins when the game ends. We also add it to the CoinGroup in case we want to access it later. Triggering Events 627 Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. After putting that code in, copy C:\3DGPAi1\RESOURCES\CH22\ITEM.CS over to C:\koob\control\server\misc. You will find the datablocks for the coins (where the coin values are assigned) in there. Note that when we added the coins in the preceding code, the static parameter was set to 0. This means that the game will not create a new coin at the place where the coin was picked up, if it is picked up. The weapons of the ammo do this, but we don't want our coins to do it. It's a game play design decision. In addition to the datablocks for the coins in item.cs, you will also find this code: if (%user.client) { messageClient(%user.client, 'MsgItemPickup', '\c0You picked up %1', %this.pickup- Name); %user.client.money += %this.value; %user.client.DoScore(); } The last two statements in there allow the player to accumulate the money values, and then the server notifies the client of the new score. Note that it is similar in that small way to the checkpoint scoring. Again, until the client code is in place, you can insert echo statements there to verify that things are working properly. Deaths We want to track the number of times we die to further satisfy requirements, so open C:\koob\control\server\server.cs, locate the method GameConnection::onDeath , and add these lines at the end: %this.deaths++; %this.DoScore(); By now these lines should be familiar. We can expand the player death by adding some sound effects and animation. Add the following to the end of C:\koob\control\server\ players\player.cs: function Player::playDeathAnimation(%this,%deathIdx) { %this.setActionThread("Die1"); } datablock AudioProfile(DeathCrySound) { fileName = "~/data/sound/orc_death.wav"; description = AudioClose3d; Chapter 22 ■ The Game Server628 Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. preload = true; }; function Player::playDeathCry( %this ) { %client = %this.client; serverPlay3D(DeathCrySound,%this.getTransform()); } The first function, playDeathAnimation , will play the animation sequence we named Die1 in our model. After that is another audio profile, pretty straightforward, followed by the function playDeathCry , which will play that profile's sound effect. These are invoked by two lines that you should place in the OnDisabled function farther back up in the player.cs file. Add these two lines to OnDisabled just before the call to SetImageTrigger : %obj.playDeathCry(); %obj.playDeathAnimation(-1); One more thing—copy the audio wave file C:\3DGPAi1\RESOURCES\CH22\ORC_ DEATH.WAV to C:\koob\control\data\sound in order to make the sound work. Kills The victim, who notifies the shooter's client when he dies, actually does the kill tracking. So we go back to GameConnection::onDeath and add this: %sourceClient = %sourceObject ? %sourceObject.client : 0; if (%obj.getState() $= "Dead") { if (isObject(%sourceClient)) { %sourceClient.incScore(1); if (isObject(%client)) %client.onDeath(%sourceObject, %sourceClient, %damageType, %location); } } This bit of code figures out who shot the player and notifies the shooter's client object of this fact. Now it is important to remember that all this takes place on the server, and when we refer to the client in this context, we are actually talking about the client's connection object and not about the remote client itself. Okay, so now let's move on to the client side and finish filling the requirements! Triggering Events 629 Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Moving Right Along So, now we have our player's model ready to appear in the game as our avatar, we've got wheels for him to get around in, and a way to figure out where he's been. We've also put some things in the game world for the player to pursue to accumulate points, and a way to discourage other players from accumulating too many points for themselves (by killing them). All of these features are created on the server. In the next chapter, we will add the features that will be handled by the game client. Chapter 22 ■ The Game Server630 Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 631 The Game Client chapter 23 B y now we've met most of our requirements, at least to the point of implementa- tion. Testing them for correct operation and completeness I will leave as an exer- cise for you, Gentle Reader, because you may (and probably will) want to modify and enhance the requirements anyway. According to my list, the requirements that remain outstanding are the following: 2. Internet multiplayer game play. 3. Global in-game chat. 11. All other players are enemies. 12. All point values configurable from a setup file. 14. 3 points per vehicle destroyed. 15. Ability to race around the track and score 5 points for each lap you lead. (partial) 16. Laps can only be scored in the car. 17. A 10-lap race with no time limit. 18. A 10-point bonus for winning the race. 29. When one map is finished, cycle to the next in the list. Of this list, I will leave numbers 14, 16, 17, and 18 and the remaining portion of number 15 (scoring 5 points) to you to complete as exercises. They are variations of the coin scor- ing and the lap and checkpoint tracking we covered in Chapter 22. The functioning code is available in the Koob installation kit on the CD, if you need help. Most of the remaining work requires additional client code to support the server addi- tions we made in the last chapter—we'll add some multiplayer support, a little bit more client support, and user interfaces to access those capabilities. Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Client Interfaces We are going to add code to allow users to run a server and to allow players to connect to a server. In order to make that connection, we will want to provide the user with an inter- face he can use to find servers, decide which one offers an interesting game, and then con- nect to the server. Another thing we need to do is make sure that when the user quits a server, he returns to his selection interface rather than simply exiting as Koob does now. Additionally, we need to add a capability to the playing interface to provide a chat win- dow with a text entry where players can type in messages to send to other players. Maybe they'll want to exchange recipes or something. Yeah, that's it—recipes! It's not like they're going to taunt anyone anyway, is it? In Chapter 6 you saw the MasterScreen interface module that combined these interfaces. In this chapter we'll look at the same issue but in a slightly different way, in order to show how easy it is to make different—yet equally valid—design decisions. Also, we'll need to modify a few of the files, like the MainScreen interface, to more closely conform to our needs. In a later section we'll add the code required to make these interfaces functional. MenuScreen Interface We will make some changes to our main menu screen so that it provides the user with the additional choices to ■ view information about the games and credits ■ play in single-player mode (as it already has) ■ host a game ■ connect to another server Open your MenuScreen.gui file and locate the following line: command = "LaunchGame();"; This line is a property statement in a GuiButtonCtrl . Delete the entire control, from where it says new GuiButtonCtrl() { down to the closing brace ("}"). In the place of the deleted control, insert the following: Chapter 23 ■ The Game Client632 Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. new GuiButtonCtrl() { profile = "GuiButtonProfile"; horizSizing = "right"; vertSizing = "top"; position = "30 138"; extent = "120 20"; minExtent = "8 8"; visible = "1"; command = "Canvas.setContent(SoloScreen);"; text = "Play Solo"; groupNum = "-1"; buttonType = "PushButton"; helpTag = "0"; }; new GuiButtonCtrl() { profile = "GuiButtonProfile"; horizSizing = "right"; vertSizing = "top"; position = "30 166"; extent = "120 20"; minExtent = "8 8"; visible = "1"; command = "Canvas.setContent(ServerScreen);"; text = "Find a Server"; groupNum = "-1"; buttonType = "PushButton"; helpTag = "0"; }; new GuiButtonCtrl() { profile = "GuiButtonProfile"; horizSizing = "right"; vertSizing = "top"; position = "30 192"; extent = "120 20"; minExtent = "8 8"; visible = "1"; command = "Canvas.setContent(HostScreen);"; text = "Host Game"; groupNum = "-1"; buttonType = "PushButton"; helpTag = "0"; }; Client Interfaces 633 Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. new GuiButtonCtrl() { profile = "GuiButtonProfile"; horizSizing = "right"; vertSizing = "top"; position = "30 237"; extent = "120 20"; minExtent = "8 8"; visible = "1"; command = "getHelp();"; helpTag = "0"; text = "Info"; groupNum = "-1"; buttonType = "PushButton"; }; You may, if you wish, use the built-in GUI Editor (press F10) to do this. Make sure that you set all of the properties to match those just listed. The significant thing to note about these controls is the command property. Each one replaces a displayed MenuScreen interface with a new interface, according to its function, with the exception of the Info button. The Info button uses the getHelp feature of the common code base. It searches all of the directories nested under the root main directory looking for files with the extension .hfl, and then it lists them in alphanumeric order. If you preface the file name with a number, such as 1., 2., and so on, it will sort them numerically. This should give you a main menu that looks like Figure 23.1. SoloPlay Interface The SoloPlay interface, as shown in Figure 23.2, prepares a list of mission files that it finds in the maps subdirec- tory in the control\data directory tree. From this list, you can select the map or mission you want to play. Its code and definition can be found in the SoloScreen modules. It's worth remembering that even when you play in solo mode, under- neath the hood, the Torque Engine is Chapter 23 ■ The Game Client634 Figure 23.1 MenuScreen interface. Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. still running in two parts: a client and a server. They are just closely coupled with no cross-network calls being made. Host Interface The Host interface is somewhat simi- lar, as you can see in Figure 23.3, but offers more options: the ability to set a time limit and a score limit, plus map selection modes. Its code and definition can be found in the HostScreen modules. If both time and score limits are set, the first one reached ends the game. A setting of 0 makes that limit infinite. The sequence mode causes the server to step through the maps in order as shown in the listing, as each game fin- ishes and the new one loads. The ran- dom mode causes the server to ran- domly select a map for each game. The time limit is saved by the control in the variable $Game::Duration , and the score limit is saved as $Game::MaxPoints . FindServer Interface The FindServer interface, shown in Figure 23.4, lets you browse for servers. Its code and definition can be found in the ServerScreen modules. It will find servers that are running on the local LAN you are connected to (if you are connected to one, of course), and it will attempt to reach out via the Internet to contact the master servers at GarageGames and find games for you to connect to. You are not required to use the GarageGames master servers, but then you will have to write your own master server software to connect to. This can be done using Torque Script but is beyond the scope of this book. There are mas- ter server resources available from the GarageGames user community. Client Interfaces 635 Figure 23.2 SoloPlay interface. Figure 23.3 Host interface. Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. note The Query LAN button on the FindServer interface does the same thing that was done automati- cally in Chapter 6 when you clicked on the Connect to Server button on the main menu screen. The discussion in the Chapter 6 section called ServerScreen Code Module describes how the Connect to Server button operations are performed, which is the same as how the Query LAN button opera- tions work here. ChatBox Interface In order to display chat messages from other players, we need to put a control in our main player interface. We also need to have a control that will allow us to type in messages to be sent to other players, as depicted in Figure 23.5. Open the file C:\koob\control\client\Initialize.cs and add the following lines to the func- tion InitializeClient : Chapter 23 ■ The Game Client636 Figure 23.4 FindServer interface. Figure 23.5 ChatBox interface. Team LRN Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... FileObject(); %MissionInfoObject = ""; if ( %file.openForRead( %missionFile ) ) { %inInfoBlock = false; while ( !%file.isEOF() ) { %line = %file.readLine(); %line = trim( %line ); if( %line $= "new ScriptObject(MissionInfo) {" ) %inInfoBlock = true; Team LRN 643 644 Chapter 23 ■ The Game Client else if( %inInfoBlock && %line $= "};" ) { %inInfoBlock = false; %MissionInfoObject = %MissionInfoObject @ %line; break;... 23 ■ The Game Client You might be tempted to misinterpret this code, even if you thoroughly understand programming in C or Torque Script What we need to do is simplify the code to remove obfuscation introduced by the line context: We'll change all instances of findFirstFile($Server::MissionFileSpec) to fFF(), all instances of findNextFile ($Server::MissionFileSpec)) to fNF(), and finally, all instances... function StartGame farther up in the server.cs file, and add these lines to the beginning: if ( $Game: :Duration) $Game: :Schedule = schedule( $Game: :Duration * 1000, 0, "CycleGame" ); This starts the game timer running When it expires, it invokes the cycleGame function The other thing we need to do is add some code that checks to see if a player has hit the $Game: :MaxPoints limit Locate the function GameConnection::DoScore()... ending with the cs extension If you go back to the previous code listing for MenuScreen.gui, you will see where each of the interfaces is invoked ServerScreen is defined in ServerScreen.gui, HostScreen is defined in HostScreen.gui, and finally, SoloScreen is defined in SoloScreen.gui Each interface has roughly the same form There is an OnWake method for the interface object that is called by the engine... finishes, not doing anything else Further action is scheduled to be taken by the onCyclePauseEnd function This allows us to put up a victory screen or other messages and leave them up for an appropriate viewing time before continuing on to the next game In order to provoke the actual cycleGame function into being, we do two things First, when the game is launched, we schedule its demise based on $Game: :Duration... binding that calls ToggleMessageBox, which is defined in MessageBox.cs (one of the files we've recently copied that we will examine shortly) In the interface files there are a couple of concepts you should note To illustrate, look at the definition of the ChatBox interface, contained in ChatBox.gui: new GuiControl(MainChatBox) { profile = "GuiModelessDialogProfile"; horizSizing = "width"; vertSizing... function cycleGame() { if (! $Game: :Cycling) { $Game: :Cycling = true; $Game: :Schedule = schedule(0, 0, "onCycleExec"); } } function onCycleExec() { endGame(); $Game: :Schedule = schedule( $Game: :EndGamePause * 1000, 0, "onCyclePauseEnd"); } function onCyclePauseEnd() { $Game: :Cycling = false; %search = $Server::MissionFileSpec; for (%file = findFirstFile(%search); %file !$= ""; %file = findNextFile(%search))... (%client.lapsCompleted * $Game: :Laps_Multiplier) + (%client.money * $Game: :Money_Multiplier) + (%client.deaths * $Game: :Deaths_Multiplier) + (%client.kills * $Game: :Kills_Multiplier) ; This code accumulates the various scoring values into a single overall score Now add the following code to the end of the same DoScore function: if (%client.score >= $Game: :MaxPoints) cycleGame(); This causes the game cycling activity... %line; break; } if( %inInfoBlock ) %MissionInfoObject = %MissionInfoObject @ %line @ " "; } %file.close(); } %MissionInfoObject = "%MissionInfoObject = " @ %MissionInfoObject; eval( %MissionInfoObject ); %file.delete(); if( %MissionInfoObject.name !$= "" ) return %MissionInfoObject.name; else return fileBase(%missionFile); } The OnWake method is as described in earlier chapters in this case the onWake... %file = findNextFile(%search); Team LRN 655 656 Chapter 23 ■ The Game Client if (%file $= "") %file = findFirstFile(%search); break; } } loadMission(%file); } The first function, cycleGame, schedules the actual cycling code to occur at some later point In this case we do it right away after making sure that we aren't actually already cycling The function nCycleExec actually ends the game The endGame function . else if( %inInfoBlock && %line $= "};" ) { %inInfoBlock = false; %MissionInfoObject = %MissionInfoObject @ %line; break; } if( %inInfoBlock. According to my list, the requirements that remain outstanding are the following: 2. Internet multiplayer game play. 3. Global in -game chat. 11. All other

Ngày đăng: 24/10/2013, 18:15

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan