#region File Description //----------------------------------------------------------------------------- // Game.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using System.Linq; using System.Text; 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; #endregion namespace InputSequenceSample { /// /// This is the main type for your game /// public class InputSequenceSampleGame : Microsoft.Xna.Framework.Game { #region Fields GraphicsDeviceManager graphics; SpriteBatch spriteBatch; SpriteFont spriteFont; // This is the master list of moves in logical order. This array is kept // around in order to draw the move list on the screen in this order. Move[] moves; // The move list used for move detection at runtime. MoveList moveList; // The move list is used to match against an input manager for each player. InputManager[] inputManagers; // Stores each players' most recent move and when they pressed it. Move[] playerMoves; TimeSpan[] playerMoveTimes; // Time until the currently "active" move dissapears from the screen. readonly TimeSpan MoveTimeOut = TimeSpan.FromSeconds(1.0); // Direction textures. Texture2D upTexture; Texture2D downTexture; Texture2D leftTexture; Texture2D rightTexture; Texture2D upLeftTexture; Texture2D upRightTexture; Texture2D downLeftTexture; Texture2D downRightTexture; // Button textures. Texture2D aButtonTexture; Texture2D bButtonTexture; Texture2D xButtonTexture; Texture2D yButtonTexture; // Other textures. Texture2D plusTexture; Texture2D padFaceTexture; #endregion #region Initialization public InputSequenceSampleGame() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // Construct the master list of moves. moves = new Move[] { new Move("Jump", Buttons.A) { IsSubMove = true }, new Move("Punch", Buttons.X) { IsSubMove = true }, new Move("Double Jump", Buttons.A, Buttons.A), new Move("Jump Kick", Buttons.A | Buttons.X), new Move("Quad Punch", Buttons.X, Buttons.Y, Buttons.X, Buttons.Y), new Move("Fireball", Direction.Down, Direction.DownRight, Direction.Right | Buttons.X), new Move("Long Jump", Direction.Up, Direction.Up, Buttons.A), new Move("Back Flip", Direction.Down, Direction.Down | Buttons.A), new Move("30 Lives", Direction.Up, Direction.Up, Direction.Down, Direction.Down, Direction.Left, Direction.Right, Direction.Left, Direction.Right, Buttons.B, Buttons.A), }; // Construct a move list which will store its own copy of the moves array. moveList = new MoveList(moves); // Create an InputManager for each player with a sufficiently large buffer. inputManagers = new InputManager[2]; for (int i = 0; i < inputManagers.Length; ++i) { inputManagers[i] = new InputManager((PlayerIndex)i, moveList.LongestMoveLength); } // Give each player a location to store their most recent move. playerMoves = new Move[inputManagers.Length]; playerMoveTimes = new TimeSpan[inputManagers.Length]; } /// /// 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); spriteFont = Content.Load("Font"); // Load direction textures. upTexture = Content.Load("Up"); downTexture = Content.Load("Down"); leftTexture = Content.Load("Left"); rightTexture = Content.Load("Right"); upLeftTexture = Content.Load("UpLeft"); upRightTexture = Content.Load("UpRight"); downLeftTexture = Content.Load("DownLeft"); downRightTexture = Content.Load("DownRight"); // Load button textures. aButtonTexture = Content.Load("A"); bButtonTexture = Content.Load("B"); xButtonTexture = Content.Load("X"); yButtonTexture = Content.Load("Y"); // Load other textures. plusTexture = Content.Load("Plus"); padFaceTexture = Content.Load("PadFace"); } #endregion #region Update and Draw /// /// 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) { for (int i = 0; i < inputManagers.Length; ++i) { // Expire old moves. if (gameTime.TotalGameTime - playerMoveTimes[i] > MoveTimeOut) { playerMoves[i] = null; } // Get the updated input manager. InputManager inputManager = inputManagers[i]; inputManager.Update(gameTime); // Allows the game to exit. if (inputManager.GamePadState.Buttons.Back == ButtonState.Pressed || inputManager.KeyboardState.IsKeyDown(Keys.Escape)) { Exit(); } // Detection and record the current player's most recent move. Move newMove = moveList.DetectMove(inputManager); if (newMove != null) { playerMoves[i] = newMove; playerMoveTimes[i] = gameTime.TotalGameTime; } } base.Update(gameTime); } /// /// This is called when the game should draw itself. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); // Calculate some reasonable boundaries within the safe area. Vector2 topLeft = new Vector2(50, 50); Vector2 bottomRight = new Vector2( GraphicsDevice.Viewport.Width - topLeft.X, GraphicsDevice.Viewport.Height - topLeft.Y); // Keeps track of where to draw next. Vector2 position = topLeft; // Draw the list of all moves. foreach (Move move in moves) { Vector2 size = MeasureMove(move); // If this move would fall off the right edge of the screen, if (position.X + size.X > bottomRight.X) { // start again on the next line. position.X = topLeft.X; position.Y += size.Y; } DrawMove(move, position); position.X += size.X + 30.0f; } // Skip some space. position.Y += 90.0f; // Draw the input from each player. for (int i = 0; i < inputManagers.Length; ++i) { position.X = topLeft.X; DrawInput(i, position); position.Y += 80; } spriteBatch.End(); base.Draw(gameTime); } /// /// Calculates the size of what would be drawn by a call to DrawMove. /// private Vector2 MeasureMove(Move move) { Vector2 textSize = spriteFont.MeasureString(move.Name); Vector2 sequenceSize = MeasureSequence(move.Sequence); return new Vector2( Math.Max(textSize.X, sequenceSize.X), textSize.Y + sequenceSize.Y); } /// /// Draws graphical instructions on how to perform a move. /// private void DrawMove(Move move, Vector2 position) { DrawString(move.Name, position, Color.White); position.Y += spriteFont.MeasureString(move.Name).Y; DrawSequence(move.Sequence, position); } /// /// Draws the input buffer and most recently fired action for a given player. /// private void DrawInput(int i, Vector2 position) { InputManager inputManager = inputManagers[i]; Move move = playerMoves[i]; // Draw the player's name and currently active move (if any). string text = "Player " + inputManager.PlayerIndex + " input "; Vector2 textSize = spriteFont.MeasureString(text); DrawString(text, position, Color.White); if (move != null) { DrawString(move.Name, new Vector2(position.X + textSize.X, position.Y), Color.Red); } // Draw the player's input buffer. position.Y += textSize.Y; DrawSequence(inputManager.Buffer, position); } /// /// Draws a string with a subtle drop shadow. /// private void DrawString(string text, Vector2 position, Color color) { spriteBatch.DrawString(spriteFont, text, new Vector2(position.X, position.Y + 1), Color.Black); spriteBatch.DrawString(spriteFont, text, new Vector2(position.X, position.Y), color); } /// /// Calculates the size of what would be drawn by a call to DrawSequence. /// private Vector2 MeasureSequence(IEnumerable sequence) { float width = 0.0f; foreach (Buttons buttons in sequence) { width += MeasureButtons(buttons).X; } return new Vector2(width, padFaceTexture.Height); } /// /// Draws a horizontal series of input steps in a sequence. /// private void DrawSequence(IEnumerable sequence, Vector2 position) { foreach (Buttons buttons in sequence) { DrawButtons(buttons, position); position.X += MeasureButtons(buttons).X; } } /// /// Calculates the size of what would be drawn by a call to DrawButtons. /// private Vector2 MeasureButtons(Buttons buttons) { Buttons direction = Direction.FromButtons(buttons); float width; // If buttons has a direction, if (direction > 0) { width = GetDirectionTexture(direction).Width; // If buttons has at least one non-directional button, if ((buttons & ~direction) > 0) { width += plusTexture.Width + padFaceTexture.Width; } } else { width = padFaceTexture.Width; } return new Vector2(width, padFaceTexture.Height); } /// /// Draws the combined state of a set of buttons flags. The rendered output /// looks like a directional arrow, a group of buttons, or both concatenated /// with a plus sign operator. /// private void DrawButtons(Buttons buttons, Vector2 position) { // Get the texture to draw for the direction. Buttons direction = Direction.FromButtons(buttons); Texture2D directionTexture = GetDirectionTexture(direction); // If there is a direction, draw it. if (directionTexture != null) { spriteBatch.Draw(directionTexture, position, Color.White); position.X += directionTexture.Width; } // If any non-direction button is pressed, if ((buttons & ~direction) > 0) { // Draw a plus if both a direction and one more more buttons is pressed. if (directionTexture != null) { spriteBatch.Draw(plusTexture, position, Color.White); position.X += plusTexture.Width; } // Draw a gamepad with all inactive buttons in the background. spriteBatch.Draw(padFaceTexture, position, Color.White); // Draw each active button over the inactive game pad face. if ((buttons & Buttons.A) > 0) { spriteBatch.Draw(aButtonTexture, position, Color.White); } if ((buttons & Buttons.B) > 0) { spriteBatch.Draw(bButtonTexture, position, Color.White); } if ((buttons & Buttons.X) > 0) { spriteBatch.Draw(xButtonTexture, position, Color.White); } if ((buttons & Buttons.Y) > 0) { spriteBatch.Draw(yButtonTexture, position, Color.White); } } } /// /// Gets the texture for a given direction. /// private Texture2D GetDirectionTexture(Buttons direction) { switch (direction) { case Direction.Up: return upTexture; case Direction.Down: return downTexture; case Direction.Left: return leftTexture; case Direction.Right: return rightTexture; case Direction.UpLeft: return upLeftTexture; case Direction.UpRight: return upRightTexture; case Direction.DownLeft: return downLeftTexture; case Direction.DownRight: return downRightTexture; default: return null; } } #endregion } #region Entry Point /// /// The main entry point for the application. /// static class Program { static void Main() { using (InputSequenceSampleGame game = new InputSequenceSampleGame()) { game.Run(); } } } #endregion }