Game.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // Game.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Text;
  14. using Microsoft.Xna.Framework;
  15. using Microsoft.Xna.Framework.Audio;
  16. using Microsoft.Xna.Framework.Content;
  17. using Microsoft.Xna.Framework.GamerServices;
  18. using Microsoft.Xna.Framework.Graphics;
  19. using Microsoft.Xna.Framework.Input;
  20. using Microsoft.Xna.Framework.Media;
  21. using Microsoft.Xna.Framework.Net;
  22. using Microsoft.Xna.Framework.Storage;
  23. #endregion
  24. namespace InputSequenceSample
  25. {
  26. /// <summary>
  27. /// This is the main type for your game
  28. /// </summary>
  29. public class InputSequenceSampleGame : Microsoft.Xna.Framework.Game
  30. {
  31. #region Fields
  32. GraphicsDeviceManager graphics;
  33. SpriteBatch spriteBatch;
  34. SpriteFont spriteFont;
  35. // This is the master list of moves in logical order. This array is kept
  36. // around in order to draw the move list on the screen in this order.
  37. Move[] moves;
  38. // The move list used for move detection at runtime.
  39. MoveList moveList;
  40. // The move list is used to match against an input manager for each player.
  41. InputManager[] inputManagers;
  42. // Stores each players' most recent move and when they pressed it.
  43. Move[] playerMoves;
  44. TimeSpan[] playerMoveTimes;
  45. // Time until the currently "active" move dissapears from the screen.
  46. readonly TimeSpan MoveTimeOut = TimeSpan.FromSeconds(1.0);
  47. // Direction textures.
  48. Texture2D upTexture;
  49. Texture2D downTexture;
  50. Texture2D leftTexture;
  51. Texture2D rightTexture;
  52. Texture2D upLeftTexture;
  53. Texture2D upRightTexture;
  54. Texture2D downLeftTexture;
  55. Texture2D downRightTexture;
  56. // Button textures.
  57. Texture2D aButtonTexture;
  58. Texture2D bButtonTexture;
  59. Texture2D xButtonTexture;
  60. Texture2D yButtonTexture;
  61. // Other textures.
  62. Texture2D plusTexture;
  63. Texture2D padFaceTexture;
  64. #endregion
  65. #region Initialization
  66. public InputSequenceSampleGame()
  67. {
  68. graphics = new GraphicsDeviceManager(this);
  69. Content.RootDirectory = "Content";
  70. // Construct the master list of moves.
  71. moves = new Move[]
  72. {
  73. new Move("Jump", Buttons.A) { IsSubMove = true },
  74. new Move("Punch", Buttons.X) { IsSubMove = true },
  75. new Move("Double Jump", Buttons.A, Buttons.A),
  76. new Move("Jump Kick", Buttons.A | Buttons.X),
  77. new Move("Quad Punch", Buttons.X, Buttons.Y, Buttons.X, Buttons.Y),
  78. new Move("Fireball", Direction.Down, Direction.DownRight,
  79. Direction.Right | Buttons.X),
  80. new Move("Long Jump", Direction.Up, Direction.Up, Buttons.A),
  81. new Move("Back Flip", Direction.Down, Direction.Down | Buttons.A),
  82. new Move("30 Lives", Direction.Up, Direction.Up,
  83. Direction.Down, Direction.Down,
  84. Direction.Left, Direction.Right,
  85. Direction.Left, Direction.Right,
  86. Buttons.B, Buttons.A),
  87. };
  88. // Construct a move list which will store its own copy of the moves array.
  89. moveList = new MoveList(moves);
  90. // Create an InputManager for each player with a sufficiently large buffer.
  91. inputManagers = new InputManager[2];
  92. for (int i = 0; i < inputManagers.Length; ++i)
  93. {
  94. inputManagers[i] =
  95. new InputManager((PlayerIndex)i, moveList.LongestMoveLength);
  96. }
  97. // Give each player a location to store their most recent move.
  98. playerMoves = new Move[inputManagers.Length];
  99. playerMoveTimes = new TimeSpan[inputManagers.Length];
  100. }
  101. /// <summary>
  102. /// LoadContent will be called once per game and is the place to load
  103. /// all of your content.
  104. /// </summary>
  105. protected override void LoadContent()
  106. {
  107. // Create a new SpriteBatch, which can be used to draw textures.
  108. spriteBatch = new SpriteBatch(GraphicsDevice);
  109. spriteFont = Content.Load<SpriteFont>("Font");
  110. // Load direction textures.
  111. upTexture = Content.Load<Texture2D>("Up");
  112. downTexture = Content.Load<Texture2D>("Down");
  113. leftTexture = Content.Load<Texture2D>("Left");
  114. rightTexture = Content.Load<Texture2D>("Right");
  115. upLeftTexture = Content.Load<Texture2D>("UpLeft");
  116. upRightTexture = Content.Load<Texture2D>("UpRight");
  117. downLeftTexture = Content.Load<Texture2D>("DownLeft");
  118. downRightTexture = Content.Load<Texture2D>("DownRight");
  119. // Load button textures.
  120. aButtonTexture = Content.Load<Texture2D>("A");
  121. bButtonTexture = Content.Load<Texture2D>("B");
  122. xButtonTexture = Content.Load<Texture2D>("X");
  123. yButtonTexture = Content.Load<Texture2D>("Y");
  124. // Load other textures.
  125. plusTexture = Content.Load<Texture2D>("Plus");
  126. padFaceTexture = Content.Load<Texture2D>("PadFace");
  127. }
  128. #endregion
  129. #region Update and Draw
  130. /// <summary>
  131. /// Allows the game to run logic such as updating the world,
  132. /// checking for collisions, gathering input, and playing audio.
  133. /// </summary>
  134. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  135. protected override void Update(GameTime gameTime)
  136. {
  137. for (int i = 0; i < inputManagers.Length; ++i)
  138. {
  139. // Expire old moves.
  140. if (gameTime.TotalGameTime - playerMoveTimes[i] > MoveTimeOut)
  141. {
  142. playerMoves[i] = null;
  143. }
  144. // Get the updated input manager.
  145. InputManager inputManager = inputManagers[i];
  146. inputManager.Update(gameTime);
  147. // Allows the game to exit.
  148. if (inputManager.GamePadState.Buttons.Back == ButtonState.Pressed ||
  149. inputManager.KeyboardState.IsKeyDown(Keys.Escape))
  150. {
  151. Exit();
  152. }
  153. // Detection and record the current player's most recent move.
  154. Move newMove = moveList.DetectMove(inputManager);
  155. if (newMove != null)
  156. {
  157. playerMoves[i] = newMove;
  158. playerMoveTimes[i] = gameTime.TotalGameTime;
  159. }
  160. }
  161. base.Update(gameTime);
  162. }
  163. /// <summary>
  164. /// This is called when the game should draw itself.
  165. /// </summary>
  166. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  167. protected override void Draw(GameTime gameTime)
  168. {
  169. GraphicsDevice.Clear(Color.CornflowerBlue);
  170. spriteBatch.Begin();
  171. // Calculate some reasonable boundaries within the safe area.
  172. Vector2 topLeft = new Vector2(50, 50);
  173. Vector2 bottomRight = new Vector2(
  174. GraphicsDevice.Viewport.Width - topLeft.X,
  175. GraphicsDevice.Viewport.Height - topLeft.Y);
  176. // Keeps track of where to draw next.
  177. Vector2 position = topLeft;
  178. // Draw the list of all moves.
  179. foreach (Move move in moves)
  180. {
  181. Vector2 size = MeasureMove(move);
  182. // If this move would fall off the right edge of the screen,
  183. if (position.X + size.X > bottomRight.X)
  184. {
  185. // start again on the next line.
  186. position.X = topLeft.X;
  187. position.Y += size.Y;
  188. }
  189. DrawMove(move, position);
  190. position.X += size.X + 30.0f;
  191. }
  192. // Skip some space.
  193. position.Y += 90.0f;
  194. // Draw the input from each player.
  195. for (int i = 0; i < inputManagers.Length; ++i)
  196. {
  197. position.X = topLeft.X;
  198. DrawInput(i, position);
  199. position.Y += 80;
  200. }
  201. spriteBatch.End();
  202. base.Draw(gameTime);
  203. }
  204. /// <summary>
  205. /// Calculates the size of what would be drawn by a call to DrawMove.
  206. /// </summary>
  207. private Vector2 MeasureMove(Move move)
  208. {
  209. Vector2 textSize = spriteFont.MeasureString(move.Name);
  210. Vector2 sequenceSize = MeasureSequence(move.Sequence);
  211. return new Vector2(
  212. Math.Max(textSize.X, sequenceSize.X),
  213. textSize.Y + sequenceSize.Y);
  214. }
  215. /// <summary>
  216. /// Draws graphical instructions on how to perform a move.
  217. /// </summary>
  218. private void DrawMove(Move move, Vector2 position)
  219. {
  220. DrawString(move.Name, position, Color.White);
  221. position.Y += spriteFont.MeasureString(move.Name).Y;
  222. DrawSequence(move.Sequence, position);
  223. }
  224. /// <summary>
  225. /// Draws the input buffer and most recently fired action for a given player.
  226. /// </summary>
  227. private void DrawInput(int i, Vector2 position)
  228. {
  229. InputManager inputManager = inputManagers[i];
  230. Move move = playerMoves[i];
  231. // Draw the player's name and currently active move (if any).
  232. string text = "Player " + inputManager.PlayerIndex + " input ";
  233. Vector2 textSize = spriteFont.MeasureString(text);
  234. DrawString(text, position, Color.White);
  235. if (move != null)
  236. {
  237. DrawString(move.Name,
  238. new Vector2(position.X + textSize.X, position.Y), Color.Red);
  239. }
  240. // Draw the player's input buffer.
  241. position.Y += textSize.Y;
  242. DrawSequence(inputManager.Buffer, position);
  243. }
  244. /// <summary>
  245. /// Draws a string with a subtle drop shadow.
  246. /// </summary>
  247. private void DrawString(string text, Vector2 position, Color color)
  248. {
  249. spriteBatch.DrawString(spriteFont, text,
  250. new Vector2(position.X, position.Y + 1), Color.Black);
  251. spriteBatch.DrawString(spriteFont, text,
  252. new Vector2(position.X, position.Y), color);
  253. }
  254. /// <summary>
  255. /// Calculates the size of what would be drawn by a call to DrawSequence.
  256. /// </summary>
  257. private Vector2 MeasureSequence(IEnumerable<Buttons> sequence)
  258. {
  259. float width = 0.0f;
  260. foreach (Buttons buttons in sequence)
  261. {
  262. width += MeasureButtons(buttons).X;
  263. }
  264. return new Vector2(width, padFaceTexture.Height);
  265. }
  266. /// <summary>
  267. /// Draws a horizontal series of input steps in a sequence.
  268. /// </summary>
  269. private void DrawSequence(IEnumerable<Buttons> sequence, Vector2 position)
  270. {
  271. foreach (Buttons buttons in sequence)
  272. {
  273. DrawButtons(buttons, position);
  274. position.X += MeasureButtons(buttons).X;
  275. }
  276. }
  277. /// <summary>
  278. /// Calculates the size of what would be drawn by a call to DrawButtons.
  279. /// </summary>
  280. private Vector2 MeasureButtons(Buttons buttons)
  281. {
  282. Buttons direction = Direction.FromButtons(buttons);
  283. float width;
  284. // If buttons has a direction,
  285. if (direction > 0)
  286. {
  287. width = GetDirectionTexture(direction).Width;
  288. // If buttons has at least one non-directional button,
  289. if ((buttons & ~direction) > 0)
  290. {
  291. width += plusTexture.Width + padFaceTexture.Width;
  292. }
  293. }
  294. else
  295. {
  296. width = padFaceTexture.Width;
  297. }
  298. return new Vector2(width, padFaceTexture.Height);
  299. }
  300. /// <summary>
  301. /// Draws the combined state of a set of buttons flags. The rendered output
  302. /// looks like a directional arrow, a group of buttons, or both concatenated
  303. /// with a plus sign operator.
  304. /// </summary>
  305. private void DrawButtons(Buttons buttons, Vector2 position)
  306. {
  307. // Get the texture to draw for the direction.
  308. Buttons direction = Direction.FromButtons(buttons);
  309. Texture2D directionTexture = GetDirectionTexture(direction);
  310. // If there is a direction, draw it.
  311. if (directionTexture != null)
  312. {
  313. spriteBatch.Draw(directionTexture, position, Color.White);
  314. position.X += directionTexture.Width;
  315. }
  316. // If any non-direction button is pressed,
  317. if ((buttons & ~direction) > 0)
  318. {
  319. // Draw a plus if both a direction and one more more buttons is pressed.
  320. if (directionTexture != null)
  321. {
  322. spriteBatch.Draw(plusTexture, position, Color.White);
  323. position.X += plusTexture.Width;
  324. }
  325. // Draw a gamepad with all inactive buttons in the background.
  326. spriteBatch.Draw(padFaceTexture, position, Color.White);
  327. // Draw each active button over the inactive game pad face.
  328. if ((buttons & Buttons.A) > 0)
  329. {
  330. spriteBatch.Draw(aButtonTexture, position, Color.White);
  331. }
  332. if ((buttons & Buttons.B) > 0)
  333. {
  334. spriteBatch.Draw(bButtonTexture, position, Color.White);
  335. }
  336. if ((buttons & Buttons.X) > 0)
  337. {
  338. spriteBatch.Draw(xButtonTexture, position, Color.White);
  339. }
  340. if ((buttons & Buttons.Y) > 0)
  341. {
  342. spriteBatch.Draw(yButtonTexture, position, Color.White);
  343. }
  344. }
  345. }
  346. /// <summary>
  347. /// Gets the texture for a given direction.
  348. /// </summary>
  349. private Texture2D GetDirectionTexture(Buttons direction)
  350. {
  351. switch (direction)
  352. {
  353. case Direction.Up:
  354. return upTexture;
  355. case Direction.Down:
  356. return downTexture;
  357. case Direction.Left:
  358. return leftTexture;
  359. case Direction.Right:
  360. return rightTexture;
  361. case Direction.UpLeft:
  362. return upLeftTexture;
  363. case Direction.UpRight:
  364. return upRightTexture;
  365. case Direction.DownLeft:
  366. return downLeftTexture;
  367. case Direction.DownRight:
  368. return downRightTexture;
  369. default:
  370. return null;
  371. }
  372. }
  373. #endregion
  374. }
  375. #region Entry Point
  376. /// <summary>
  377. /// The main entry point for the application.
  378. /// </summary>
  379. static class Program
  380. {
  381. static void Main()
  382. {
  383. using (InputSequenceSampleGame game = new InputSequenceSampleGame())
  384. {
  385. game.Run();
  386. }
  387. }
  388. }
  389. #endregion
  390. }