1. Trang chủ
  2. » Công Nghệ Thông Tin

Building XNA 2.0 Games- P5 ppt

30 299 0

Đ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

Thông tin cơ bản

Định dạng
Số trang 30
Dung lượng 1,04 MB

Nội dung

CHAPTER 5 ■ THE CHARACTER EDITOR 107 So far, we’re iterating through all images for each texture, getting the source and destina- tion rectangles so that we can draw them in a neat row on the bottom of the screen. Of course, the special case with weapons is coming right up: if (l == 3) { sRect.X = (i % 4) * 80; sRect.Y = (i / 4) * 64; sRect.Width = 80; if (i < 15) { dRect.X = i * 30; dRect.Width = 30; } } With the correct source and destination rectangles, we draw the image. But since we have the destination rectangle, we might as well check if the mouse location is within the rectangle and clicking: spriteBatch.Draw(texture, dRect, sRect, Color.White); if (dRect.Contains(mouseState.X, mouseState.Y)) { if (mouseClick) { charDef.Frames[selFrame].Parts[selPart].Index = i + 64 * l; } } } } } spriteBatch.End(); Assuming we add a call to DrawPalette() and DrawCursor() at the end of Game1.Draw() somewhere, we’ll be treated to the result shown in Figure 5-6. Also, be sure to set mouseClick to false at the end of the Draw() method. 108 CHAPTER 5 ■ THE CHARACTER EDITOR Figure 5-6. Icon palette The Parts List We’ll use the icon palette to specify which image index each part uses. The parts list will allow us to manipulate our composited character in a way similar to a layer-heavy image-editing approach. We’ll be able to select a part to manipulate, move parts up and down the list (like Send to Bottom and Bring to Top in layer ordering), and delete parts. We do this in a method called Game1.DrawPartsList(), as follows: for (int i = 0; i < charDef.Frames[selFrame].Parts.Length; i++) { int y = 5 + i * 15; text.Size = 0.75f; string line = ""; int index = charDef.Frames[selFrame].Parts[i].Index; if (index < 0) line = ""; CHAPTER 5 ■ THE CHARACTER EDITOR 109 else if (index < 64) line = "head" + index.ToString(); else if (index < 74) line = "torso" + index.ToString(); else if (index < 128) line = "arms" + index.ToString(); else if (index < 192) line = "legs" + index.ToString(); else line = "weapon" + index.ToString(); if (selPart == i) { text.Color = Color.Lime; text.DrawText(600, y, i.ToString() + ": " + line); We’ll put in two buttons to swap the current part with the one on the previous or next layer, using a function named Game1.SwapParts(): if (DrawButton(700, y, 1, mouseState.X, mouseState.Y, mouseClick)) { SwapParts(selPart, selPart - 1); if (selPart > 0) selPart ; } if (DrawButton(720, 5 y, 2, mouseState.X, mouseState.Y, mouseClick)) { SwapParts(selPart, selPart + 1); if (selPart < charDef.Frames[selFrame].Parts.Length - 1) selPart++; } We’ll put some makeshift buttons next to the swap buttons to modify the parts. One of these is to mirror parts. For the mirror button, we’ll use an (n) for normal and an (m) for mirrored. Part part = charDef.Frames[selFrame].Parts[selPart]; if (text.DrawClickText(740, y, (part.Flip == 0 ? "(n)" : "(m)"), mouseState.X, mouseState.Y, mouseClick)) { part.Flip = 1 - part.Flip; } Because scaling leaves all sorts of openings for things to go terribly wrong in artistic consis- tency, we’ll put in a button next to the selected part to reset the scale, denoted with an (r). We’ll also add a part delete button, marked with an (x). 110 CHAPTER 5 ■ THE CHARACTER EDITOR if (text.DrawClickText(762, y, "(r)", mouseState.X, mouseState.Y, mouseClick)) { part.Scaling = new Vector2(1.0f, 1.0f); } if (text.DrawClickText(780, y, "(x)", mouseState.X, mouseState.Y, mouseClick)) { part.Index = -1; } } else { if (text.DrawClickText(600, y, i.ToString() + ": " + line, mouseState.X, mouseState.Y, mouseClick)) { selPart = i; } } } Earlier in Draw(), add a line to draw the character: DrawCharacter(new Vector2(400f, 450f), 2f, FACE_RIGHT, selFrame, false, 1.0f); Also, put in a line to draw the black box under the parts list (for contrast). You’ll end up with what you see in Figure 5-7. Define selPart at the class level to indicate which part is currently selected. We’ll ultimately end up with class-level variables selFrame, selKeyFrame, and selAnim as well. Remember Game1.SwapParts()? We use it while changing part layers. Here’s the code: private void SwapParts(int idx1, int idx2) { if (idx1 < 0 || idx2 < 0 || idx1 >= charDef.Frames[selFrame].Parts.Length || idx2 >= charDef.Frames[selFrame].Parts.Length) return; Part i = charDef.Frames[selFrame].Parts[idx1]; Part j = charDef.Frames[selFrame].Parts[idx2]; charDef.Frames[selFrame].Parts[idx1] = j; charDef.Frames[selFrame].Parts[idx2] = i; } Notice that when this is called from Game1.Draw(), we don’t check any bounds there. We escape in the first four lines if we’re out of bounds here. CHAPTER 5 ■ THE CHARACTER EDITOR 111 Figure 5-7. Parts list It’s the classic swap algorithm, t = i; i = j; j = t, but it’s applied to two objects, so we store the references temporarily, rather than storing the values themselves. Moving, Rotating, and Scaling Parts Now we can specify part icons, but we can’t move them, so we can only end up with a head, arms, and legs in a heap on the floor, which isn’t what we’re really going for. We need to be able to manipulate parts. We allow that with the following code: int xM = mouseState.X - preState.X; int yM = mouseState.Y - preState.Y; if (mouseState.LeftButton == ButtonState.Pressed) { if (preState.LeftButton == ButtonState.Pressed) { charDef.Frames[selFrame].Parts[selPart].Location += new Vector2((float)xM / 2.0f, (float)yM / 2.0f); } } 112 CHAPTER 5 ■ THE CHARACTER EDITOR else { if (preState.LeftButton == ButtonState.Pressed) { mouseClick = true; } } if (mouseState.RightButton == ButtonState.Pressed) { if (preState.RightButton == ButtonState.Pressed) { charDef.Frames[selFrame].Parts[selPart].Rotation += (float)yM / 100.0f; } } if (mouseState.MiddleButton == ButtonState.Pressed) { if (preState.MiddleButton == ButtonState.Pressed) { charDef.Frames[selFrame].Parts[selPart].Scaling += new Vector2((float)xM * 0.01f, (float)yM * 0.01f); } } preState = mouseState; This should be fairly self-explanatory. For each click type, we figure out how far the mouse has moved since the last update, and then translate, rotate, or scale accordingly. We’re using left-button dragging for moving, right-button dragging for rotating, and middle-button dragging for scaling. We’re now able to move, rotate, and scale parts, so we can finally get a look at what we’re shooting for with this character format. Take a look at our guy in Figure 5-8, which should give you a much better idea of what we’re creating. CHAPTER 5 ■ THE CHARACTER EDITOR 113 Figure 5-8. Our hero (assembled) The Frames List The character you see in Figure 5-8 is one frame. If we’re going to have animation, we’ll need a series of frames. Figure 5-8 could be idle1. Then we would need idle2, idle3, and so on. Let’s create a frames list in Game1.DrawFramesList(): for (int i = frameScroll; i < frameScroll + 20; i++) { if (i < charDef.Frames.Length) { int y = (i - frameScroll) * 15 + 280; if (i == selFrame) { text.Color = Color.Lime; text.DrawText(600, y, i.ToString() + ": " + charDef.Frames[i].Name + (editMode == EditingMode.FrameName ? "*" : "")); Remember how we edited text in the map editor? We’re using a similar system here. We use the class-level variable editingMode to keep track of which field we’re editing, and then 114 CHAPTER 5 ■ THE CHARACTER EDITOR from Game1.Update(), we call UpdateKeys(), which may call PressKey(). We can basically copy the code over from MapEditor, with a few changes, which we’ll get to soon. Next to the selected frame, we’ll draw a little add frame button, denoted with an (a). Clicking this button will add a reference to this frame to the selected animation. if (text.DrawClickText(720, y, "(a)", mouseState.X, mouseState.Y, mouseClick)) { Animation animation = charDef.Animations[selAnim]; for (int j = 0; j < animation.KeyFrames.Length; j++) { KeyFrame keyFrame = animation.KeyFrames[j]; if (keyFrame.FrameRef == -1) { keyFrame.FrameRef = i; keyFrame.Duration = 1; break; } } } } else { if (text.DrawClickText(600, y, i.ToString() + ": " + charDef.Frames[i].Name, mouseState.X, mouseState.Y, mouseClick)) { When selecting a frame, two things happen. If the frame’s name was empty, we copy the previously selected frame to the current frame. This isn’t very intuitive, but it works. Also, we make the currently selected frame’s name editable. if (selFrame != i) { if (String.IsNullOrEmpty(charDef.Frames[i].Name)) CopyFrame(selFrame, i); selFrame = i; editingText = EDITING_FRAME_NAME; } } } } } CHAPTER 5 ■ THE CHARACTER EDITOR 115 Finally, we allow our list to be scrolled. if (DrawButton(770, 280, 1, mouseState.X, mouseState.Y, (mouseState.LeftButton == ButtonState.Pressed)) && frameScroll > 0) frameScroll ; if (DrawButton(770, 570, 2, mouseState.X, mouseState.Y, (mouseState.LeftButton == ButtonState.Pressed)) && frameScroll < charDef.Frames.Length - 20) frameScroll++; We can now create several frames of animation, as shown in Figure 5-9. Figure 5-9. The frames list Let’s take a little look at Game1.PressKey(). In MapEditor, we would evaluate editMode, copy an appropriate string into a temporary string, work with that temporary string, and then copy the string back. All that we change here is where we’re copying that string to and from: 116 CHAPTER 5 ■ THE CHARACTER EDITOR string t = ""; switch (editMode) { case EditingMode.FrameName: t = charDef.Frames[selFrame].Name; break; case EditingMode.AnimationName: t = charDef.Animations[selAnim].Name; break; case EditingMode.PathName: t = charDef.path; break; default: return; } switch (editMode) { case EditingMode.FrameName: charDef.Frames[selFrame].Name = t; break; case EditingMode.AnimationName: charDef.Animations[selAnim].Name = t; break; case EditingMode.PathName: charDef.Path = t; break; } We’ll be editing frame names, animation names, and paths, and we’ve defined some constants appropriately. Also, we use a nonintuitive method for copying frames: if the user selects a frame that has a blank name (that is, a fresh, unused frame under typical circumstances), the previously selected frame will be copied onto the new one using the CopyFrame() method. Here’s Game1.CopyFrame(): private void CopyFrame(int src, int dest) { Frame keySrc = charDef.Frames[src]; Frame keyDest = charDef.Frames[dest]; keyDest.Name = keySrc.Name; [...]... taking this in two chunks: first create our rudimentary game engine, and then create scripting By the time you’re finished with this chapter, you’ll have a very slick-looking start to Zombie Smashers XNA Building the Game To put together our game, we’ll need to set up our environment, and then get to coding Specifically, we’ll do the following: • Create a new project in our game’s solution • Copy over... simple movement and collision detection So, let’s get started with a new ZombieSmashers project 127 128 CHAPTER 6 ■ BRINGING IT TO THE GAME Creating a New Project: ZombieSmashers Create a new Windows Game (2.0) called ZombieSmashers in our ever-growing ZombieSmashers solution Again, by putting most of our code into a single solution, we can leverage the quickaccess capabilities of Visual Studio to easily... moving the player could cause a frame to be drawn with some overlap In reality, the order is up to you and how you like to keep the separate functions organized Here is the rundown for our Zombie Smashers XNA game: • Update animation • Update location • Perform collision detection • Handle key input The order isn’t all that sensitive right now, because of how independent each action is from the others What . DrawCharacter(new Vector2( 400 f, 450f), 2f, FACE_RIGHT, selFrame + 1, false, 0. 2f); DrawCharacter(new Vector2( 400 f, 450f), 2f, FACE_RIGHT, selFrame, false, 1.0f); Figure 5- 10 shows our animations. appear in the frames list. 1 20 CHAPTER 5 ■ THE CHARACTER EDITOR if (selFrame > 0) DrawCharacter(new Vector2( 400 f, 450f), 2f, FACE_RIGHT, selFrame - 1, false, 0. 2f); if (selFrame < charDef.GetFrameArray().Length. CHARACTER EDITOR 121 int fref = charDef.Animations[selAnim].KeyFrames[curKey].FrameRef; if (fref < 0) fref = 0; DrawCharacter(new Vector2( 500 f, 100 f), 0. 5f, FACE_LEFT, fref, true, 1.0f); We also

Ngày đăng: 01/07/2014, 22:20

TỪ KHÓA LIÊN QUAN

w