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

Building XNA 2.0 Games- P11 doc

30 261 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 896,13 KB

Nội dung

288 CHAPTER 9 ■ SCRIPTING, AI, AND DEPTH (AND DEATH) ), rotation + frame / 4f, new Vector2(32.0f, 32.0f), size, SpriteEffects.None, 1.0f); } } Our map scripting system is now in place. We just need to add our MapFlags class. public class MapFlags { String[] flags; public MapFlags(int size) { flags = new String[size]; for (int i = 0; i < flags.Length; i++) flags[i] = ""; } public bool GetFlag(String flag) { for (int i = 0; i < flags.Length; i++) { if (flags[i] == flag) return true; } return false; } public void SetFlag(String flag) { if (GetFlag(flag)) return; for (int i = 0; i < flags.Length; i++) { if (flags[i] == "") { flags[i] = flag; return; } } } } There’s one last bit of cleanup to do: remove the for loop in our Game1 initialize routine that spawns our ten zombies. We’ll be spawning all of our zombies from map scripts from here on out. CHAPTER 9 ■ SCRIPTING, AI, AND DEPTH (AND DEATH) 289 With our map in place using its brand-new script, run ZombieSmashers. You’ll see some- thing like Figure 9-7. Figure 9-7. Final product of this chapter Conclusion We’ve covered some fairly varied ground in this chapter. The main goal was to introduce map scripting, but we ended up adding quite a bit of functionality to our characters: blood sprays, initialization and death, and rudimentary AI. We know that we say this every chapter, but we’re a lot closer to having a fleshed-out game. To recap, we accomplished the following: • Added all sorts of blood sprays and spurts to the character editor • Created zombie attack, decap, and bloodsplode animations • Added initialization scripts • Implemented the new blood effects in our game • Implemented character initialization and death •Added AI • Defined a map scripting language • Added script-editing functionality to the map editor • Introduced map level and global level flags •Added fog 290 CHAPTER 9 ■ SCRIPTING, AI, AND DEPTH (AND DEATH) In the next chapter, we’ll introduce player death, map transitions, a HUD, and game menus. As we near the completion of our game, it is important to keep our head in the game and not get distracted by the want or need to change things that are done. One of the biggest prob- lems that plagues independent developers is their constant desire to fix old code, no matter how well it works. The best thing to do at this point is finish the game first, and then go back. 291 ■ ■ ■ CHAPTER 10 Menus, a HUD, and Deployment At Last, the Coveted Xbox 360 Deployment We’ll warn you right off the bat: this is going to be another odds-and-ends chapter. The initial design was to produce all of the interface-related items we would need: a health bar, score, map transitions, and a main menu. However, since these tie in with the constraints we’ll be dealing with on the Xbox 360, and because thus far we haven’t touched the thing, it seemed like a perfect time to introduce the concept of deployment. If you don’t have an Xbox 360 and/or an XNA Creators Club membership, you can bliss- fully skip the deployment section of this chapter. But, honestly, if you’ve gotten this far in the book and still don’t have any intention of playing around with an Xbox 360, you’ve got some issues. Granted, they might be good issues (thriftiness?), but they are issues nonetheless. Here’s the rundown of what we’ll be doing in this chapter: • Add a player HUD to display the health and score. We’ll need to implement scoring and map transitions, and add scripting functionality to support map transitions. • Add a menu system. • Implement player death. We’ll need to have a menu system in place to deal with this. • Dive into Xbox 360 deployment. There will be a bit of “we need to do A, but to get there we need to have B and C in place,” so bear with us. Adding a HUD Our HUD will consist of a row of five hearts for our health and a score, as shown in Figure 10-1. The hearts will just be a visual representation of our integer health value, not some sort of atomic unit of health. For instance, if you have 82/100 HP, you’ll have 4.1 hearts. We just thought hearts would look cute. 292 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT Figure 10-1. Health and score display Creating the HUD Class The HUD class will deal with all things related to the interface: updating the health display and fading transitions, and drawing the health, score, and transitions. Just like everything else so far, we’ll be calling the Update() and Draw() functions from Game1. class HUD { SpriteBatch sprite; Texture2D spritesTex; Texture2D nullTex; Character[] character; Map map; We’ll be using a new object, scoredraw, to draw numbers. “But wait,” you may be saying, “why not just use a text class and ToString() to draw text?” The long and the short of it is that ToString() generates a ton of garbage when used every frame; this kills performance on the Xbox 360. We’ll use a more efficient algorithm that you may have seen in a Computer Science 101 class: ScoreDraw scoreDraw; We will talk more about ScoreDraw in the next section. We’ll use the field heartFrame to let our hearts sort of waver in a classic comic, cutesy fashion. We’re using the fHP field (think floating health points) for a sort of catch-up health bar. When we take damage or get health, we want our health bar to smoothly transition from the previous value to the current value. When we call HUD.Update(), we’ll have the floating bar try to get to CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 293 where the real bar is. Then when we Draw(), we’ll draw more prominently at the floating posi- tion than the real position. This technique is infinitely more professional-looking than just drawing the current health value. float heartFrame; float fHP; For the constructor, we’ll just send it all of the objects it will need: the ever-present SpriteBatch, some textures, and the Character array and Map. public HUD(SpriteBatch _sprite, Texture2D _spritesTex, Texture2D _nullTex, Character[] _character, Map _map) { sprite = _sprite; spritesTex = _spritesTex; character = _character; map = _map; nullTex = _nullTex; scoreDraw = new ScoreDraw(sprite, spritesTex); } As promised, the Update() function increments our heartFrame and tries to get fHP to match with our goal HP. public void Update() { heartFrame += Game1.FrameTime; if (heartFrame > 6.28f) heartFrame -= 6.28f; if (character[0].HP > fHP) { fHP += Game1.FrameTime * 15f; if (fHP > character[0].HP) fHP = character[0].HP; } if (character[0].HP < fHP) { fHP -= Game1.FrameTime * 15f; if (fHP < character[0].HP) fHP = character[0].HP; } } Our Draw() method will first draw the score, then some black background hearts (our floating health hearts), and finally our real health hearts. We’re using the same sprites texture we previously used it for smoke, flame, and muzzle flashes, but we’ll add some hearts and numbers to it. The new image is shown in Figure 10-2. 294 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT Figure 10-2. Updated sprites texture The hearts all start at 0, 192 and are 32 ✕ 32. public void Draw() { sprite.Begin(SpriteBlendMode.AlphaBlend); scoreDraw.Draw(Game1.Score, new Vector2(50f, 78f), Color.White, Justification.Left); Likening our health meter to a progress bar, we’ll call our floating health value fProg and our health value prog. Each value will be between 0 and 5, since we’re using 5 hearts. When we iterate through our five hearts, we can figure out if we’re using a full heart, no heart, or a portion of a heart by the difference between the currently drawn heart and our progress value. float fProg = fHP / character[0].MHP; float prog = character[0].HP / character[0].MHP; fProg *= 5f; prog *= 5f; for (int i = 0; i < 5; i++) { We’ll be using the r float to determine how much to rotate our hearts. The value is a func- tion of our heartFrame value and the current heart index. This way, the hearts don’t all rotate in sync. Since it’s a cosine function, the hearts bob one way or another. CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 295 float r = (float)Math.Cos((double)heartFrame * 2.0 + (double)i) * 0.1f; First, we draw the dark background hearts: sprite.Draw(spritesTex, new Vector2( 66f + (float)i * 32f, 66f), new Rectangle(i * 32, 192, 32, 32), new Color(new Vector4(0.5f, 0f, 0f, .25f)), r, new Vector2(16f, 16f), 1.25f, SpriteEffects.None, 1f); Next, we compute how much of a heart is shown, by getting the difference between the progress value and the current heart index, and draw the floating health heart: float ta = fProg - (float)i; if (ta > 1f) ta = 1f; if (ta > 0f) { sprite.Draw(spritesTex, new Vector2( 66f + (float)i * 32f, 66f), new Rectangle(i * 32, 192, (int)(32f * ta), 32), new Color(new Vector4(1f, 0f, 0f, .75f)), r, new Vector2(16f, 16f), 1.25f, SpriteEffects.None, 1f); } Finally, compute another heart sliver width and draw the real health heart: ta = prog - (float)i; if (ta > 1f) ta = 1f; if (ta > 0f) { sprite.Draw(spritesTex, new Vector2( 66f + (float)i * 32f, 66f), new Rectangle(i * 32, 192, (int)(32f * ta), 32), new Color(new Vector4(.9f, 0f, 0f, 1f)), r, new Vector2(16f, 16f), 1.25f, SpriteEffects.None, 1f); } } Lastly, we’re implementing some fade-to-black functionality. We’ll have transition values in the map soon, for entering and exiting segments, as well as for when we first start a game. Based on where the map is transition-wise, we’ll draw our nullTex over the entire screen with an appropriate alpha value. 296 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT float a = map.GetTransVal(); if (a > 0f) { sprite.Draw(nullTex, new Rectangle(0, 0, (int)Game1.ScreenSize.X, (int)Game1.ScreenSize.Y), new Color( new Vector4(0f, 0f, 0f, a))); } sprite.End(); } } Drawing the Score We referred to a ScoreDraw class earlier. Let’s define it here: public enum Justification { Left = 0, Right } class ScoreDraw { SpriteBatch spriteBatch; Texture2D spritesTex; public ScoreDraw(SpriteBatch _spriteBatch, Texture2D _spritesTex) { spriteBatch = _spriteBatch; spritesTex = _spritesTex; } For our drawing, we’re just applying a simple loop to our original score. We calculate modulus 10, draw the modulus 10 value, divide by 10, shift our position left, and repeat until nothing remains. For instance, if we give it the number 123, we’ll do this: • Compute 123 mod 10 = 3 • Draw 3, shift left a bit • Divide 123 by 10 = 12 • Compute 12 mod 10 = 2 • Draw 2, shift left a bit • Divide 12 by 10 = 1 • Compute 1 mod 10 = 1 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 297 • Draw 1, shift left a bit • Divide 1 by 10 = 0 • Fini! Computer science professors love to use this problem as an introduction to modulus arithmetic. public void Draw(long score, Vector2 loc, Color color, Justification justify) { int place = 0; The obnoxiously ugly part is in left-justified text. Drawing and shifting left as necessary is fine, but if we can’t draw and shift right, we would get a reverse score. Instead, we apply our divide-by-10 loop to the score to determine the entire string width, shift our draw position right by that much, and proceed as normal. if (justify == Justification.Left) { loc.X -= 17f; long s = score; if (s == 0) loc.X += 17f; else while (s > 0) { s /= 10; loc.X += 17f; } } The numbers use the same sprites texture. They start at 0, 224, and their dimensions are 16 ✕ 32. while (true) { long digit = score % 10; score = score / 10; spriteBatch.Draw(spritesTex, loc + new Vector2((float)place * -17f, 0f), new Rectangle((int)digit * 16, 224, 16, 32), color); place++; if (score <= 0) return; } } } . left a bit • Divide 123 by 10 = 12 • Compute 12 mod 10 = 2 • Draw 2, shift left a bit • Divide 12 by 10 = 1 • Compute 1 mod 10 = 1 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 29 7 • Draw 1, shift. sprite.Draw(spritesTex, new Vector2( 66f + (float)i * 32f, 66f), new Rectangle(i * 32, 1 92, 32, 32) , new Color(new Vector4 (0. 5f, 0f, 0f, .25 f)), r, new Vector2(16f, 16f), 1 .25 f, SpriteEffects.None, 1f); Next,. > 0f) { sprite.Draw(spritesTex, new Vector2( 66f + (float)i * 32f, 66f), new Rectangle(i * 32, 1 92, (int)(32f * ta), 32) , new Color(new Vector4(1f, 0f, 0f, .75f)), r, new Vector2(16f,

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