Visual C# Game Programming for Teens phần 7 pps

47 248 0
Visual C# Game Programming for Teens phần 7 pps

Đ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

When using distance to determine whether two sprites are colliding, what we must do is calculate the center point of each sprite, calculate the radius of the sprite (from the center point to the edge), and then check the distance between the two center points. If the distance is less than the two radii combined, then you know the sprites are overlapping. Why? The radius of each sprite, when added together, should be less than the distance between the two sprites. To calculate the distance between any two points, we can use the classic distance formula. Any two points can be converted into a right triangle by treating them as the end points of the two sides of that triangle, as shown in Figure 11.3. Take the delta value of the X and Y of each point, square each delta value, add them Figure 11.2 The center points of two sprites are used to calculate the distance between them. Figure 11.3 A triangle is used to calculate the distance between two points. Image courtesy of Wikipedia. 264 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs together, then take the square root, and you have the distance between the two points. Here is the formula written as pseudo-code: delta_x = x2 – x1 delta_y = y2 – y1 delta_x_squared = delta_x * delta_x delta_y_squared = delta_y * delta_y distance = square root ( delta_x_squared + delta_y_squared) Here is a function that will meet our needs for calculating distance. This Distance() function would be most reusable if added to the Game class along with some overloaded parameters. We may also need a more specific function suited for the player’s standing position (returning the distance to another Character), so it might be helpful to also add a helper function to the Character class that also calculates distance. public double Distance(PointF first, PointF second) { float deltaX = second.X - first.X; float deltaY = second.Y - first.Y; double dist = Math.Sqrt(deltaX * deltaX + deltaY * deltaY); return dist; } But, we don’t want to just use the player’s raw X,Y position for the comparison. Remember back in Chapter 9, “Going Deeper into the Dungeon with Portals,” we had to compare the player’s foot position to see if he’s walking on a portal or not? The raw position gives the upper-left corner of the player sprite’s collision box. We need to use the same HeroFeet() function that returns an adjusted Point containing the coordinates of the player’s feet, as if the sprite is really walking on the tiled ground. private Point HeroFeet() { return new Point((int)hero.X + 32, (int)hero.Y + 32 + 16); } After deciding whether the player is close enough to an NPC to talk with it, the next step is to trigger a new dialogue mode in the game. We will want the rest of the game to pause while talking so nothing happens that the player would be unable to respond to in the game (like being attacked). This pause mode can be handled with a flag that causes some parts of the game to stop updating, but we Talking with NPCs 265 will want them to be drawn. While that is happening, we do want to allow dialogue to happen, so this probably calls for a Dialogue class. But what should the class do? The first example for this chapter, Dialogue demo 1, shows how to calculate the distance between the hero and an NPC, displays the distance, and draws a “talk radius” circle around the NPC so you can see when the character is in range. By pressing the Space key when in range, a “talking” flag is triggered. Figure 11.4 shows the example. (The code to load the vendor and player characters will be shown in the second example later in this chapter.) Without getting too deep into the complete source code listing, here is the key code from the first demo (there are three for this chapter). If the player is in range then the circle is drawn in blue to show that the player is in range. A line connecting both characters shows visually what the distance looks like from the precise locations from which is it calculated. Figure 11.4 If the NPC is in range, then the player can begin a dialogue. 266 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs private void doVendor() { float relativeX=0, relativeY=0; int talkRadius = 70; Pen color; //draw the vendor sprite if (vendor.X > level.ScrollPos.X && vendor.X < level.ScrollPos.X + 23 * 32 && vendor.Y > level.ScrollPos.Y && vendor.Y < level.ScrollPos.Y + 17 * 32) { relativeX = Math.Abs(level.ScrollPos.X - vendor.X); relativeY = Math.Abs(level.ScrollPos.Y - vendor.Y); vendor.GetSprite.Draw((int)relativeX, (int)relativeY); } //get center of hero sprite PointF heroCenter = HeroFeet(); heroCenter.X += 16; heroCenter.Y += 16; game.Device.DrawRectangle(Pens.Red, heroCenter.X - 2, heroCenter.Y - 2, 4, 4); //get center of NPC PointF vendorCenter = new Point((int)relativeX, (int)relativeY); vendorCenter.X += vendor.GetSprite.Width / 2; vendorCenter.Y += vendor.GetSprite.Height / 2; game.Device.DrawRectangle(Pens.Red, vendorCenter.X - 2, vendorCenter.Y - 2, 4, 4); double dist = Distance(heroCenter, vendorCenter); //draw line connecting player to vendor if (dist < talkRadius) color = new Pen(Brushes.Blue, 2.0f); else color = new Pen(Brushes.Red, 2.0f); game.Device.DrawLine(color, heroCenter, vendorCenter); //print distance Talking with NPCs 267 game.Print((int)relativeX, (int)relativeY, "D = " + dist.ToString("N0"), Brushes.White); //draw circle around vendor to show talk radius float spriteSize = vendor.GetSprite.Width / 2; float centerx = relativeX + spriteSize; float centery = relativeY + spriteSize; RectangleF circleRect = new RectangleF(centerx - talkRadius, centery - talkRadius, talkRadius * 2, talkRadius * 2); game.Device.DrawEllipse(color, circleRect); //is playing trying to talk to this vendor? if (dist < talkRadius) { if (talkFlag) talking = true; } else talking = false; } Tip The complete source code for the Dialogue demo is found later in the chapter. We’ll skip through the code in the meantime while exploring how the dialogue system works. Dialogue Choices If our game had a mystery plot that required the player to interview dozens or perhaps hundreds of NPCs to find out “who dunnit,” then we would absolutely need another game editor to handle all of the complex interactions with these NPCs, with branching dialogue trees and variables so that the NPCs remember past conversations—or at least seem to remember them. We don’t want this level of complexity for our Dungeon Crawler game. Basically, on each major area of the game, we want to have several NPCs that help the player: buying drop items, selling better gear, healing the player, and so on. These are pretty simple interactions. Since the inventory system and item editor won’t be coming along for a couple more chapters, we can’t offer up actual gear for the player to use, nor can we let the player sell drop items to the town vendors (yet). But we can get the framework in place so that these things are possible. In other words, we need a generic dialogue system with options that the player can select. 268 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs The graphical user interface (GUI) for a game is a complex subject. GUI programmers are in high demand in the game industry, as are tool pro- grammers! So you are learning a bit of both high-demand skills here! Continuing to think through the design considerations of our dialogue system, one assumption I’ll make now is that we will not have any complex graphical controls like scrolling lists, drop-down lists, or even anything like a scrollbar. The level of complexity for our GUI will end with buttons, and there will be a limited number of them. However, with the use of a state variable, we can create multiple levels for the dialogue system. Let’s say, first of all, there are two dialogue choices for a vendor: n BUY n SELL If you choose the “BUY” option, then a variable is set so that a list of items for sale is displayed next. Then we just recycle the same dialogue with a different set of options (a limited list of items for sale). n Dagger n Short Sword n Barbarian Hammer n Leather Armor n Chain Armor n Plate Armor n More There are some limitations to this system, but with creative use of state variables you could offer an unlimited number of items by making the last button a More button that brings up a second page, and so on. One design consideration that you might want to consider is abandoning any sort of Back button in the dialogue system. I know it seems reasonable to let the player go back one level or page, but that tends to complicate things. It is easy enough to just end the dialogue and start it up again with the character, and I have seen many games take this approach. Talking with NPCs 269 Creating the Dialogue System Now that we can determine whether the player is close enough to an NPC to talk with it, the next step is to bring up a dialogue window and let the user interact with the NPC. First, we’ll incorporate the new dialogue helper functions and properties into the classes to make more effective use of them. Making Eye Contact We can still use the distance function to find out when the player is close to an NPC, but Distance() is obviously so reusable that it must be moved into the Game class. I’ll go a step further by adding an overload. Feel free to add any other variations of the core Game functions or properties that you would find useful. This new version works with individual coordinate values for the two points passed as parameters. This new Distance function along with the previous version have been added to the Game class starting with the Dialogue demo 2 project. public double Distance(float x1, float x2, float y1, float y2) { PointF first = new PointF(x1, x2); PointF second = new PointF(x2, y2); return Distance(first, second); } The HeroFeet() function will work again, but it is becoming tiresome. Simple, yes, but it is not very reusable. I want a more generic version of this code actually built into the Character class. We have to make some more assumptions about the tile size used in the game, but at this point we’re set on 32x32 so I won’tbe concerned with that now. The HeroFeet() function has become the Character. FootPos property. This property is also now part of the Character class (beginning with the Dialogue demo 2 project). public PointF FootPos { get { return new Point((int)this.X + 32, (int)this.Y + 32 + 16); } } Another helpful property that would greatly help to simplify our code is a CenterPos property for the Character class. The center of a character is its X,Y position plus its width and height, each divided by two. 270 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs public PointF CenterPos { get { PointF pos = this.Position; pos.X += this.GetSprite.Width / 2; pos.Y += this.GetSprite.Height / 2; return pos; } } Next, we’ll incorporate the distance calculations right inside the Character class while taking into account both the foot position and the center position of the character. Some parameters are objects passed by reference, not because changes are made to them, but to avoid having the object copied to the function every time (a reference is just a pointer to the object in memory). public double FootDistance(ref Character other) { return p_game.Distance(this.FootPos, other.FootPos); } public double FootDistance(PointF pos) { return p_game.Distance(this.FootPos, pos); } public double CenterDistance(ref Character other) { return p_game.Distance(CenterPos, other.CenterPos); } public double CenterDistance(PointF pos) { return p_game.Distance(this.CenterPos, pos); } I’m going to use the CenterPos property this time, rather than FootPos,to simplify the example code a bit and show that both techniques work equally well when you just want to know if the player is close enough to an NPC to talk to it. Creating the Dialogue System 271 When the player character is within range of the NPC and the talk radius circle turns blue, then press the Space key to begin talking. Dialogue GUI What do we want this dialogue system to look like? It needs to be simple and positioned in such a way that it doesn’t block out a large portion of the screen, but at the same time, it would be helpful to draw the dialogue interface as a window at a certain location every time. What about drawing the window at one of the four corners of the game window, depending on where the player character is located? It would be unfortunate to draw the dialogue window over the top of the player! That affects the user’s suspension of disbelief. The player wants to see his character while talking with an NPC, not interact in some sort of disembodied way. Let’s start by figuring out where the player is located and then drawing a box in an opposing corner—whichever corner is farthest from the player. The Dialogue class will be reusable and serve many roles beyond just talking with NPCs. This will be our de facto way to communicate with the player, with either just simple messages requiring the click of an OK button to a more complex message with many choices. The Dialogue class will be self-contained, requiring very little from other classes except for Game.Device, which is needed to draw. We’re going to need to use a smaller font than the default font in the Game class. Although we have Game.SetFont() for changing that font, it will be too much of a pain to change the font back and forth after showing the dialogue text, so the dialogue system will use its own font. The dialogue window will be set up using properties and then drawn with a Draw() function, which will pause the game until the player chooses one of the options. Figure 11.5 shows a dialogue window positioned at the lower left with a size of one-quarter the screen (400x300). Positioning the Dialogue Window In my opinion, this window size is a bit too large, even if we give it some transparency with an alpha channel. While I would enjoy working on a resizable dialogue window, I’m not willing to get into the complexity of drawing button controls onto a variable-sized window—no, we need to keep this simple and enhance it as needed for each game. Let’s try a slightly smaller window with that 272 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs alpha channel, shown in Figu re 11.6. This screen mock-up shows the slightly smaller dialogue window (360x280) in the four corners. A border and shadow would certainly improve its appearance, but it already looks usable. Hint To create your own dialogue window with whatever level of transparency you want, use a graphic editor like GIMP or Paint.Net, create a window with the resolution you want, and then use the Opacity slider or Layer, Mask menu option to change the alpha level of the image. An alpha level of 60% looks pretty good. However, we can also just draw a filled rectangle with what ever alpha level we want at runtime so that’s probably the best solution (although it’s a bit slower). To automatically move the dialogue to one of the corners based on the player’s position, we’ll use an enumeration: Figure 11.5 A possible dialogue window position. Creating the Dialogue System 273 [...]... InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { this.Text = "NPC Dialogue Demo 3"; //create game object Form form = (Form)this; game = new Game( ref form, 800, 600); //create tilemap level = new Level(ref game, 25, 19, 32); level.loadTilemap("sample.level"); level.loadPalette("palette.bmp", 5); //load hero hero = new Character(ref game) ; hero.Load("paladin.char"); hero.Position... final GUI for the dialogue window with all of the buttons filled with sample items for purchase, while Figure 11.10 shows that the game responds to the selection after the dialogue window is closed To save space, the using statements and the namespace will be omitted since they are now redundant public partial class Form1 : Form { public struct keyStates { public bool up, down, left, right; } Game game;... hostile NPC swinging weapons at each other, basically as fast as the attacks are allowed to go, the HP for each slowly goes down until one falls or retreats This form of combat works great for a game like Diablo and Baldur’s Gate, and our own pending Dungeon Crawler game The combat system for our game can be either real time or turn-based with a few simple flags in the code Starting an Attack The code... much easier changes later For the first time we are actually going to use the mouse in our game code! That’s quite a statement now that we’re so heavily invested into 11 chapters, but until now we have not needed the mouse The main form is covered up by the PictureBox that is created by the Game class and attached to the form, so we have to modify the PictureBox control in the Game class to support mouse... //draw button border p _game. Device.DrawRectangle(Pens.Gray, rect); //print button label size = p _game. Device.MeasureString(p_buttons[n].Text, p_fontButton); tx = (int)(rect.X + rect.Width / 2 - size.Width / 2); ty = rect.Y + 2; p _game. Device.DrawString(p_buttons[n].Text, p_fontButton, Brushes.White, tx, ty); } Final Example I promised to go over the source code for a complete example before ending this chapter,... SizeF layoutArea = new SizeF(p_size.Width, 80); 277 278 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs int lines = 4; int length = p_message.Length; size = p _game. Device.MeasureString(p_message, p_fontMessage, layoutArea, null, out length, out lines); RectangleF layoutRect = new RectangleF(p_position.X + 4, p_position.Y + 34, size.Width, size.Height); p _game. Device.DrawString(p_message, p_fontMessage,... position property for the buttons, though.) In fact, the Dialogue.Button structure doesn’t really resemble a button at all! There is no positional or dimensional information in the structure, just a text property What gives?! The structure is in place for future needs (such as the aforementioned features) We don’t need anything more than the text property, but putting it in a structure allows for much easier... waypoints or just walking back and forth between two points For our first few examples of combat, the hostile NPCs will be positioned in the level randomly and will not move We will give them movement behavior later, but for now I just want to show you how to engage an enemy in order to attack them There are several ways we could go about handling combat in the game In a real-time game, there is never a pause... step 275 276 Chapter 11 n Dialogue: Trainers, Vendors, and NPCs Figure 11 .7 The dialogue automatically moves based on the player’s location Hint If the dialogue window is too small for your needs, you could make a much taller window and just cause it to stay in place or allow it to automatically move from left to right instead of jumping among the four corners A window of this type could be used for. .. sets specified in these template char files for any unique classes of your own design The purpose behind the char file, at least for player characters, is to define the animation properties Hint In the final game, these character templates will be used to create real game characters The complete sets of animation and character editor data files are included for each one in this chapter’s resource files . other classes except for Game. Device, which is needed to draw. We’re going to need to use a smaller font than the default font in the Game class. Although we have Game. SetFont() for changing that. redundant. public partial class Form1 : Form { public struct keyStates { public bool up, down, left, right; } Game game; Level level; keyStates keyState; bool gameover = false; Character hero; Character. the PictureBox that is created by the Game class and attached to the form, so we have to modify the PictureBox control in the Game class to support mouse input, oddly enough. 278 Chapter 11 n Dialogue: Trainers,

Ngày đăng: 14/08/2014, 01:20

Từ khóa liên quan

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

Tài liệu liên quan