using System; using System.IO; using System.Linq; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage; namespace XNAPacMan { /// /// This is a game component that implements IUpdateable. /// /// Optionally takes a GameLoop argument, when the menu must be able to /// resume the current GameLoop. Otherwise, the reference would be lost /// and the gameLoop garbage collected. public class Menu : Microsoft.Xna.Framework.DrawableGameComponent { public Menu(Game game, GameLoop gameLoop) : base(game) { gameLoop_ = gameLoop; gameStart_ = (gameLoop == null); } /// /// Allows the game component to perform any initialization it needs to before starting /// to run. This is where it can query for any required services and load content. /// public override void Initialize() { spriteBatch_ = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); graphics_ = (GraphicsDeviceManager)Game.Services.GetService(typeof(GraphicsDeviceManager)); soundBank_ = (SoundBank)Game.Services.GetService(typeof(SoundBank)); selection_ = 0; if (gameLoop_ == null) { items_ = new string[] { "New Game", "High Scores", "Quit" }; } else { items_ = new string[] { "Resume", "Quit Game" }; } menuItem_ = Game.Content.Load("MenuItem"); title_ = Game.Content.Load("sprites/Title"); selectionArrow_ = Game.Content.Load("sprites/Selection"); oldState_ = Keyboard.GetState(); base.Initialize(); } /// /// Allows the game component to update itself. /// /// Provides a snapshot of timing values. public override void Update(GameTime gameTime) { base.Update(gameTime); // Wonder why we test for this condition? Just replace gameStart_ by true and // try running the game. The answer should be instantaneous. if (gameStart_) { soundBank_.PlayCue("NewLevel"); gameStart_ = false; } KeyboardState newState = Keyboard.GetState(); // Get keys pressed now that weren't pressed before var newPressedKeys = from k in newState.GetPressedKeys() where !(oldState_.GetPressedKeys().Contains(k)) select k; // Scroll through menu items if (newPressedKeys.Contains(Keys.Down)) { selection_++; selection_ %= items_.Length; soundBank_.PlayCue("PacMAnEat1"); } else if (newPressedKeys.Contains(Keys.Up)) { selection_--; selection_ = (selection_ < 0? items_.Length -1 : selection_); soundBank_.PlayCue("PacManEat2"); } else if (newPressedKeys.Contains(Keys.Enter)) { menuAction(); } // Update keyboard state for next update oldState_ = newState; } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. public override void Draw(GameTime gameTime) { base.Draw(gameTime); // The menu is a main component, so it is responsible for initializing the sprite batch each frame spriteBatch_.Begin(); // Draw title spriteBatch_.Draw(title_, new Vector2((graphics_.PreferredBackBufferWidth / 2) - (title_.Width / 2), 75), Color.White); // Draw items Vector2 itemPosition; itemPosition.X = (graphics_.PreferredBackBufferWidth / 2) - 100; for (int i = 0; i < items_.Length; i++) { itemPosition.Y = (graphics_.PreferredBackBufferHeight / 2) - 60 + (60 * i); if (i == selection_) { spriteBatch_.Draw(selectionArrow_, new Vector2(itemPosition.X - 50, itemPosition.Y), Color.White); } spriteBatch_.DrawString(menuItem_, items_[i], itemPosition, Color.Yellow); } spriteBatch_.End(); } void menuAction() { Game.Components.Remove(this); switch (items_[selection_]) { case ("Resume"): Game.Components.Add(gameLoop_); break; case ("New Game") : Game.Components.Add(new GameLoop(Game)); break; case ("High Scores"): Game.Components.Add(new HighScores(Game)); break; case ("Quit"): Game.Exit(); break; case ("Quit Game"): Game.Components.Add(new Menu(Game, null)); SaveHighScore(gameLoop_.Score); break; default: throw new ArgumentException("\"" + items_[selection_] + "\" is not a valid case"); } } /// /// Keep a history of the best 10 scores /// /// New score to save, might make it inside the list, might not. public static void SaveHighScore(int highScore) { const string fileName = "highscores.txt"; if (!File.Exists(fileName)) { File.WriteAllLines(fileName, new string[] { highScore.ToString() }); } else { List contents = File.ReadAllLines(fileName).ToList(); contents.Add(highScore.ToString()); if (contents.Count >= 10) { contents.Sort((a, b) => Convert.ToInt32(a).CompareTo(Convert.ToInt32(b))); while (contents.Count > 10) { contents.RemoveAt(0); } } File.WriteAllLines(fileName, contents.ToArray()); } } GameLoop gameLoop_; SoundBank soundBank_; GraphicsDeviceManager graphics_; SpriteBatch spriteBatch_; SpriteFont menuItem_; string[] items_; int selection_; bool gameStart_; Texture2D title_; Texture2D selectionArrow_; KeyboardState oldState_; } }