Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 40 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
40
Dung lượng
1,01 MB
Nội dung
fontSpr.ScaleFactor = 3 PrintText fontImg, fontSpr, 10, 180, C_RED, _ “B I G H U G E F O N T !” fontSpr.ScaleFactor = 0.6 PrintText fontImg, fontSpr, 10, 260, C_PURPLE, _ “This Is A Smaller Font” d3ddev.EndScene End Sub Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode = 27 Then Shutdown End Sub Private Sub Form_Paint() d3ddev.Present ByVal 0, ByVal 0, 0, ByVal 0 End Sub Level Up This chapter provided a core technique in the ability to print text on the screen, which has been ignored up to this point (although displaying status information on the screen in previous sample programs would have been very useful). The ability to display a message on the screen is absolutely crucial, so you are likely to find yourself using the code pro- vided in this chapter more than any other. Chapter 13 ■ Core Technique: Drawing Text260 T his chapter explains the core technique of detecting collisions between two sprites in a game. This is a higher-level technique than previous topics you have learned so far, which have focused more on the key aspects of just getting something on the screen. Now that you have a complete prototype for the game up and running (per Chapter 12, “Walking Around in the Game World”), you can focus on these higher-level things. This chapter shows you how to identify when the player has hit a tile that is impassable (such as an ocean square), and this technique is refined over the next three chapters when dealing with non-player characters (NPCs). Here is a breakdown of the major topics in this chapter: ■ Rectangle intersection ■ Writing a reusable collision function ■ Keeping track of “bad” tiles ■ Checking for tile collisions ■ Revising the scroll data display ■ The CollisionTest program Reacting to Solid Objects Collision detection is an important technique that you should learn. It is an absolute requirement for every game ever made. I can’t think of any game that does not need col- lision detection, because it is such an essential aspect of gameplay. Without collisions, there is no action, goal, or purpose in a game. There is no way to die without collisions 261 Core Technique: Collision Detection chapter 14 taking place. In other words, collision detection makes the sprites in a game come to life, and makes the game believable. Rectangle Intersection Traditional collision detection involves the comparison of two rectangles on the screen, usually representing the bounding boxes around two sprites. A function available within the Windows API gives you a quick answer to the question of a collision between two sprites. This function is called IntersectRect : Public Declare Function IntersectRect _ Lib “user32” Alias “IntersectRect” ( _ lpDestRect As RECT, _ lpSrc1Rect As RECT, _ lpSrc2Rect As RECT) _ As Long IntersectRect returns the intersection rectangle, lpDestRect , with the union of the two other rectangles (although this information is not usually needed). Two additional RECT structures are required in the call to this function, and they contain the location and size of each sprite. Writing a Reusable Collision Function It is helpful to write a support function that sets up the two RECT structures by represent- ing the two sprites that need to be checked for a collision. By encapsulating this support code inside a function, you can reduce the amount of code that you must write to check for multiple collisions in your game (which can become quite tedious if you have to set up the structures for every single instance). I recommend adding this Collision function to the Sprite.bas file: Public Function Collision( _ ByRef sprite1 As TSPRITE, _ ByRef sprite2 As TSPRITE) As Boolean Dim dest As RECT Dim rect1 As RECT Dim rect2 As RECT ‘set up the first rect rect1.Left = sprite1.x rect1.Top = sprite1.y rect1.Right = sprite1.x + sprite1.width rect1.Bottom = sprite1.y + sprite1.height Chapter 14 ■ Core Technique: Collision Detection262 ‘set up the second rect rect2.Left = sprite2.x rect2.Top = sprite2.y rect2.Right = sprite2.x + sprite2.width rect2.Bottom = sprite2.y + sprite2.height ‘check for collision If IntersectRect(dest, rect1, rect2) <> 0 Then Collision = True Else Collision = False End If End Function Keeping Track of “Bad” Tiles For the purposes of detecting collisions within the Celtic Crusader game, it is important first to check tile numbers for valid movement regions on the map. Water tiles, solid object tiles, and so on should be impassable; in other words, these are blocking tiles that the player should not be able to cross. The whole process of limiting the player’s movement in the game world is done by mak- ing use of a list of impassable tiles. You can store these tile numbers in a data file to be loaded at runtime or just set an array containing the tile numbers that you know are impassable. I created a simple Integer array to keep track of the bad tiles: Dim badtiles() As Integer Then initialize the bad tiles with just a few tile numbers for testing purposes. In the com- plete game I fill this array with all of the impassable tiles. This really shouldn’t be a very large number in the end, because most of the tiles in Mappy are, in fact, for creating ter- rain that the player can walk on. Just as an example, tile 2 is the water tile, which certainly should not be walked on! Public Sub BuildTileCollisionList() ReDim badtiles(5) badtiles(0) = 2 badtiles(1) = 34 badtiles(2) = 44 badtiles(3) = 54 badtiles(4) = 79 End Sub Reacting to Solid Objects 263 When it comes time to actually check the list of bad tiles, you can use the following func- tion. Do you see how unfriendly this function is, with the hard-coded array range (0 to 4)? You need either to modify this value when adding new items to the bad tiles array or create a constant that specifies the number of bad tiles. I am keeping it simple so you can focus mainly on the subject of collision and not on support code. Public Function IsBadTile(ByVal tilenum As Long) As Boolean Dim n As Long For n = 0 To 4 If badtiles(n) - 1 = tilenum Then IsBadTile = True Exit Function End If Next n IsBadTile = False End Function Checking for Tile Collisions The main tile collision subroutine called from within the game loop is called Check- TileCollisions . This subroutine scans the current tile under the player’s feet and then runs it by the IsBadTile function to determine if the current tile number is on the black list. If that is true, then the next step is to prevent the player from moving over that tile. By call- ing this CheckTileCollisions subroutine before the tiles are drawn and before the scroll win- dow is drawn, you can cause the player to actually take a step back to counter the movement that triggered the tile collision. As far as the player is concerned, the sprite just stopped at the edge of the impassable tile. What actually happened is that the sprite moved onto the tile and was moved off it by the same amount of space. By the time the scene is rendered, it appears that the sprite just stopped. Public Sub CheckTileCollisions() Dim tilenum As Long tilenum = CurrentTile() If IsBadTile(tilenum) Then Scroll 0, 0 Select Case heroSpr.AnimSeq Case 0 ScrollY = ScrollY + HEROSPEED Case 1 ScrollY = ScrollY + HEROSPEED ScrollX = ScrollX - HEROSPEED Chapter 14 ■ Core Technique: Collision Detection264 Case 2 ScrollX = ScrollX - HEROSPEED Case 3 ScrollX = ScrollX - HEROSPEED ScrollY = ScrollY - HEROSPEED Case 4 ScrollY = ScrollY - HEROSPEED Case 5 ScrollX = ScrollX + HEROSPEED ScrollY = ScrollY - HEROSPEED Case 6 ScrollX = ScrollX + HEROSPEED Case 7 ScrollX = ScrollX + HEROSPEED ScrollY = ScrollY + HEROSPEED End Select End If End Sub Revising the Scroll Data Display At this point, I need to show you some of the code I changed in the program to accom- modate the tile collision routines. This code was originally found back in Chapter 12 in the WalkAbout program, which is the game’s first prototype. The following code includes some support routines that I wrote to provide the current player’s position and tile num- ber (which were programmed in the ShowScrollData subroutine previously). Public Function TileAt(ByVal x As Long, ByVal y As Long) As Long Dim tile As point tile.x = x \ TILEWIDTH tile.y = y \ TILEHEIGHT TileAt = mapdata(tile.y * MAPWIDTH + tile.x) End Function Public Function CurrentTile() As Long CurrentTile = TileAt(PlayerPos.x, PlayerPos.y) End Function Public Function PlayerPos() As point ‘get tile pos at center of screen PlayerPos.x = ScrollX + SCREENWIDTH / 2 PlayerPos.y = ScrollY + SCREENHEIGHT / 2 End Function Reacting to Solid Objects 265 Public Sub ShowScrollData() Static old As point Dim tile As point tile.x = PlayerPos.x \ TILEWIDTH tile.y = PlayerPos.y \ TILEHEIGHT If (tile.x <> old.x) Or (tile.y <> old.y) Then ‘erase the background DrawSurface wood, 0, 0, 639, 30, backbuffer, 0, 449 old = tile PrintText fontImg, fontSpr, 5, 452, C_WHITE, _ “Scroll=(“ & PlayerPos.x & “,” & PlayerPos.y & “) “ PrintText fontImg, fontSpr, 5, 466, C_WHITE, _ “Tile(“ & tile.x & “,” & tile.y & “)=” & CurrentTile() End If End Sub Another thing this code does is make use of the new PrintText subroutine that was pro- vided in the previous chapter for printing text on the screen. I modified the ShowScrollData subroutine so that it would print the scrolling and tile numbers at the bottom of the screen rather than in the window caption. Along the way, I came up with a nice image to fill the space at the bottom of the screen. This is a wood-grain bitmap image, loaded into a surface and drawn at the bottom of the screen during each screen update. I created and loaded this image using the following code in Sub Main : Dim wood As Direct3DSurface8 Set wood = LoadSurface(App.Path & “\bottom.bmp”, 644, 32) The CollisionTest Program A lot of changes were made to the WalkAbout program during the development of collision-detection code, with the resulting program for this chapter (CollisionTest). I do not want to list the source code for the project in each chapter because the source code is starting to get somewhat complex, and I prefer that you focus on the algorithms and indi- vidual functions and subroutines that are covered in each chapter. I encourage you, therefore, to load the current project that you copied from the CD-ROM to your hard drive and examine the program running. At this point, I am confident that you have gained enough experience entering code that it is no longer necessary for me to include complete listings of each program. In each chapter from here on out I use the Chapter 14 ■ Core Technique: Collision Detection266 same code that was originally developed for the WalkAbout program, with incremental updates and improvements along the way. With the tile-collision code added to the original WalkAbout program, the resulting new program has been called CollisionTest and is available on the CD-ROM in \sources \chapter14. Figure 14.1 shows the new version of the game running. Note the player is standing next to a solid, impassable tile. Level Up This chapter provided an introduction to collision detection. You learned about the basic collision between two sprites—or more accurately, between two rectangles—using the IntersectRect function available in the Windows API (and greatly simplifies the collision code that you would otherwise have to write yourself). You then learned how to imple- ment tile collision in the game so you can specify a certain tile number as impassable. By modifying some of the code in the game, it is now possible to prevent the player from walking on specific tiles. Level Up 267 Figure 14.1 The CollisionTest program demonstrates tile-collision checking within the game. This page intentionally left blank Non-Player Characters (NPCs) Chapter 15 Creating the Character Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .271 Chapter 16 Adding NPCs to the Game World . . . . . . . . . . . . . . . . . . . . . . . . . . . .279 Chapter 17 Talking with NPCs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .293 Chapter 18 Engaging in Combat with NPCs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .303 PART IV [...]... great for balancing the game play without requiring that you modify every single subclass that you have used in the game Class modifiers must be dealt with in code rather than in a character-editor program, therefore I don’t use subclass modifiers (although the idea is a good one) Using Character Classes in the Game Using Character Classes in the Game You know what type of data you want to use in the game. .. prototype character-class editor for an RPG chapter 16 Adding NPCs to the Game World A role-playing game is only fun if the game world is sufficiently populated to allow interaction and fighting with computer-controlled players These non-player characters, or NPCs for short, are a vital element of a role-playing game (RPG) Although some types of RPGs prefer to reserve NPC for human characters, I simplify... ensures that NPCs stay near their homes in the game world Public Sub SetRandomDestination(ByVal num As Long) With charStates(num) ‘set random X near the starting position ‘(the NPC will never wander away from his “home”) destpos.x = startpos.x + Random(600) 287 288 Chapter 16 ■ Adding NPCs to the Game World If destpos.x > GAMEWORLDWIDTH Then destpos.x = GAMEWORLDWIDTH - 1 End If ‘set random Y near the... point is on the algorithms and helping you understand how the game works, so you can modify it yourself.) Before diving into the new source code for the game, which is starting to look more and more like a functional RPG with each new chapter (refer back to Figure 16.1), create the custom sprite artwork for each new character in the game Fortunately, as you have seen in previous chapters, you have these... respawning an NPC, perhaps at the other side of the game world, you can keep the game flowing smoothly without allowing a rampaging player to decimate your plans for challenging gameplay! Indeed, one thing you might consider is increasing the experience and level of each NPC that is killed, so that over time the game world gets more and more challenging for the player You explore these issues in the next... law enforcement (Have you considered the possibility of having local 281 282 Chapter 16 ■ Adding NPCs to the Game World guards attack the player if you harm any local peasants? That would be a fascinating aspect of the game. ) The other type of NPC is malevolent in nature—that is, characters opposed to the player, including evil creatures, outlaws, bandits, and of course in the context of this game: ... constructing a playergeneration system for this game, I point you in the right direction and provide the essentials for creating each of the five character classes described in Chapter 3, “Designing the Game. ” Throughout this chapter, you have NPCs with which the player may engage in battle; therefore, the discussion of player attributes (and NPC attributes) is called for Here is a breakdown of the major... potential with my particular vision for a game Take this as far and wide as you possibly can! By the time you have finished this chapter, you have a new, improved version of the Celtic Crusader game (which is still more of a game engine at this point than a complete game) available The version that you see developed in this chapter is shown in Figure 16.1 The game has basic NPC support by the time you finish... listing the source code for the editor program here (The source code is short, but doesn’t account for the controls on the form.) Level Up This chapter filled in the details of character attributes and classes Now that things are looking pretty good in the game department, you can focus some attention again on character design The last few chapters have focused on designing the game world and interacting... continue performing an activity for a long period of time Very high stamina provides a character with the ability to engage in lengthy battles without rest, while a character with low stamina tires quickly (and is likely to fall in battle) Gaining Experience and Leveling Up One of the most rewarding aspects of a role-playing game (RPG) is gaining experience by performing actions in the game (usually . is an absolute requirement for every game ever made. I can’t think of any game that does not need col- lision detection, because it is such an essential aspect of gameplay. Without collisions, there. rewarding aspects of a role-playing game (RPG) is gaining experience by performing actions in the game (usually combat) and leveling up your character. When you start the game, the character is also. type ready for use before deciding which method you would prefer to use in the game. Ultimately, I think it is best to use the second option, which means you store character class information