InputState.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. //-----------------------------------------------------------------------------
  2. // InputState.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using Microsoft.Xna.Framework;
  8. using Microsoft.Xna.Framework.Input;
  9. using Microsoft.Xna.Framework.Input.Touch;
  10. using System;
  11. using System.Collections.Generic;
  12. namespace CardsFramework
  13. {
  14. /// <summary>
  15. /// Helper for reading input from keyboard, gamepad, and touch input. This class
  16. /// tracks both the current and previous state of the input devices, and implements
  17. /// query methods for high level input actions such as "move up through the menu"
  18. /// or "pause the game".
  19. /// </summary>
  20. public class InputState
  21. {
  22. public const int MaxInputs = 4; // Maximum number of supported input devices (e.g., players)
  23. // Current Inputstates - Tracks the latest state of all input devices
  24. public readonly GamePadState[] CurrentGamePadStates;
  25. public readonly KeyboardState[] CurrentKeyboardStates;
  26. public MouseState CurrentMouseState;
  27. private int touchCount; // Number of active touch inputs
  28. public TouchCollection CurrentTouchState;
  29. // Last Inputstates - Stores the previous frame's input states for detecting changes
  30. public readonly GamePadState[] LastGamePadStates;
  31. public readonly KeyboardState[] LastKeyboardStates;
  32. public MouseState LastMouseState;
  33. public TouchCollection LastTouchState;
  34. public readonly List<GestureSample> Gestures = new List<GestureSample>(); // Stores touch gestures
  35. public readonly bool[] GamePadWasConnected;
  36. /// <summary>
  37. /// Cursor move speed in pixels per second
  38. /// </summary>
  39. private const float cursorMoveSpeed = 250.0f;
  40. private Vector2 currentCursorLocation;
  41. /// <summary>
  42. /// Current location of our Cursor
  43. /// </summary>
  44. public Vector2 CurrentCursorLocation => currentCursorLocation;
  45. private Vector2 lastCursorLocation;
  46. /// <summary>
  47. /// Current location of our Cursor
  48. /// </summary>
  49. public Vector2 LastCursorLocation => lastCursorLocation;
  50. private bool isMouseWheelScrolledDown;
  51. /// <summary>
  52. /// Has the user scrolled the mouse wheel down?
  53. /// </summary>
  54. public bool IsMouseWheelScrolledDown => isMouseWheelScrolledDown;
  55. private bool isMouseWheelScrolledUp;
  56. private Matrix inputTransformation; // Used to transform input coordinates between screen and game space
  57. private float baseBufferWidth;
  58. private float baseBufferHeight;
  59. /// <summary>
  60. /// Has the user scrolled the mouse wheel up?
  61. /// </summary>
  62. public bool IsMouseWheelScrolledUp => isMouseWheelScrolledUp;
  63. /// <summary>
  64. /// Constructs a new input state.
  65. /// </summary>
  66. public InputState(float baseBufferWidth, float baseBufferHeight)
  67. {
  68. this.baseBufferWidth = baseBufferWidth;
  69. this.baseBufferHeight = baseBufferHeight;
  70. // Initialize arrays for multiple controller/keyboard states
  71. CurrentKeyboardStates = new KeyboardState[MaxInputs];
  72. CurrentGamePadStates = new GamePadState[MaxInputs];
  73. LastKeyboardStates = new KeyboardState[MaxInputs];
  74. LastGamePadStates = new GamePadState[MaxInputs];
  75. GamePadWasConnected = new bool[MaxInputs];
  76. // Configure platform-specific input options
  77. if (UIUtility.IsMobile)
  78. {
  79. TouchPanel.EnabledGestures = GestureType.Tap;
  80. }
  81. else if (UIUtility.IsDesktop)
  82. {
  83. // No desktop-specific initialization needed
  84. }
  85. else
  86. {
  87. // For now, we'll throw an exception if we don't know the platform
  88. throw new PlatformNotSupportedException();
  89. }
  90. }
  91. /// <summary>
  92. /// Reads the latest state of the keyboard and gamepad.
  93. /// </summary>
  94. public void Update(GameTime gameTime)
  95. {
  96. // Update keyboard and gamepad states for all players
  97. for (int i = 0; i < MaxInputs; i++)
  98. {
  99. LastKeyboardStates[i] = CurrentKeyboardStates[i];
  100. LastGamePadStates[i] = CurrentGamePadStates[i];
  101. CurrentKeyboardStates[i] = Keyboard.GetState();
  102. CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);
  103. // Keep track of whether a gamepad has ever been
  104. // connected, so we can detect if it is unplugged.
  105. if (CurrentGamePadStates[i].IsConnected)
  106. {
  107. GamePadWasConnected[i] = true;
  108. }
  109. }
  110. // Update mouse state
  111. LastMouseState = CurrentMouseState;
  112. CurrentMouseState = Mouse.GetState();
  113. // Update touch state
  114. touchCount = 0;
  115. LastTouchState = CurrentTouchState;
  116. CurrentTouchState = TouchPanel.GetState();
  117. // Process all available gestures
  118. Gestures.Clear();
  119. while (TouchPanel.IsGestureAvailable)
  120. {
  121. Gestures.Add(TouchPanel.ReadGesture());
  122. }
  123. // Process touch inputs
  124. foreach (TouchLocation location in CurrentTouchState)
  125. {
  126. switch (location.State)
  127. {
  128. case TouchLocationState.Pressed:
  129. touchCount++;
  130. lastCursorLocation = currentCursorLocation;
  131. // Transform touch position to game coordinates
  132. currentCursorLocation = TransformCursorLocation(location.Position);
  133. break;
  134. case TouchLocationState.Moved:
  135. break;
  136. case TouchLocationState.Released:
  137. break;
  138. }
  139. }
  140. // Handle mouse clicks as touch equivalents
  141. if (IsLeftMouseButtonClicked())
  142. {
  143. lastCursorLocation = currentCursorLocation;
  144. // Transform mouse position to game coordinates
  145. currentCursorLocation = TransformCursorLocation(new Vector2(CurrentMouseState.X, CurrentMouseState.Y));
  146. touchCount = 1;
  147. }
  148. if (IsMiddleMouseButtonClicked())
  149. {
  150. touchCount = 2; // Treat middle mouse click as double touch
  151. }
  152. if (IsRightMoustButtonClicked())
  153. {
  154. touchCount = 3; // Treat right mouse click as triple touch
  155. }
  156. // Reset mouse wheel flags
  157. isMouseWheelScrolledUp = false;
  158. isMouseWheelScrolledDown = false;
  159. // Detect mouse wheel scrolling
  160. if (CurrentMouseState.ScrollWheelValue != LastMouseState.ScrollWheelValue)
  161. {
  162. int scrollWheelDelta = CurrentMouseState.ScrollWheelValue - LastMouseState.ScrollWheelValue;
  163. // Handle the scroll wheel event based on the delta
  164. if (scrollWheelDelta > 0)
  165. {
  166. // Mouse wheel scrolled up
  167. isMouseWheelScrolledUp = true;
  168. }
  169. else if (scrollWheelDelta < 0)
  170. {
  171. // Mouse wheel scrolled down
  172. isMouseWheelScrolledDown = true;
  173. }
  174. }
  175. // Update the cursor location using gamepad and keyboard
  176. float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
  177. // Move cursor with 1st gamepad thumbstick
  178. if (CurrentGamePadStates[0].IsConnected)
  179. {
  180. lastCursorLocation = currentCursorLocation;
  181. currentCursorLocation.X += CurrentGamePadStates[0].ThumbSticks.Left.X * elapsedTime * cursorMoveSpeed;
  182. currentCursorLocation.Y -= CurrentGamePadStates[0].ThumbSticks.Left.Y * elapsedTime * cursorMoveSpeed;
  183. }
  184. // Keep cursor within bounds
  185. currentCursorLocation.X = MathHelper.Clamp(currentCursorLocation.X, 0f, baseBufferWidth);
  186. currentCursorLocation.Y = MathHelper.Clamp(currentCursorLocation.Y, 0f, baseBufferHeight);
  187. }
  188. /// <summary>
  189. /// Checks if left mouse button was clicked (pressed and then released)
  190. /// </summary>
  191. /// <returns>True if left mouse button was clicked, false otherwise.</returns>
  192. internal bool IsLeftMouseButtonClicked()
  193. {
  194. return CurrentMouseState.LeftButton == ButtonState.Released && LastMouseState.LeftButton == ButtonState.Pressed;
  195. }
  196. /// <summary>
  197. /// Checks if middle mouse button was clicked (pressed and then released)
  198. /// </summary>
  199. /// <returns>True if middle mouse button was clicked, false otherwise.</returns>
  200. internal bool IsMiddleMouseButtonClicked()
  201. {
  202. return CurrentMouseState.MiddleButton == ButtonState.Released && LastMouseState.MiddleButton == ButtonState.Pressed;
  203. }
  204. /// <summary>
  205. /// Checks if right mouse button was clicked (pressed and then released)
  206. /// </summary>
  207. /// <returns>True if right mouse button was clicked, false otherwise.</returns>
  208. internal bool IsRightMoustButtonClicked()
  209. {
  210. return CurrentMouseState.RightButton == ButtonState.Released && LastMouseState.RightButton == ButtonState.Pressed;
  211. }
  212. /// <summary>
  213. /// Helper for checking if a key was newly pressed during this update. The
  214. /// controllingPlayer parameter specifies which player to read input for.
  215. /// If this is null, it will accept input from any player. When a keypress
  216. /// is detected, the output playerIndex reports which player pressed it.
  217. /// </summary>
  218. public bool IsNewKeyPress(Keys key, PlayerIndex? controllingPlayer,
  219. out PlayerIndex playerIndex)
  220. {
  221. if (controllingPlayer.HasValue)
  222. {
  223. // Read input from the specified player.
  224. playerIndex = controllingPlayer.Value;
  225. int i = (int)playerIndex;
  226. return (CurrentKeyboardStates[i].IsKeyDown(key) &&
  227. LastKeyboardStates[i].IsKeyUp(key));
  228. }
  229. else
  230. {
  231. // Accept input from any player.
  232. return (IsNewKeyPress(key, PlayerIndex.One, out playerIndex) ||
  233. IsNewKeyPress(key, PlayerIndex.Two, out playerIndex) ||
  234. IsNewKeyPress(key, PlayerIndex.Three, out playerIndex) ||
  235. IsNewKeyPress(key, PlayerIndex.Four, out playerIndex));
  236. }
  237. }
  238. /// <summary>
  239. /// Helper for checking if a button was newly pressed during this update.
  240. /// The controllingPlayer parameter specifies which player to read input for.
  241. /// If this is null, it will accept input from any player. When a button press
  242. /// is detected, the output playerIndex reports which player pressed it.
  243. /// </summary>
  244. public bool IsNewButtonPress(Buttons button, PlayerIndex? controllingPlayer,
  245. out PlayerIndex playerIndex)
  246. {
  247. if (controllingPlayer.HasValue)
  248. {
  249. // Read input from the specified player.
  250. playerIndex = controllingPlayer.Value;
  251. int i = (int)playerIndex;
  252. return (CurrentGamePadStates[i].IsButtonDown(button) &&
  253. LastGamePadStates[i].IsButtonUp(button));
  254. }
  255. else
  256. {
  257. // Accept input from any player.
  258. return (IsNewButtonPress(button, PlayerIndex.One, out playerIndex) ||
  259. IsNewButtonPress(button, PlayerIndex.Two, out playerIndex) ||
  260. IsNewButtonPress(button, PlayerIndex.Three, out playerIndex) ||
  261. IsNewButtonPress(button, PlayerIndex.Four, out playerIndex));
  262. }
  263. }
  264. /// <summary>
  265. /// Checks for a "menu select" input action.
  266. /// The controllingPlayer parameter specifies which player to read input for.
  267. /// If this is null, it will accept input from any player. When the action
  268. /// is detected, the output playerIndex reports which player pressed it.
  269. /// </summary>
  270. public bool IsMenuSelect(PlayerIndex? controllingPlayer,
  271. out PlayerIndex playerIndex)
  272. {
  273. return IsNewKeyPress(Keys.Space, controllingPlayer, out playerIndex) ||
  274. IsNewKeyPress(Keys.Enter, controllingPlayer, out playerIndex) ||
  275. IsNewButtonPress(Buttons.A, controllingPlayer, out playerIndex) ||
  276. IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
  277. }
  278. /// <summary>
  279. /// Checks for a "menu cancel" input action.
  280. /// The controllingPlayer parameter specifies which player to read input for.
  281. /// If this is null, it will accept input from any player. When the action
  282. /// is detected, the output playerIndex reports which player pressed it.
  283. /// </summary>
  284. public bool IsMenuCancel(PlayerIndex? controllingPlayer,
  285. out PlayerIndex playerIndex)
  286. {
  287. return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
  288. IsNewButtonPress(Buttons.B, controllingPlayer, out playerIndex) ||
  289. IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex);
  290. }
  291. /// <summary>
  292. /// Checks for a "menu up" input action.
  293. /// The controllingPlayer parameter specifies which player to read
  294. /// input for. If this is null, it will accept input from any player.
  295. /// </summary>
  296. public bool IsMenuUp(PlayerIndex? controllingPlayer)
  297. {
  298. PlayerIndex playerIndex;
  299. return IsNewKeyPress(Keys.Up, controllingPlayer, out playerIndex) ||
  300. IsNewKeyPress(Keys.Left, controllingPlayer, out playerIndex) ||
  301. IsNewButtonPress(Buttons.DPadLeft, controllingPlayer, out playerIndex) ||
  302. IsNewButtonPress(Buttons.LeftThumbstickLeft, controllingPlayer, out playerIndex);
  303. }
  304. /// <summary>
  305. /// Checks for a "menu down" input action.
  306. /// The controllingPlayer parameter specifies which player to read
  307. /// input for. If this is null, it will accept input from any player.
  308. /// </summary>
  309. public bool IsMenuDown(PlayerIndex? controllingPlayer)
  310. {
  311. PlayerIndex playerIndex;
  312. return IsNewKeyPress(Keys.Down, controllingPlayer, out playerIndex) ||
  313. IsNewKeyPress(Keys.Right, controllingPlayer, out playerIndex) ||
  314. IsNewButtonPress(Buttons.DPadRight, controllingPlayer, out playerIndex) ||
  315. IsNewButtonPress(Buttons.LeftThumbstickRight, controllingPlayer, out playerIndex);
  316. }
  317. /// <summary>
  318. /// Checks for a "pause the game" input action.
  319. /// The controllingPlayer parameter specifies which player to read
  320. /// input for. If this is null, it will accept input from any player.
  321. /// </summary>
  322. public bool IsPauseGame(PlayerIndex? controllingPlayer)
  323. {
  324. PlayerIndex playerIndex;
  325. return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
  326. IsNewButtonPress(Buttons.Back, controllingPlayer, out playerIndex) ||
  327. IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
  328. }
  329. /// <summary>
  330. /// Updates the matrix used to transform input coordinates.
  331. /// </summary>
  332. /// <param name="inputTransformation">The transformation matrix to apply.</param>
  333. public void UpdateInputTransformation(Matrix inputTransformation)
  334. {
  335. this.inputTransformation = inputTransformation;
  336. }
  337. /// <summary>
  338. /// Transforms touch/mouse positions from screen space to game space.
  339. /// </summary>
  340. /// <param name="mousePosition">The screen-space position to transform.</param>
  341. /// <returns>The transformed position in game space.</returns>
  342. public Vector2 TransformCursorLocation(Vector2 mousePosition)
  343. {
  344. // Transform back to cursor location
  345. return Vector2.Transform(mousePosition, inputTransformation);
  346. }
  347. }
  348. }