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

Building XNA 2.0 Games- P12 pps

30 267 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,3 MB

Nội dung

318 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 1f - optionFrame[i], GetAlpha(true))), (1f - optionFrame[i]) * 1f, new Vector2(160f, 32f), 1f, SpriteEffects.None, 1f); } sprite.End(); If we’re not in dead mode, draw the second layer of fog and foreground graphic: if (menuMode != MenuMode.Dead) { sprite.Begin(SpriteBlendMode.Additive); pan *= 2f; for (int i = 0; i < fog.Length / 2; i++) { sprite.Draw(spritesTex, fog[i] + new Vector2(pan, 0f), new Rectangle((i % 4) * 64, 0, 64, 64), new Color(new Vector4(1f, 1f, 1f, .1f * GetAlpha(false))), (fog[i].X + fog[i].Y) / 100f, new Vector2(32f, 32f), (float)(i % 10) * .5f + 2f, SpriteEffects.None, 1f); } sprite.End(); sprite.Begin(SpriteBlendMode.AlphaBlend); sprite.Draw(poseForeTex, new Vector2(Game1.ScreenSize.X - (Game1.ScreenSize.Y / 480f) * 616f * GetAlpha(false) + (float)Math.Cos((double)frame) * 20f + 20f, Game1.ScreenSize.Y - (Game1.ScreenSize.Y / 480f) * 286f), new Rectangle(0, 0, 616, 286), new Color(new Vector4(GetAlpha(false), GetAlpha(false), GetAlpha(false), 1f)), 0f, new Vector2(), (Game1.ScreenSize.Y / 480f), SpriteEffects.None, 1f); sprite.End(); } } CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 319 Options Population Now, let’s take a look at our PopulateOptions() method. It’s just a bunch of cases again. The only odd bit is for the main level: if we’re in pause mode, our main level will be a bit different than for the other modes. private void PopulateOptions() { for (int i = 0; i < option.Length; i++) option[i] = Option.None; switch (level) { case Level.Main: if (menuMode == MenuMode.Pause) { option[0] = Option.ResumeGame; option[1] = Option.EndGame; option[2] = Option.Options; option[3] = Option.Quit; totalOptions = 4; } else { option[0] = Option.NewGame; option[1] = Option.Continue; option[2] = Option.Options; option[3] = Option.Quit; totalOptions = 4; } break; case Level.Options: option[0] = Option.Back; totalOptions = 1; break; case Level.Dead: option[0] = Option.EndGame; option[1] = Option.Quit; totalOptions = 2; break; default: totalOptions = 0; break; } } 320 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT Pausing and Dying We’ll be using the Pause() and Die() methods from elsewhere in our game to set all of the appropriate flags to pause or go into you-are-dead mode. public void Pause() { menuMode = MenuMode.Pause; Game1.GameMode = Game1.GameModes.Menu; transFrame = 1f; level = Level.Main; transType = Trans.All; } public void Die() { menuMode = MenuMode.Dead; Game1.GameMode = Game1.GameModes.Menu; transFrame = 1f; level = Level.Dead; transType = Trans.All; } } That concludes our big bad Menu class. We’ve done a lot of coding this chapter, but it has all been fairly simple, using techniques that are definitely not new. We are going to keep going, because we need to clean up and update our current classes before we run the game. Updating the Game We need to plug everything in to Game1, and we also need to sort out some stuff. Adding the HUD and Menu to the Game We’ll start at the class level of Game1 by declaring our HUD and Menu. We also need a new enumer- ation called GameModes, which we’ll use to define the current state: playing or at the menu. Remember that paused and dead count as being in the menu. HUD hud; public enum GameModes : int { Menu = 0, Playing = 1 } CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 321 private static Menu menu; private static long score = 0; private static GameModes gameMode; public static GameModes GameMode { get { return gameMode; } set { gameMode = value; } } public static Menu Menu { get { return menu; } set { menu = value; } } public static long Score { get { return score; } set { score = value; } } From Menu.Update(), we called NewGame() and Quit(). Let’s define them next. NewGame() clears all characters and particles, sets the map path to start, resets the map flags, reads the map, sets the map transition direction to Intro, and tells the map that it is tran- sitioning in. When the map is loaded and the game mode switches over to GameMode.Playing, our setintroentrance command will see that we are in a TransitionDirection.Intro transition and plant our new character at the intro location we gave it. public void NewGame() { gameMode = GameModes.Playing; character[0] = new Character(new Vector2(100f, 100f), CharDefs[(int)CharacterDefinitions.Guy], 0, Character.TEAM_GOOD_GUYS); character[0].HP = character[0].MHP = 100; for (int i = 1; i < character.Length; i++) character[i] = null; pManager.Reset(); map.Path = "start"; map.GlobalFlags = new MapFlags(64); map.Read(); map.TransDir = TransitionDirection.Intro; map.transInFrame = 1f; 322 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT } public void Quit() { this.Exit(); } In LoadContent(), we’ll create our new Menu and HUD, sending them all of the right textures. Since we won’t need our pose, pose foreground, and options textures elsewhere, we can load them directly in the constructor, rather than loading them in the Game1 scope and passing a reference. nullTex = Content.Load<Texture2D>(@"gfx/1x1"); menu = new Menu( Content.Load<Texture2D>(@"gfx/pose"), Content.Load<Texture2D>(@"gfx/posefore"), Content.Load<Texture2D>(@"gfx/options"), mapBackTex[0], spritesTex, spriteBatch); hud = new HUD(spriteBatch, spritesTex, nullTex, character, map); Next, we need to do some reorganizing. Reorganizing the Code We previously updated all of the game logic in Update() and all of the game-drawing logic in Draw(). However, now that we have two game modes, we need some more complicated cases to determine whether we want to update game logic or draw the game, so let’s move the code into UpdateGame() and DrawGame(), respectively. The UpdateGame() method (well, most of it) looks like this: private void UpdateGame() { scroll += ((character[0].loc - new Vector2(400f, 400f)) - scroll) * frameTime * 20f; if (scroll.Y > yLim) scroll.Y = yLim; if (map.transOutFrame <= 0f) { pManager.UpdateParticles(frameTime, map, character); if (character[0] != null) { CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 323 } for (int i = 0; i < character.Length; i++) { if (character[i] != null) { character[i].Update(map, pManager, character); if (character[i].dyingFrame > 1f) { } } } } map.Update(pManager, character); hud.Update(); } The basic functionality is the same as the code lifted from Update(), but we won’t be updating the particles or characters if the map is transitioning out. Otherwise, we would be able to walk to the edge, trigger a transition, and start walking back in the opposite direction, which would look all wrong! Also, we added a hud.Update() at the end. Similarly, DrawGame() takes a big chunk from Draw(). The only change we’re doing for now is to draw the main screen a bit darker if we’re paused or dead. When we start playing with shaders in the next chapter, we’ll be able to draw the main screen in a grayscale or sepia tone if the pause menu is overlaid. private void DrawGame() { graphics.GraphicsDevice.SetRenderTarget(0, mainTarget); graphics.GraphicsDevice.Clear(Color.Black); map.Draw(spriteBatch, mapsTex, mapBackTex, 0, 2); graphics.GraphicsDevice.SetRenderTarget(0, null); spriteBatch.Begin(SpriteBlendMode.None); spriteBatch.Draw(mainTarget.GetTexture(), new Vector2(), (gameMode == GameModes.Menu ? Color.Gray : Color.White)); spriteBatch.End(); 324 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT spriteBatch.Begin(SpriteBlendMode.AlphaBlend); if (QuakeManager.blast.val > 0f) { } spriteBatch.End(); } Now that we’ve extracted some important functionality from Update() and Draw(), we need to sort things out within these methods. The sound-updating and frameTime-calculating stuff is the same, but if we’re in you-are-dead menu mode, we still want to update the game, albeit slightly slower. protected override void Update(GameTime gameTime) { Sound.Update(); Music.Play("music1"); QuakeManager.Update(); frameTime = (float)gameTime.ElapsedGameTime.TotalSeconds; if (slowTime > 0f) { slowTime -= frameTime; frameTime /= 10f; } switch (gameMode) { case GameModes.Playing: UpdateGame(); break; case GameModes.Menu: if (menu.menuMode == Menu.MenuMode.Dead) { float pTime = frameTime; frameTime /= 3f; UpdateGame(); frameTime = pTime; } menu.Update(this); break; } base.Update(gameTime); } CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 325 Our Draw() method shrinks a bit, too. If we’re playing the game, we’ll draw the game and then draw the HUD. If we’re in menu mode, we’ll draw the menu. If it’s a pause or dead menu, we’ll make sure the game gets drawn under it. We need to draw the HUD only while we’re playing the game. If the HUD is shown while the pause or dead menu is up, the interface gets a bit crowded. protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.Black); switch (gameMode) { case GameModes.Playing: DrawGame(); hud.Draw(); break; case GameModes.Menu: if (menu.menuMode == Menu.MenuMode.Pause || menu.menuMode == Menu.MenuMode.Dead) DrawGame(); menu.Draw(); break; } base.Draw(gameTime); } This should complete the vicious cycle we’ve just introduced. We now have a pause screen, as shown in Figure 10-11. We also have a you-are-dead screen, as shown in Figure 10-12. Figure 10-11. Pausing the game 326 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT Figure 10-12. After death Lastly, and compared to the rest of this, certainly least, we should implement some sort of functionality for scoring. Scoring We’ll add some fairly basic scoring functionality. Since score is a public static in Game1, it’s simple enough to set from anywhere. We’ll be setting it from Character.KillMe() and from HitManager.CheckHit(). Also, we need to add a new field to Character at the class level: public int LastHitBy = -1; A Character can use this to determine who hit him last. In HitManager, after we’ve determined a successful hit, we’ll set lastHitBy to the index of the hit owner. if (c[i].InHitBounds(p.Location)) { float hVal = 1f; c[i].LastHitBy = p.Owner; Further down, after we’ve fully calculated hVal, we’ll add some points (50 times hVal) to the static score value if the hit owner index is 0. if (c[i].LastHitBy == 0) Game1.Score += (int)hVal * 50; CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT 327 In Character, we’ll add more to the score if the character was last hit by character index 0. public void KillMe() { if (DyingFrame < 0f) { DyingFrame = 0f; if (LastHitBy == 0) Game1.Score += MHP * 50; } } There we have it—scoring is implemented! Our final product is shown in Figure 10-13. Figure 10-13. In-game scoring ■Note Scoring in The Dishwasher game used a fairly complicated combo system. Any points scored would feed into a combo score, and combo hits and kills would increase the combo multiplier. Once a combo ended, the combo score would be multiplied by the combo multiplier, and the final score would be added to the main player score (think Tony Hawk with buckets of blood). It added a lot of strategy to the combat for players seeking the best scores. For our Zombie Smashers XNA game, we’ll leave it up to you to implement more complex scoring, if that interests you. [...]... Marketplace Select Game Store ➤ All Games ➤ XNA Creators Club Select and download XNA Game Studio Connect After your download is finished, navigate to the Games blade, select Games Library ➤ My Games, and find and launch XNA Game Studio Connect You’ll see a connection key, as shown in Figure 10-17 329 330 CHAPTER 10 ■ MENUS, A HUD, AND DEPLOYMENT Figure 10-17 Running XNA Game Studio Connect Jog (or swivel)... 1000+ ms GC calls every minute or so You can use the XNA Framework Remote Performance Monitor for Xbox 360 (available on the Start ➤ XNA Game Studio 2.0 ➤ Tools menu) to track collections for clues on when and how much garbage is being produced However, what we’ve found to be really helpful is the CLR Profiler The CLR Profiler for the NET Framework 2.0 is a free application available for download from... XBox 360 Now you need to add the Xbox 360 to the Device Center In the XNA Game Studio Device Management 2.0 toolbar, click Add New Device You’re prompted to enter a device name, as shown in Figure 10-16 Figure 10-16 Adding a new device At this point, jog (or swivel) over to your Xbox 360 You’ll need to download an application called XNA Game Studio Connect On your Xbox 360, sign in with your Live-enabled... Xbox 360 a name and click Next You’re prompted to enter a connection key, as shown in Figure 10-18 Plug in the numbers from XNA Game Studio Connect and click Next You should see a dialog saying that you’re ready to go Figure 10-18 You’ll need to enter the connection key from XNA Game Studio Connect This is a 99% painless process If you’re using some old analog TV for your Xbox 360 (we use one of these... Let’s tackle the fun part: trying the game on Xbox 360 For this, we’ll need to create a new project and connect the Xbox 360 to XNA Game Studio If you have not yet purchased a Creators Club Premium Membership (required to deploy to Xbox 360), you can do so at http://creators .xna. com/en-US/membership You’ll be required to have a Gamertag and at least a four-month subscription (currently $49) Creating... and so on), blurring, bloom, and water We’ll use pixel shaders to generate these effects Pixel shaders—once scary, inaccessible, complicated bits of programming—are quite easy to work with in XNA Game Studio 2.0 Here’s what we’ll be doing: • Create a color filter effect • Modify our main game logic to load and implement the color filter • Implement a water effect (must be implemented in the map script... really helpful is the CLR Profiler The CLR Profiler for the NET Framework 2.0 is a free application available for download from Microsoft.com If you use it to launch the x86 binary (it’s in ZombieSmashersXna/bin/x86), you’ll end up with an enormous (gigabytes, not megabytes) log file, detailing pretty much everything that ever occurred since launch In the Summary section, you can view a timeline of GC... float4 Pause(PS_INPUT Input) : COLOR0 { float4 col = 0; for (int i = 0; i < 12; i++) { col += tex2D(samplerState, Input.TexCoord + 0.005 * offsets[i]); } float a = (col.r + col.g + col.b) / 3.0f; a /= 12.0f; col.rgb = a; 337 338 CHAPTER 11 ■ POSTPROCESSING EFFECTS return col; } technique PauseTechnique { pass P0 { PixelShader = compile ps_2_0 Pause(); } } Let’s plug it in Just as with the negative effect, . -0. 326 2 12, -0. 405 805 , -0. 8 401 44, -0. 0735 80, -0. 695914, 0. 457137, -0 . 20 3345, 0. 6 20 716, 0. 9 623 40, -0. 194983, 0. 473434, -0. 4 80 026 , 0. 519456, 0. 767 02 2 , 0. 185461, -0. 893 124 , 0. 507 431, 0. 064 425 , . 0. 185461, -0. 893 124 , 0. 507 431, 0. 064 425 , 0. 896 4 20 , 0. 4 124 58, -0. 321 9 40, -0. 9 326 15, -0. 791559, -0. 597 705 , }; struct PS_INPUT { float2 TexCoord : TEXCOORD0; }; For our main function, we’ll sum. GameModes.Playing; character [0] = new Character(new Vector2( 100 f, 100 f), CharDefs[(int)CharacterDefinitions.Guy], 0, Character.TEAM_GOOD_GUYS); character [0] .HP = character [0] .MHP = 100 ; for (int i =

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

w