using System; using System.IO; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; using Microsoft.Xna.Framework.Media; namespace Platformer1 { /// /// This is the main type for your game /// public class PlatformerGame : Microsoft.Xna.Framework.Game { // Resources for drawing. private GraphicsDeviceManager graphics; private SpriteBatch spriteBatch; // Global content. private SpriteFont hudFont; private Texture2D winOverlay; private Texture2D loseOverlay; private Texture2D diedOverlay; // Meta-level game state. private int levelIndex = -1; private Level level; private bool wasContinuePressed; // When the time remaining is less than the warning time, it blinks on the hud private static readonly TimeSpan WarningTime = TimeSpan.FromSeconds(30); #if ZUNE private const int TargetFrameRate = 30; private const int BackBufferWidth = 240; private const int BackBufferHeight = 320; private const Buttons ContinueButton = Buttons.B; #else private const int TargetFrameRate = 60; private const int BackBufferWidth = 1280; private const int BackBufferHeight = 720; private const Buttons ContinueButton = Buttons.A; #endif public PlatformerGame() { graphics = new GraphicsDeviceManager(this); graphics.PreferredBackBufferWidth = BackBufferWidth; graphics.PreferredBackBufferHeight = BackBufferHeight; Content.RootDirectory = "Content"; // Framerate differs between platforms. TargetElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / TargetFrameRate); } /// /// LoadContent will be called once per game and is the place to load /// all of your content. /// protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); // Load fonts hudFont = Content.Load("Fonts/Hud"); // Load overlay textures winOverlay = Content.Load("Overlays/you_win"); loseOverlay = Content.Load("Overlays/you_lose"); diedOverlay = Content.Load("Overlays/you_died"); MediaPlayer.IsRepeating = true; MediaPlayer.Play(Content.Load("Sounds/Music")); LoadNextLevel(); } /// /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { HandleInput(); level.Update(gameTime); base.Update(gameTime); } private void HandleInput() { KeyboardState keyboardState = Keyboard.GetState(); GamePadState gamepadState = GamePad.GetState(PlayerIndex.One); // Exit the game when back is pressed. if (gamepadState.Buttons.Back == ButtonState.Pressed) Exit(); bool continuePressed = keyboardState.IsKeyDown(Keys.Space) || gamepadState.IsButtonDown(ContinueButton); // Perform the appropriate action to advance the game and // to get the player back to playing. if (!wasContinuePressed && continuePressed) { if (!level.Player.IsAlive) { level.StartNewLife(); } else if (level.TimeRemaining == TimeSpan.Zero) { if (level.ReachedExit) LoadNextLevel(); else ReloadCurrentLevel(); } } wasContinuePressed = continuePressed; } private void LoadNextLevel() { // Find the path of the next level. string levelPath; // Loop here so we can try again when we can't find a level. while (true) { // Try to find the next level. They are sequentially numbered txt files. levelPath = String.Format("Levels/{0}.txt", ++levelIndex); levelPath = Path.Combine(StorageContainer.TitleLocation, "Content/" + levelPath); if (File.Exists(levelPath)) break; // If there isn't even a level 0, something has gone wrong. if (levelIndex == 0) throw new Exception("No levels found."); // Whenever we can't find a level, start over again at 0. levelIndex = -1; } // Unloads the content for the current level before loading the next one. if (level != null) level.Dispose(); // Load the level. level = new Level(Services, levelPath); } private void ReloadCurrentLevel() { --levelIndex; LoadNextLevel(); } /// /// Draws the game from background to foreground. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); level.Draw(gameTime, spriteBatch); DrawHud(); spriteBatch.End(); base.Draw(gameTime); } private void DrawHud() { Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea; Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y); Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f, titleSafeArea.Y + titleSafeArea.Height / 2.0f); // Draw time remaining. Uses modulo division to cause blinking when the // player is running out of time. string timeString = "TIME: " + level.TimeRemaining.Minutes.ToString("00") + ":" + level.TimeRemaining.Seconds.ToString("00"); Color timeColor; if (level.TimeRemaining > WarningTime || level.ReachedExit || (int)level.TimeRemaining.TotalSeconds % 2 == 0) { timeColor = Color.Yellow; } else { timeColor = Color.Red; } DrawShadowedString(hudFont, timeString, hudLocation, timeColor); // Draw score float timeHeight = hudFont.MeasureString(timeString).Y; DrawShadowedString(hudFont, "SCORE: " + level.Score.ToString(), hudLocation + new Vector2(0.0f, timeHeight * 1.2f), Color.Yellow); // Determine the status overlay message to show. Texture2D status = null; if (level.TimeRemaining == TimeSpan.Zero) { if (level.ReachedExit) { status = winOverlay; } else { status = loseOverlay; } } else if (!level.Player.IsAlive) { status = diedOverlay; } if (status != null) { // Draw status message. Vector2 statusSize = new Vector2(status.Width, status.Height); spriteBatch.Draw(status, center - statusSize / 2, Color.White); } } private void DrawShadowedString(SpriteFont font, string value, Vector2 position, Color color) { spriteBatch.DrawString(font, value, position + new Vector2(1.0f, 1.0f), Color.Black); spriteBatch.DrawString(font, value, position, color); } } }