From 47f826e875050106f8de07eed98d1c0aeb290225 Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Thu, 6 Feb 2025 15:05:39 -0500 Subject: [PATCH 01/11] Add chapter 9 --- articles/toc.yml | 4 +- .../09_handling_input/index.md | 412 ++++++++++++++++++ articles/tutorials/building_2d_games/index.md | 1 + 3 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 articles/tutorials/building_2d_games/09_handling_input/index.md diff --git a/articles/toc.yml b/articles/toc.yml index 3acdcdc2..d6fe30b8 100644 --- a/articles/toc.yml +++ b/articles/toc.yml @@ -126,8 +126,10 @@ href: tutorials/building_2d_games/06_optimizing_texture_rendering/ - name: "07: The Sprite Class" href: tutorials/building_2d_games/07_the_sprite_class/ - - name: "07: The AnimatedSprite Class" + - name: "08: The AnimatedSprite Class" href: tutorials/building_2d_games/08_the_animatedsprite_class/ + - name: "09: Handling Input" + href: tutorials/building_2d_games/09_handling_input/ - name: Console Access href: console_access.md - name: Help and Support diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md new file mode 100644 index 00000000..af4108ad --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -0,0 +1,412 @@ +--- +title: "Chapter 09: Handling Input" +description: "Learn how to handle keyboard, mouse, and gamepad input in MonoGame." +--- + +When you play a game, you need ways to control what's happening; using a keyboard or gamepad to control a character or clicking the mouse to navigate a menu, MonoGame helps us handle all these different types of controls through dedicated input classes: + +- [**Keyboard**](xref:Microsoft.Xna.Framework.Input.Keyboard): Detects which keys are being pressed. +- [**Mouse**](xref:Microsoft.Xna.Framework.Input.Mouse): Tracks mouse movement, button clicks, and scroll wheel use. +- [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad): Manages controller input like button presses and joystick movement. + +> [!NOTE] +> MonoGame also supports touch and accelerometer input for mobile devices, however this tutorial focuses on desktop input handling using keyboard, mouse, and gamepad controls. + +Each of these input types has a `GetState` method that, when called, checks what is happening with that device at that moment. Think of it like taking a snapshot; when you call `GetState`, MonoGame looks at that exact moment to see which buttons are pressed, where the mouse is, or how the controller is being used. + +In this chapter you will, we will learn how to use each of these dedicated input classes to handle player input. + +## Keyboard Input + +The keyboard is often the primary input device for PC games, used for everything from character movement to menu navigation. MonoGame provides the [**Keyboard**](xref:Microsoft.Xna.Framework.Input.Keyboard) class to handle keyboard input, making it easy to detect which keys are being pressed at any time. Calling [**Keyboard.GetState**](xref:Microsoft.Xna.Framework.Input.Keyboard.GetState) will retrieve the current state of the keyboard as a [**KeyboardState**](xref:Microsoft.Xna.Framework.Input.KeyboardState) struct. + +### KeyboardState Struct + +The [**KeyboardState**](xref:Microsoft.Xna.Framework.Input.KeyboardState) struct contains methods that can be used to determine if a keyboard key is currently down or up: + +| Method | Description | +|-----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| +| [**IsKeyDown(Keys)**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys)) | Returns `true` if the specified key is down; otherwise, returns `false`. | +| [**IsKeyUp(Keys)**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyUp(Microsoft.Xna.Framework.Input.Keys)) | Returns `true` if the specified key is up; otherwise, returns `false`. | + +For example, if we wanted to see if the Space key is down, you could use the following: + +```cs +if(Keyboard.GetState().IsKeyDown(Keys.Space)) +{ + // The space key is down, so do something. +} +``` + +### Implementing Keyboard Input + +Let's implement keyboard controls to move our slime sprite around the screen. Open the *Game1.cs* file and perform the following: + +1. First, add the following fields to track the position of the slime and movement speed: + + ```cs + private Vector2 _slimePosition; + private const float MOVEMENT_SPEED = 5.0f; + ``` + +2. Next, in [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), check if the up, down, left, or right arrow keys are pressed, and if any of them are, adjust the slime's position. Add the following just before the call to `base.Update`: + + ```cs + KeyboardState keyboardState = Keyboard.GetState(); + + if(keyboardState.IsKeyDown(Keys.Up)) + { + _slimePosition.Y -= MOVEMENT_SPEED; + } + + if(keyboardState.IsKeyDown(Keys.Down)) + { + _slimePosition.Y += MOVEMENT_SPEED; + } + + if(keyboardState.IsKeyDown(Keys.Left)) + { + _slimePosition.X -= MOVEMENT_SPEED; + } + + if(keyboardState.IsKeyDown(Keys.Right)) + { + _slimePosition.X += MOVEMENT_SPEED; + } + ``` + + > [!TIP] + > Notice we store the keyboard state in a variable instead of calling [**Keyboard.GetState**](xref:Microsoft.Xna.Framework.Input.Keyboard.GetState) multiple times. This is more efficient and ensures consistent input checking within a single frame. + + > [!NOTE] + > Remember that the Y-axis increases downward in MonoGame's coordinate system. This is why we subtract from Y to move up and add to Y to move down. + +3. Finally, in [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)), update the position of the slime when it is rendered by using the `_slimePosition` value: + + ```cs + _slime.Draw(_spriteBatch, _slimePosition); + ``` + +Running the game now, you can move the slime sprite around using the arrow keys on the keyboard. Try it out! + +## Mouse Input + +The mouse is often the secondary input device for PC games, used for various actions from camera movement to interacting with menus and objects. MonoGame provides the [**Mouse**](xref:Microsoft.Xna.Framework.Input.Mouse) class to handle mouse input, making it easy to detect which buttons are pressed, the position of the mouse cursor, and the value of the scroll wheel. Calling [**Mouse.GetState**](xref:Microsoft.Xna.Framework.Input.Mouse.GetState) will retrieve the current state of the mouse as a [**MouseState**](xref:Microsoft.Xna.Framework.Input.MouseState) struct. + +### MouseState Struct + +The [**MouseState**](xref:Microsoft.Xna.Framework.Input.MouseState) struct contains properties that can be used to determine the state of the mouse buttons, the mouse position, and the scroll wheel value: + +| Property | Type | Description | +|----------------------------------------------------------------------------------------|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| [**LeftButton**](xref:Microsoft.Xna.Framework.Input.MouseState.LeftButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the left mouse button. | +| [**MiddleButton**](xref:Microsoft.Xna.Framework.Input.MouseState.MiddleButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the middle mouse button. This is often the button when pressing the scroll wheel down as a button | +| [**Position**](xref:Microsoft.Xna.Framework.Input.MouseState.Position) | [**Point**](xref:Microsoft.Xna.Framework.Point) | Returns the position of the mouse cursor relative to the bounds of the game window. | +| [**RightButton**](xref:Microsoft.Xna.Framework.Input.MouseState.RightButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the right mouse button. | +| [**ScrollWheelValue**](xref:Microsoft.Xna.Framework.Input.MouseState.ScrollWheelValue) | `int` | Returns the **cumulative** scroll wheel value since the start of the game | +| [**XButton1**](xref:Microsoft.Xna.Framework.Input.MouseState.XButton1) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the first extended button on the mouse. | +| [**XButton2**](xref:Microsoft.Xna.Framework.Input.MouseState.XButton2) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the second extended button on the mouse. | + +> [!NOTE] +> [**ScrollWheelValue**](xref:Microsoft.Xna.Framework.Input.MouseState.ScrollWheelValue) returns the cumulative value of the scroll wheel since the start of the game, not how much it moved since the last update. To determine how much it moved between one update and the next, you would need to compare it with the previous frame's value. We'll discuss comparing previous and current frame values for inputs in the next chapter. + +Unlike keyboard input which uses [**IsKeyDown(Keys)**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys))/[**IsKeyUp(Keys)**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyUp(Microsoft.Xna.Framework.Input.Keys)) methods mouse buttons return a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState): + +- [**ButtonState.Pressed**](xref:Microsoft.Xna.Framework.Input.ButtonState): The button is being held down. +- [**ButtonState.Released**](xref:Microsoft.Xna.Framework.Input.ButtonState): The button is not being pressed. + +For example, if we wanted to see if the left mouse button is down, you could use the following + +```cs +if(Mouse.GetState().LeftButton == ButtonState.Pressed) +{ + // The left button is down, so do something. +} +``` + +### Implementing Mouse Input + +Let's implement mouse controls to move the bat sprite around the screen to the point that the cursor is clicked at. Open *Game1.cs* and perform the following: + +1. First, add the following field to track the position of the bat: + + ```cs + private Vector2 _batPosition; + ``` + +2. Next, in [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) set the initial position of the bat to where it is currently drawn, 10px to the right of the slime. Add the following **after** the call to `base.Initialize()` + + ```cs + _batPosition = new Vector2(_slime.Width + 10, 0); + ``` + + > [!IMPORTANT] + > Notice that we set the value of the bat position **after** the call to `base.Initialize`. Recall from Chapter 03 in the [Content Loading](../03_the_game1_file/index.md#content-loading), that the [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) method is called during the `base.Initialize()` call. Since we are creating the slime sprite inside of [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) we need to ensure it's been created before we can use the `Width` property of the slime to set the position of the bat in [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize). + > + > We could have just as easily set the bat's position inside the [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) method after creating the slime, but I wanted to demonstrate the importance of the call order relationship between [**Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) and [**LoadContent**](xref:Microsoft.Xna.Framework.Game.LoadContent) + +3. Next, in [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), check if the left mouse button is pressed, and if so, adjust the bat's position to the position of the mouse cursor. Add the following just before the call to `base.Update`: + + ```cs + MouseState mouseState = Mouse.GetState(); + + if(mouseState.LeftButton == ButtonState.Pressed) + { + _batPosition = mouseState.Position.ToVector2(); + } + ``` + +4. Finally, in [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)), update the position of the bat when it is rendered by using the `_batPosition` value: + +```cs +_bat.Draw(_spriteBatch, _batPosition); +``` + +Running the game now, you can move the bat sprite around by clicking the left mouse button on the game screen and it will move to that position. Try it out! + +> [!NOTE] +> When the bat moves to the position of the mouse cursor, notice that it does so relative to the upper-left corner of the bat sprite and not the center of the sprite. This is because the `Origin` of the bat sprite is [**Vector2.Zero**](xref:Microsoft.Xna.Framework.Vector2.Zero) (upper-left) corner by default. + +## Gamepad Input + +Gamepads are often used as a primary input for a game or an alternative for keyboard and mouse controls. MonoGame provides the [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad) class to handle gamepad input, making it easy to detect which buttons are pressed and the value of the joysticks. Calling [**GamePad.GetState**](xref:Microsoft.Xna.Framework.Input.GamePad.GetState(Microsoft.Xna.Framework.PlayerIndex)) will retrieve the state of the gamepad as a [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct. Since multiple gamepads can be connected, you will need to supply a [**PlayerIndex**](xref:Microsoft.Xna.Framework.PlayerIndex) value to specify which gamepad state to retrieve. + +### GamePadState Struct + +The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct and properties that can be used to get the state of the buttons, dpad, triggers, and joysticks: + +| Property | Type | Description | +|--------------------------------------------------------------------------------|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.Buttons) | [**GamePadButtons**](xref:Microsoft.Xna.Framework.Input.GamePadButtons) | Returns a struct that identifies which buttons on the controller are pressed. | +| [**DPad**](xref:Microsoft.Xna.Framework.Input.GamePadState.DPad) | [**GamePadDPad**](xref:Microsoft.Xna.Framework.Input.GamePadDPad) | Returns a struct that identifies which directions on the DPad are pressed. | +| [**IsConnected**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsConnected) | `bool` | Returns a value that indicates whether the controller is connected. | +| [**ThumbSticks**](xref:Microsoft.Xna.Framework.Input.GamePadState.ThumbSticks) | [**GamePadThumbSticks**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks) | Returns a struct that contains the direction of each thumbstick. Each thumbstick (left and right) are represented as a [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) value between `-1.0f` and `1.0` for the x- and y-axes. | +| [**Triggers**](xref:Microsoft.Xna.Framework.Input.GamePadState.Triggers) | [**GamePadTriggers**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers) | Returns a struct that contains the value of each trigger. Each trigger (left and right) are represented as a `float` value between `0.0f`, meaning not pressed, and `1.0f`, meaning fully pressed. | + +#### Buttons + +The [**GamePadState.Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.Buttons) property returns a [**GamePadButtons**](xref:Microsoft.Xna.Framework.Input.GamePadButtons) struct that can be used to identify which buttons on the controller are pressed. This struct contains the following properties: + +| Property | Type | Description | +|--------------------------------------------------------------------------------------|-------------------------------------------------------------------|-----------------------------------------------| +| [**A**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.A) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the A button | +| [**B**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.B) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the B button | +| [**Back**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.Back) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the Back button | +| [**BigButton**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.BigButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the BigButton button | +| [**LeftShoulder**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.LeftShoulder) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the LeftShoulder button | +| [**LeftStick**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.LeftStick) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the LeftStick button | +| [**RightShoulder**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.RightShoulder) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the RightShoulder button | +| [**RightStick**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.RightStick) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the RightStick button | +| [**Start**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.Start) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the Start button | +| [**X**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.X) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the X button | +| [**Y**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.Y) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the Y button | + +> [!NOTE] +> Recall from [Chapter 01](../01_what_is_monogame/index.md) that MonoGame is a implementation the XNA API. Since XNA was originally created for making games on Windows PC and Xbox 360, the names of the gamepad buttons match those of an Xbox 360 controller. + +Like with the [mouse input](#mousestate-struct), each of these buttons are represented by a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value. For instance, if you wanted to check if the A button is being pressed you could do the following: + +```cs +if(GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed) +{ + // Button A is pressed, do something. +} +``` + +#### DPad + +The [**DPad**](xref:Microsoft.Xna.Framework.Input.GamePadState.DPad) property returns a [**GamePadDPad**](xref:Microsoft.Xna.Framework.Input.GamePadDPad) struct that can be used to identify which DPad buttons on the controller are pressed. This struct contains the following properties: + +| Property | Type | Description | +|------------------------------------------------------------------|-------------------------------------------------------------------|---------------------------------------------| +| [**Down**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Down button. | +| [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Left button. | +| [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Right button. | +| [**Up**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Up Button. | + +Like with the [Buttons](#buttons), these also return a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value to represent the state of the DPad button. For instance, if you wanted to check if the DPad up button is being pressed, you could do the following: + +```cs +if(GamePad.GetState(PlayerIndex.One).DPad.Down == ButtonState.Pressed) +{ + // DPad down is pressed, do something. +} +``` + +#### Thumbsticks + +The [**ThumbSticks**](xref:Microsoft.Xna.Framework.Input.GamePadState.ThumbSticks) property returns a [**GamePadThumbSticks**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks) struct that can be used to retrieve the values of the left and right thumbsticks. This struct contains the following properties: + +| Property | Type | Description | +|--------------------------------------------------------------------------|-----------------------------------------------------|------------------------------------------------| +| [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks.Left) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The direction the left thumbstick is pressed. | +| [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks.Right) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The direction the right thumbstick is pressed. | + +The thumbstick values are represented as a [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) value: + +- X-axis: A value between `-1.0f` (pushed fully to the left) and `1.0f` (pushed fully to the right). +- Y-axis: A value between `-1.0f` (pushed fully downward) and `1.0f` (pushed fully upward). + +For example, if you wanted to move a sprite using the left thumbstick, you could do the following + +```cs +Vector2 leftStick = GamePad.GetState(PlayerIndex.One).Thumbsticks.Left; +leftStick.Y *= -1.0f; +sprite.Position += leftStick; +``` + +> [!IMPORTANT] +> When the Y-Axis is fully pushed downward, its value is `-1.0f`. Recall from [Chapter 05](../05_working_with_textures/index.md#drawing-a-texture) that MonoGame uses a coordinate system where the Y values **increase** moving down. This is directly opposite of the value represented by the thumbstick. To resolve this, the common approach is to multiply the Y value of the thumbstick by `-1.0f`. This is why it is done in the example above. + +#### Triggers + +The [**Triggers**](xref:Microsoft.Xna.Framework.Input.GamePadState.Triggers) property returns a [**GamePadTriggers**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers) struct that can be used to retrieve the values of the left and right triggers. This struct contains the following properties: + +| Property | Type | Description | +|-----------------------------------------------------------------------|---------|--------------------------------| +| [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers.Left) | `float` | The value of the left trigger. | +| [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers.Right) | `float` | The value of the left trigger. | + +The trigger values are represented as a float value between `0.0f` (not pressed) to `1.0f` (fully pressed). The triggers on a gamepad, however, can be either *analog* or *digital* depending the gamepad manufacturer. For gamepads with *digital* triggers, the value will always be either `0.0f` or `1.0f`, as a digital trigger does not register values in between based on the amount of pressure applied to the trigger. + +For example, if we were creating a racing game, the right trigger could be used for acceleration like the following: + +```cs +float acceleration = GamePad.GetState(PlayerIndex.One).Triggers.Right; +``` + +### GamePadState Methods + +The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct also contains two methods that can be used to get information about the device's inputs as either being up or down: + +| Method | Description | +|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons)) | Returns a value that indicates whether the specified button is down. Multiple [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) values can be given using the bitwise OR `|` operator. When multiple buttons are given, the return value indicates if all buttons specified are down, not just one of them. | +| [**IsButtonUp(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonUp(Microsoft.Xna.Framework.Input.Buttons)) | Returns a value that indicates whether the specified button is up. Multiple [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) values can be given using the bitwise OR `|` operator. When multiple buttons are given, the return value indicates if all buttons specified are up, not just one of them. | + +You can use the [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons)) and [**IsButtonUp(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonUp(Microsoft.Xna.Framework.Input.Buttons)) methods to get the state of all buttons, including the DPad. The following is a complete list of all of the [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) enum values: + +- [**Buttons.A**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.B**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.Back**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.BigButton**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.DPadDown**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.DPadLeft**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.DPadRight**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.DPadUp**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftShoulder**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftStick**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftThumbstickDown**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftThumbstickLeft**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftThumbstickRight**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftThumbstickUp**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.LeftTrigger**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.None**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightShoulder**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightStick**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightStickDown**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightStickLeft**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightStickRight**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightStickUp**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.RightTrigger**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.Start**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.X**](xref:Microsoft.Xna.Framework.Input.Buttons) +- [**Buttons.Y**](xref:Microsoft.Xna.Framework.Input.Buttons) + + +> [!CAUTION] +> While you can use these methods to get the state of any of these button inputs, the state will only tell you if it is being pressed or released. For the actual joystick values and trigger values, you would need to use the properties instead. + +For example, if we wanted to check if the A button on the the first gamepad is pressed, you could use the following: + +```cs +if(GamePad.GetState(PlayerIndex.One).IsButtonDown(Buttons.A)) +{ + // The A button is pressed, do something. +} +``` + +### Implementing GamePad Input + +Let's implement gamepad controls as an alternative method of moving the slime sprite around. Open the *Game1.cs* file and perform the following: + +1. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), use the value of the left thumbstick to adjust the sprite's position. Since the value of the thumbstick is a range between `-1.0f` and `1.0f`, we can multiply those values by the `MOVEMENT_SPEED`. This will make the slime move slower or faster depending on how far in the direction the thumbstick is pushed. Add the following just before the `base.Update` call: + +```cs +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); +_slimePos.X += gamePadState.ThumbSticks.Left.X * MOVEMENT_SPEED; +_slimePos.Y -= gamePadState.ThumbSticks.Left.Y * MOVEMENT_SPEED; +``` + +Running the game now, you can move the slime sprite around using the left thumbstick on your gamepad. Try it out! Notice that the more you push the thumbstick in a particular direction, the faster the slime moves up to the movement speed cap. + +### GamePad Vibration + +Another thing we can do with a gamepad is tell it to vibrate. To do this, the [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad) class has a [**SetVibration**](xref:Microsoft.Xna.Framework.Input.GamePad.SetVibration(Microsoft.Xna.Framework.PlayerIndex,System.Single,System.Single)) method that requires the player index, and the speed of the left and right vibration motors. The speed can be any value from `0.0f` (no vibration) to `1.0f` (full vibration). + +Let's adjust the current code so that when the A button is pressed on the gamepad, it gives a slight speed boost to the slime as it moves. When moving with a speed boost, we can apply vibration to the gamepad as feedback to the player. Update the code you just added to the following: + +```cs +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if (gamePadState.Buttons.A == ButtonState.Pressed) +{ + _slimePos.X += gamePadState.ThumbSticks.Left.X * 1.5f * MOVEMENT_SPEED; + _slimePos.Y -= gamePadState.ThumbSticks.Left.Y * 1.5f * MOVEMENT_SPEED; + GamePad.SetVibration(PlayerIndex.One, 1.0f, 1.0f); +} +else +{ + _slimePos.X += gamePadState.ThumbSticks.Left.X * MOVEMENT_SPEED; + _slimePos.Y -= gamePadState.ThumbSticks.Left.Y * MOVEMENT_SPEED; + GamePad.SetVibration(PlayerIndex.One, 0.0f, 0.0f); +} +``` + +Running the game now, when you press the A button, the slime sprite will move slightly faster and you can feel the vibration. Try it out! + +## Conclusion + +In this chapter, you learned how to: + +- Handle keyboard input to detect key presses. +- Handle mouse input including button clicks and cursor position. +- Work with gamepad controls including buttons, thumbsticks, and vibration. +- Implement movement controls using different input methods. +- Consider controller-specific details like coordinate systems and analog vs digital input. + +In the next chapter, we'll learn how to track previous input states to handle single-press events and implement an input management system to simplify some of the complexity of handling input. + +## Test Your Knowledge + +1. Why do we store the result of GetState() in a variable instead of calling it multiple times? + +
+ Question 1 Answer + + > Storing the state in a variable is more efficient and ensures consistent input checking within a frame. Each GetState() call polls the device, which can impact performance if called repeatedly. +

+ +2. What's the main difference between how keyboard and mouse/gamepad button states are checked? + +
+ Question 2 Answer + + > Keyboard input uses IsKeyUp/IsKeyDown methods, while mouse and gamepad buttons return a ButtonState enum value (Pressed or Released). +

+ +3. When using thumbstick values for movement, why do we multiply the Y value by -1? + +
+ Question 3 Answer + + > The thumbstick Y-axis values (-1.0f down to 1.0f up) are inverted compared to MonoGame's screen coordinate system (Y increases downward). Multiplying by -1 aligns the thumbstick direction with screen movement. +

+ +4. What's the difference between analog and digital trigger input on a gamepad? + +
+ Question 4 Answer + + > Analog triggers provide values between 0.0f and 1.0f based on how far they're pressed, while digital triggers only report 0.0f (not pressed) or 1.0f (pressed). This affects how you handle trigger input in your game. +

\ No newline at end of file diff --git a/articles/tutorials/building_2d_games/index.md b/articles/tutorials/building_2d_games/index.md index 06165feb..ad2e6abb 100644 --- a/articles/tutorials/building_2d_games/index.md +++ b/articles/tutorials/building_2d_games/index.md @@ -29,6 +29,7 @@ This documentation will introduce game development concepts using the MonoGame f | [06: Optimizing Texture Rendering](06_optimizing_texture_rendering/index.md) | Explore optimization techniques when rendering textures using a texture atlas. | | | [07: The Sprite Class](07_the_sprite_class/index.md) | Explore creating a reusable Sprite class to efficiently sprites and their rendering properties, including position, rotation, scale, and more. | | | [08: The AnimatedSprite Class](07_the_sprite_class/index.md) | Create an AnimatedSprite class that builds upon our Sprite class to support frame-based animations. | | +| [09: Handling Input](09_handling_input/index.md) | Learn how to handle keyboard, mouse, and gamepad input in MonoGame. | | In additional to the chapter documentation, supplemental documentation is also provided to give a more in-depth look at different topics with MonoGame. These are provided through the Appendix documentation below: From 5938d56ae56606904157c54fbb1b29a58bda4c14 Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Fri, 7 Feb 2025 13:36:29 -0500 Subject: [PATCH 02/11] Add image of controller with button layout --- .../images/xbox-controller-back.svg | 129 ++++++++++++++++++ .../images/xbox-controller-front.svg | 2 + .../09_handling_input/index.md | 4 + 3 files changed, 135 insertions(+) create mode 100644 articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-back.svg create mode 100644 articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-back.svg b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-back.svg new file mode 100644 index 00000000..d00541b2 --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-back.svg @@ -0,0 +1,129 @@ + + + +Left ShoulderRight ShoulderLeft TriggerRight Trigger diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg new file mode 100644 index 00000000..4793fdfc --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg @@ -0,0 +1,2 @@ + +Left ThumbstickRight ThumbstickDPadBackStartBig ButtonYABXLeft ShoulderRight Shoulder diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index af4108ad..cdfad00e 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -203,6 +203,10 @@ The [**GamePadState.Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.B > [!NOTE] > Recall from [Chapter 01](../01_what_is_monogame/index.md) that MonoGame is a implementation the XNA API. Since XNA was originally created for making games on Windows PC and Xbox 360, the names of the gamepad buttons match those of an Xbox 360 controller. +> +> | Front | Back | +> |------------------------------------------------------------|----------------------------------------------------------| +> | ![Front Of Controller](./images/xbox-controller-front.svg) | ![Back Of Controller](./images/xbox-controller-back.svg) | Like with the [mouse input](#mousestate-struct), each of these buttons are represented by a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value. For instance, if you wanted to check if the A button is being pressed you could do the following: From 73d0904e04b08139a85e20dfc9d17115cd6595e7 Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Fri, 7 Feb 2025 13:47:15 -0500 Subject: [PATCH 03/11] Moved notice about storing state higher in document --- .../tutorials/building_2d_games/09_handling_input/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index cdfad00e..a209ed63 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -38,6 +38,9 @@ if(Keyboard.GetState().IsKeyDown(Keys.Space)) } ``` +> [!TIP] +> Notice we store the keyboard state in a variable instead of calling [**Keyboard.GetState**](xref:Microsoft.Xna.Framework.Input.Keyboard.GetState) multiple times. This is more efficient and ensures consistent input checking within a single frame. + ### Implementing Keyboard Input Let's implement keyboard controls to move our slime sprite around the screen. Open the *Game1.cs* file and perform the following: @@ -75,9 +78,6 @@ Let's implement keyboard controls to move our slime sprite around the screen. O } ``` - > [!TIP] - > Notice we store the keyboard state in a variable instead of calling [**Keyboard.GetState**](xref:Microsoft.Xna.Framework.Input.Keyboard.GetState) multiple times. This is more efficient and ensures consistent input checking within a single frame. - > [!NOTE] > Remember that the Y-axis increases downward in MonoGame's coordinate system. This is why we subtract from Y to move up and add to Y to move down. From f9ddda1263c19fff1d71391bba3c2d85a278801f Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Fri, 7 Feb 2025 13:47:52 -0500 Subject: [PATCH 04/11] Updated smaller example snippets to store state for consistency of note --- .../09_handling_input/index.md | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index a209ed63..d0ee7e20 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -32,7 +32,9 @@ The [**KeyboardState**](xref:Microsoft.Xna.Framework.Input.KeyboardState) struct For example, if we wanted to see if the Space key is down, you could use the following: ```cs -if(Keyboard.GetState().IsKeyDown(Keys.Space)) +KeyboardState keyboardState = Keyboard.GetState(); + +if(keyboardState.IsKeyDown(Keys.Space)) { // The space key is down, so do something. } @@ -118,7 +120,9 @@ Unlike keyboard input which uses [**IsKeyDown(Keys)**](xref:Microsoft.Xna.Framew For example, if we wanted to see if the left mouse button is down, you could use the following ```cs -if(Mouse.GetState().LeftButton == ButtonState.Pressed) +MouseState mouseState = Mouse.GetState(); + +if(mouseState.LeftButton == ButtonState.Pressed) { // The left button is down, so do something. } @@ -211,7 +215,9 @@ The [**GamePadState.Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.B Like with the [mouse input](#mousestate-struct), each of these buttons are represented by a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value. For instance, if you wanted to check if the A button is being pressed you could do the following: ```cs -if(GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed) +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if(gamePadState.Buttons.A == ButtonState.Pressed) { // Button A is pressed, do something. } @@ -231,7 +237,9 @@ The [**DPad**](xref:Microsoft.Xna.Framework.Input.GamePadState.DPad) property r Like with the [Buttons](#buttons), these also return a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value to represent the state of the DPad button. For instance, if you wanted to check if the DPad up button is being pressed, you could do the following: ```cs -if(GamePad.GetState(PlayerIndex.One).DPad.Down == ButtonState.Pressed) +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if(gamePadState.DPad.Down == ButtonState.Pressed) { // DPad down is pressed, do something. } @@ -254,8 +262,11 @@ The thumbstick values are represented as a [**Vector2**](xref:Microsoft.Xna.Fram For example, if you wanted to move a sprite using the left thumbstick, you could do the following ```cs -Vector2 leftStick = GamePad.GetState(PlayerIndex.One).Thumbsticks.Left; +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +Vector2 leftStick = gamePadState.Thumbsticks.Left; leftStick.Y *= -1.0f; + sprite.Position += leftStick; ``` @@ -324,7 +335,9 @@ You can use the [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.G For example, if we wanted to check if the A button on the the first gamepad is pressed, you could use the following: ```cs -if(GamePad.GetState(PlayerIndex.One).IsButtonDown(Buttons.A)) +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if(gamePadState.IsButtonDown(Buttons.A)) { // The A button is pressed, do something. } @@ -338,6 +351,7 @@ Let's implement gamepad controls as an alternative method of moving the slime sp ```cs GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + _slimePos.X += gamePadState.ThumbSticks.Left.X * MOVEMENT_SPEED; _slimePos.Y -= gamePadState.ThumbSticks.Left.Y * MOVEMENT_SPEED; ``` From c83fa8d1d86bb74a14973310488161ed2c00e50a Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Fri, 7 Feb 2025 15:22:58 -0500 Subject: [PATCH 05/11] Rewrite notes on screen coordinates to include callback references --- .../building_2d_games/09_handling_input/index.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index d0ee7e20..1a8c8b71 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -80,8 +80,8 @@ Let's implement keyboard controls to move our slime sprite around the screen. O } ``` - > [!NOTE] - > Remember that the Y-axis increases downward in MonoGame's coordinate system. This is why we subtract from Y to move up and add to Y to move down. + > [!IMPORTANT] + > Why are we subtracting from the Y position when moving up instead of adding? Recall from [Chapter 05](../05_working_with_textures/index.md#drawing-a-texture) that MonoGame uses a coordinate system where the Y value **increases** moving down. So in order to move **up** the screen, we need to reduce the Y value. 3. Finally, in [**Draw**](xref:Microsoft.Xna.Framework.Game.Draw(Microsoft.Xna.Framework.GameTime)), update the position of the slime when it is rendered by using the `_slimePosition` value: @@ -271,7 +271,9 @@ sprite.Position += leftStick; ``` > [!IMPORTANT] -> When the Y-Axis is fully pushed downward, its value is `-1.0f`. Recall from [Chapter 05](../05_working_with_textures/index.md#drawing-a-texture) that MonoGame uses a coordinate system where the Y values **increase** moving down. This is directly opposite of the value represented by the thumbstick. To resolve this, the common approach is to multiply the Y value of the thumbstick by `-1.0f`. This is why it is done in the example above. +> Notice that we inverted the y-axis value of the thumbstick by multiplying it by `-1.0f`. This is necessary because the thumbstick y-axis values range from `-1.0f` (down) to `1.0f` (up). The y-axis of the screen coordinates in MonoGame **increases** downward, as we saw in [Chapter 05](../05_working_with_textures/index.md#drawing-a-texture) and in the [Implementing Keyboard Input](#implementing-keyboard-input) section above. +> +> This inversion aligns the thumbstick's y-axis value with the screen movement. #### Triggers From f24f8f8257c52a359c36bb3b291fe1ad51690d38 Mon Sep 17 00:00:00 2001 From: Christopher Whitley <103014489+AristurtleDev@users.noreply.github.com> Date: Fri, 7 Feb 2025 17:19:57 -0500 Subject: [PATCH 06/11] Added playstation controller image --- .../images/pas-controller-front.svg | 7750 +++++++++++++++++ .../images/ps-controller-back.svg | 206 + .../images/ps-controller-front.svg | 481 + .../images/xbox-controller-front.svg | 2 +- .../09_handling_input/index.md | 3 + 5 files changed, 8441 insertions(+), 1 deletion(-) create mode 100644 articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg create mode 100644 articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-back.svg create mode 100644 articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg new file mode 100644 index 00000000..361460ef --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg @@ -0,0 +1,7750 @@ + + + +xLeft ThumbstickRight ShoulderLeft ShoulderXBAYBig ButtonStartBackDPadRight Thumbstick diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-back.svg b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-back.svg new file mode 100644 index 00000000..4a9f39ba --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-back.svg @@ -0,0 +1,206 @@ + + + +Right ShoulderLeft ShoulderLeft TriggerRight Trigger diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg new file mode 100644 index 00000000..af4f8314 --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg @@ -0,0 +1,481 @@ + + + +xLeft ThumbstickRight ShoulderLeft ShoulderXBAYBig ButtonStartBackDPadRight Thumbstick diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg index 4793fdfc..ec65bee8 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg +++ b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg @@ -1,2 +1,2 @@ -Left ThumbstickRight ThumbstickDPadBackStartBig ButtonYABXLeft ShoulderRight Shoulder +Left ThumbstickRight ThumbstickDPadBackStartBig ButtonYABXLeft ShoulderRight ShoulderABXY diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index 1a8c8b71..6b702f19 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -210,7 +210,10 @@ The [**GamePadState.Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.B > > | Front | Back | > |------------------------------------------------------------|----------------------------------------------------------| +> | Xbox | | > | ![Front Of Controller](./images/xbox-controller-front.svg) | ![Back Of Controller](./images/xbox-controller-back.svg) | +> | Playstation | | +> | ![Front Of Controller](./images/ps-controller-front.svg) | ![Back Of Controller](./images/ps-controller-back.svg) | Like with the [mouse input](#mousestate-struct), each of these buttons are represented by a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value. For instance, if you wanted to check if the A button is being pressed you could do the following: From dc750e93a6fb30463ada0759eb62cb6030058aa7 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Fri, 7 Feb 2025 20:40:24 -0500 Subject: [PATCH 07/11] Update images --- .../images/pas-controller-front.svg | 7750 ----------------- .../images/ps-controller-front.svg | 482 +- .../images/xbox-controller-front.svg | 2 +- 3 files changed, 3 insertions(+), 8231 deletions(-) delete mode 100644 articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg deleted file mode 100644 index 361460ef..00000000 --- a/articles/tutorials/building_2d_games/09_handling_input/images/pas-controller-front.svg +++ /dev/null @@ -1,7750 +0,0 @@ - - - -xLeft ThumbstickRight ShoulderLeft ShoulderXBAYBig ButtonStartBackDPadRight Thumbstick diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg index af4f8314..cb6b268f 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg +++ b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg @@ -1,481 +1,3 @@ - + - -xLeft ThumbstickRight ShoulderLeft ShoulderXBAYBig ButtonStartBackDPadRight Thumbstick +xLeft ThumbstickRight ShoulderLeft ShoulderXBAYStartBackDPadRight Thumbstick diff --git a/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg index ec65bee8..338ce85f 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg +++ b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg @@ -1,2 +1,2 @@ -Left ThumbstickRight ThumbstickDPadBackStartBig ButtonYABXLeft ShoulderRight ShoulderABXY +Left ThumbstickRight ThumbstickDPadBackStartYABXLeft ShoulderRight ShoulderABXY From 7df428f40bf0a0b0f7386187f67530222f2f7694 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Fri, 7 Feb 2025 20:59:19 -0500 Subject: [PATCH 08/11] Replace `joysticks` with `thumbsticks` --- .../building_2d_games/09_handling_input/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index 6b702f19..43353192 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -7,7 +7,7 @@ When you play a game, you need ways to control what's happening; using a keyboar - [**Keyboard**](xref:Microsoft.Xna.Framework.Input.Keyboard): Detects which keys are being pressed. - [**Mouse**](xref:Microsoft.Xna.Framework.Input.Mouse): Tracks mouse movement, button clicks, and scroll wheel use. -- [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad): Manages controller input like button presses and joystick movement. +- [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad): Manages controller input like button presses and thumbstick movement. > [!NOTE] > MonoGame also supports touch and accelerometer input for mobile devices, however this tutorial focuses on desktop input handling using keyboard, mouse, and gamepad controls. @@ -173,11 +173,11 @@ Running the game now, you can move the bat sprite around by clicking the left mo ## Gamepad Input -Gamepads are often used as a primary input for a game or an alternative for keyboard and mouse controls. MonoGame provides the [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad) class to handle gamepad input, making it easy to detect which buttons are pressed and the value of the joysticks. Calling [**GamePad.GetState**](xref:Microsoft.Xna.Framework.Input.GamePad.GetState(Microsoft.Xna.Framework.PlayerIndex)) will retrieve the state of the gamepad as a [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct. Since multiple gamepads can be connected, you will need to supply a [**PlayerIndex**](xref:Microsoft.Xna.Framework.PlayerIndex) value to specify which gamepad state to retrieve. +Gamepads are often used as a primary input for a game or an alternative for keyboard and mouse controls. MonoGame provides the [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad) class to handle gamepad input, making it easy to detect which buttons are pressed and the value of the thumbsticks. Calling [**GamePad.GetState**](xref:Microsoft.Xna.Framework.Input.GamePad.GetState(Microsoft.Xna.Framework.PlayerIndex)) will retrieve the state of the gamepad as a [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct. Since multiple gamepads can be connected, you will need to supply a [**PlayerIndex**](xref:Microsoft.Xna.Framework.PlayerIndex) value to specify which gamepad state to retrieve. ### GamePadState Struct -The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct and properties that can be used to get the state of the buttons, dpad, triggers, and joysticks: +The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct and properties that can be used to get the state of the buttons, dpad, triggers, and thumbsticks: | Property | Type | Description | |--------------------------------------------------------------------------------|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -335,7 +335,7 @@ You can use the [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.G > [!CAUTION] -> While you can use these methods to get the state of any of these button inputs, the state will only tell you if it is being pressed or released. For the actual joystick values and trigger values, you would need to use the properties instead. +> While you can use these methods to get the state of any of these button inputs, the state will only tell you if it is being pressed or released. For the actual thumbstick values and trigger values, you would need to use the properties instead. For example, if we wanted to check if the A button on the the first gamepad is pressed, you could use the following: From 65314389be8d9a82668df195d7f759cdd368b207 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Sat, 8 Feb 2025 00:19:09 -0500 Subject: [PATCH 09/11] Added touch input section --- .../09_handling_input/index.md | 183 ++++++++++++++++-- 1 file changed, 172 insertions(+), 11 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index 43353192..f2bda03e 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -8,6 +8,7 @@ When you play a game, you need ways to control what's happening; using a keyboar - [**Keyboard**](xref:Microsoft.Xna.Framework.Input.Keyboard): Detects which keys are being pressed. - [**Mouse**](xref:Microsoft.Xna.Framework.Input.Mouse): Tracks mouse movement, button clicks, and scroll wheel use. - [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad): Manages controller input like button presses and thumbstick movement. +- [**TouchPanel**](xref:Microsoft.Xna.Framework.Input.TouchPanel): Manages touch input on devices with a touch panel such as mobile phones and tablets. > [!NOTE] > MonoGame also supports touch and accelerometer input for mobile devices, however this tutorial focuses on desktop input handling using keyboard, mouse, and gamepad controls. @@ -25,7 +26,7 @@ The keyboard is often the primary input device for PC games, used for everything The [**KeyboardState**](xref:Microsoft.Xna.Framework.Input.KeyboardState) struct contains methods that can be used to determine if a keyboard key is currently down or up: | Method | Description | -|-----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| +| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | | [**IsKeyDown(Keys)**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys)) | Returns `true` if the specified key is down; otherwise, returns `false`. | | [**IsKeyUp(Keys)**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyUp(Microsoft.Xna.Framework.Input.Keys)) | Returns `true` if the specified key is up; otherwise, returns `false`. | @@ -100,7 +101,7 @@ The mouse is often the secondary input device for PC games, used for various act The [**MouseState**](xref:Microsoft.Xna.Framework.Input.MouseState) struct contains properties that can be used to determine the state of the mouse buttons, the mouse position, and the scroll wheel value: | Property | Type | Description | -|----------------------------------------------------------------------------------------|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | [**LeftButton**](xref:Microsoft.Xna.Framework.Input.MouseState.LeftButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the left mouse button. | | [**MiddleButton**](xref:Microsoft.Xna.Framework.Input.MouseState.MiddleButton) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the middle mouse button. This is often the button when pressing the scroll wheel down as a button | | [**Position**](xref:Microsoft.Xna.Framework.Input.MouseState.Position) | [**Point**](xref:Microsoft.Xna.Framework.Point) | Returns the position of the mouse cursor relative to the bounds of the game window. | @@ -180,7 +181,7 @@ Gamepads are often used as a primary input for a game or an alternative for keyb The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct and properties that can be used to get the state of the buttons, dpad, triggers, and thumbsticks: | Property | Type | Description | -|--------------------------------------------------------------------------------|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [**Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.Buttons) | [**GamePadButtons**](xref:Microsoft.Xna.Framework.Input.GamePadButtons) | Returns a struct that identifies which buttons on the controller are pressed. | | [**DPad**](xref:Microsoft.Xna.Framework.Input.GamePadState.DPad) | [**GamePadDPad**](xref:Microsoft.Xna.Framework.Input.GamePadDPad) | Returns a struct that identifies which directions on the DPad are pressed. | | [**IsConnected**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsConnected) | `bool` | Returns a value that indicates whether the controller is connected. | @@ -192,7 +193,7 @@ The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct a The [**GamePadState.Buttons**](xref:Microsoft.Xna.Framework.Input.GamePadState.Buttons) property returns a [**GamePadButtons**](xref:Microsoft.Xna.Framework.Input.GamePadButtons) struct that can be used to identify which buttons on the controller are pressed. This struct contains the following properties: | Property | Type | Description | -|--------------------------------------------------------------------------------------|-------------------------------------------------------------------|-----------------------------------------------| +| ------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | --------------------------------------------- | | [**A**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.A) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the A button | | [**B**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.B) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the B button | | [**Back**](xref:Microsoft.Xna.Framework.Input.GamePadButtons.Back) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the Back button | @@ -231,7 +232,7 @@ if(gamePadState.Buttons.A == ButtonState.Pressed) The [**DPad**](xref:Microsoft.Xna.Framework.Input.GamePadState.DPad) property returns a [**GamePadDPad**](xref:Microsoft.Xna.Framework.Input.GamePadDPad) struct that can be used to identify which DPad buttons on the controller are pressed. This struct contains the following properties: | Property | Type | Description | -|------------------------------------------------------------------|-------------------------------------------------------------------|---------------------------------------------| +| ---------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------- | | [**Down**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Down button. | | [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Left button. | | [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadDPad.Down) | [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) | Returns the state of the DPad Right button. | @@ -253,7 +254,7 @@ if(gamePadState.DPad.Down == ButtonState.Pressed) The [**ThumbSticks**](xref:Microsoft.Xna.Framework.Input.GamePadState.ThumbSticks) property returns a [**GamePadThumbSticks**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks) struct that can be used to retrieve the values of the left and right thumbsticks. This struct contains the following properties: | Property | Type | Description | -|--------------------------------------------------------------------------|-----------------------------------------------------|------------------------------------------------| +| ------------------------------------------------------------------------ | --------------------------------------------------- | ---------------------------------------------- | | [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks.Left) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The direction the left thumbstick is pressed. | | [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadThumbSticks.Right) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The direction the right thumbstick is pressed. | @@ -283,7 +284,7 @@ sprite.Position += leftStick; The [**Triggers**](xref:Microsoft.Xna.Framework.Input.GamePadState.Triggers) property returns a [**GamePadTriggers**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers) struct that can be used to retrieve the values of the left and right triggers. This struct contains the following properties: | Property | Type | Description | -|-----------------------------------------------------------------------|---------|--------------------------------| +| --------------------------------------------------------------------- | ------- | ------------------------------ | | [**Left**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers.Left) | `float` | The value of the left trigger. | | [**Right**](xref:Microsoft.Xna.Framework.Input.GamePadTriggers.Right) | `float` | The value of the left trigger. | @@ -299,10 +300,10 @@ float acceleration = GamePad.GetState(PlayerIndex.One).Triggers.Right; The [**GamePadState**](xref:Microsoft.Xna.Framework.Input.GamePadState) struct also contains two methods that can be used to get information about the device's inputs as either being up or down: -| Method | Description | -|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons)) | Returns a value that indicates whether the specified button is down. Multiple [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) values can be given using the bitwise OR `|` operator. When multiple buttons are given, the return value indicates if all buttons specified are down, not just one of them. | -| [**IsButtonUp(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonUp(Microsoft.Xna.Framework.Input.Buttons)) | Returns a value that indicates whether the specified button is up. Multiple [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) values can be given using the bitwise OR `|` operator. When multiple buttons are given, the return value indicates if all buttons specified are up, not just one of them. | +| Method | Description | +| -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons)) | Returns a value that indicates whether the specified button is down. Multiple [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) values can be given using the bitwise OR ` | ` operator. When multiple buttons are given, the return value indicates if all buttons specified are down, not just one of them. | +| [**IsButtonUp(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonUp(Microsoft.Xna.Framework.Input.Buttons)) | Returns a value that indicates whether the specified button is up. Multiple [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) values can be given using the bitwise OR ` | ` operator. When multiple buttons are given, the return value indicates if all buttons specified are up, not just one of them. | You can use the [**IsButtonDown(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons)) and [**IsButtonUp(Buttons)**](xref:Microsoft.Xna.Framework.Input.GamePadState.IsButtonUp(Microsoft.Xna.Framework.Input.Buttons)) methods to get the state of all buttons, including the DPad. The following is a complete list of all of the [**Buttons**](xref:Microsoft.Xna.Framework.Input.Buttons) enum values: @@ -388,6 +389,165 @@ else Running the game now, when you press the A button, the slime sprite will move slightly faster and you can feel the vibration. Try it out! +## TouchPanel Input + +For mobile devices such as Android/iOS phones and tablets, the primary input device is the touch panel screen. Touching a location on the screen is similar to clicking a location on your computer with a mouse. MonoGame provides the [**TouchPanel**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel) class to handle touch input. + +The [**TouchPanel**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel) class offers two ways of retrieving information about touch input: + +- [**TouchPanel.GetState**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.GetState) retrieves a [**TouchCollection**](xref:Microsoft.Xna.Framework.Input.Touch.TouchCollection) struct that contains [**TouchLocation**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation) values for each point of touch on the touch panel. +- [**TouchPanel.ReadGesture**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.ReadGesture) retrieves a [**GestureSample**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample) struct that contains information about recent gestures that have been performed like a vertical or horizontal drag across the screen. + +### TouchCollection + +When calling [**TouchPanel.GetState**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.GetState) a [**TouchCollection**](xref:Microsoft.Xna.Framework.Input.Touch.TouchCollection) struct is returned. This collection contains a [**TouchLocation**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation) value for each point of touch. + +#### TouchLocation + +Each [**TouchLocation**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation) value in a touch collection contains the following properties + +| Property | Type | Description | +| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| [**Id**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.Id) | `int` | The id of the touch location. | +| [**Position**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.Position) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | The position of the touch location. | +| [**Pressure**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.Pressure) | `float` | The amount of pressure applied at the touch location. **(Only available for Android devices.)** | +| [**State**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) | [**TouchLocationState**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocationState) | The current state of the touch location. | + +The important properties of the location are the [**Position**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.Position) and the [**State**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) The position property will tell us the location of the touch event, and the state can be one of the following values: + +| State | Description | +| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------- | +| [**Invalid**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) | This touch location position is invalid. | +| [**Moved**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) | This touch location position was updated or pressed at the same position. | +| [**Pressed**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) | This touch location was pressed. | +| [**Released**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) | This touch location was released. | + +When the state is moved or pressed, then we know that location on the touch panel is being touched. So we can capture it and use it like the following: + +```cs +TouchCollection touchCollection = TouchPanel.GetState(); + +foreach(TouchLocation touchLocation in touchCollection) +{ + if(touchLocation.State == TouchLocationState.Pressed || touchLocation.State == TouchLocationState.Moved) + { + // The the location at touchLocation.Position is currently being pressed, + // so we can act on that information. + } +} +``` + +> [!NOTE] +> Unlike mouse input which only tracks a single point, [**TouchPanel**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel) supports multiple simultaneous touch points. The [**TouchCollection**](xref:Microsoft.Xna.Framework.Input.Touch.TouchCollection) contains all active touch points, which is why we loop through them in the sample above. + +The state of a touch location progresses through the states typically in order of: + +- [**Pressed**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State): Initial contact with the screen. +- [**Moved**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) : Touch point moved while maintaining contact. +- [**Released**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State): Contact with screen ended. +- [**Invalid**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation.State) : Touch data is invalid (using when tracking data is lost). + +### GestureSample + +When calling [**TouchPanel.ReadGesture**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.ReadGesture) a [**GestureSample**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample) struct containing the information about recent gestures that have been performed is returned. The [**GestureSample**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample) struct contains the following properties: + +| Property | Type | Description | +| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| [**Delta**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample.Delta) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | Gets the delta information about the first touch-point in the gesture sample. | +| [**Delta2**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample.Delta2) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | Gets the delta information about the second touch-point in the gesture sample. | +| [**GestureType**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample.GestureType) | [**GestureType**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | Gets the type of the gesture. | +| [**Position**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample.Position) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | Gets the position of the first touch-point in the gesture sample. | +| [**Position2**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample.Position2) | [**Vector2**](xref:Microsoft.Xna.Framework.Vector2) | Gets the position of the second touch-point in the gesture sample. | + +> [!NOTE] +> Gestures have two delta properties and two position properties. This is because some gestures require multiple touch inputs to perform, such as performing a pinch to zoom in or out. You would need the location of both touch points to determine the correct zoom to apply during the gesture. + +To determine what type of gesture is performed, we can get that from the [**GestureType**](xref:Microsoft.Xna.Framework.Input.Touch.GestureSample.GestureType) property which will be one of the following values: + +| Gesture Type | Description | +| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| [**DoubleTap**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user double tapped the device twice which is always preceded by a Tap gesture. | +| [**DragComplete**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | States completion of a drag gesture (VerticalDrag, HorizontalDrag, or FreeDrag). | +| [**Flick**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | States that a touch was combined with a quick swipe. | +| [**FreeDrag**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched a point and the performed a free-form drag. | +| [**Hold**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched a single point for approximately one second. | +| [**HorizontalDrag**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched the screen and performed either a left-to-right or right-to-left drag gesture. | +| [**None**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | No gesture. | +| [**Pinch**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user converged or diverged two touch-points on the screen which is like a two-finger drag. | +| [**PinchComplete**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | An in-progress pinch gesture was completed. | +| [**Tap**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched a single point. | +| [**VerticalDrag**](xref:Microsoft.Xna.Framework.Input.Touch.GestureType) | The user touched the screen and performed either a top-to-bottom or bottom-to-top drag gesture. | + +> [!IMPORTANT] +> Before gestures can be detected, they have to be enabled using [**TouchPanel.EnabledGestures**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.EnabledGestures). This can be done in [**Game.Initialize**](xref:Microsoft.Xna.Framework.Game.Initialize) like the following: +> +> ```cs +> protected override void Initialize() +> { +> base.Initialize(); +> +> // Enable gestures we want to handle +> TouchPanel.EnabledGestures = +> GestureType.Tap | +> GestureType.HorizontalDrag | +> GestureType.VerticalDrag; +> } + +The following is an example of using a gesture to detect horizontal and vertical drags: + +```cs +while(TouchPanel.IsGestureAvailable) +{ + GestureSample gesture = TouchPanel.ReadGesture(); + + if(gesture.GestureType == GestureType.HorizontalDrag) + { + // A horizontal drag from left-to-right or right-to-left occurred. + // You can use the Delta property to determine how much movement + // occurred during the swipe. + float xDragAmount = gesture.Delta.X; + + // Now do something with that information. + } + + if(gesture.GestureType == GestureType.VerticalDrag) + { + // A vertical drag from top-to-bottom or bottom-to-top occurred. + // You can use the Delta property to determine how much movement + // occurred during the swipe. + float yDragAmount = gesture.Delta.Y; + + // Now do something with that information. + } +} +``` + +> [!IMPORTANT] +> Notice above that we use a `while` loop with [**TouchPanel.IsGestureAvailable**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.IsGestureAvailable) as the condition for the loop. The reason we do this is because when a user performs a gesture, such as a horizontal drag across the screen, very quickly, what can often occurs is a series of multiple small drag gestures are registered and queued. +> +> Each time [**TouchPanel.ReadGesture**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.ReadGesture) is called, it will dequeue the next gesture. So to ensure that we handle the complete gesture, we loop the gesture queue until there are none left. + +### Implementing TouchPanel Input (Optional) + +> [!NOTE] +> This section is optional. This tutorial does not go into detail on creating mobile projects where a touch screen would be available for input. However, the following code is implemented as a reference. + +Let's implement touch controls to move the bat sprite around the screen to the point that the screen is touched, similar to what we did for mouse controls in the [Implementing Mouse Input](#implementing-mouse-input) section above. Open *Game1.cs* and perform the following: + +1. In [**Update**](xref:Microsoft.Xna.Framework.Game.Update(Microsoft.Xna.Framework.GameTime)), check for a touch location and move the bat sprite to that location if a touch occurs. Add the following just before the `base.Update` call: + +```cs +TouchCollection touchCollection = TouchPanel.GetState(); + +if(touchCollection.Count > 0) +{ + TouchLocation touchLocation = touchCollection[0]; + _batSprite.Position = touchLocation.Position; +} +``` + +If you have your development environment setup for mobile development, running the game now, you can touch the screen to move the bat to the point that was touched. + ## Conclusion In this chapter, you learned how to: @@ -395,6 +555,7 @@ In this chapter, you learned how to: - Handle keyboard input to detect key presses. - Handle mouse input including button clicks and cursor position. - Work with gamepad controls including buttons, thumbsticks, and vibration. +- Understand touch input for mobile devices including touch points and gestures. - Implement movement controls using different input methods. - Consider controller-specific details like coordinate systems and analog vs digital input. From 8747fb7baada6797177691c7872547e2ef00c8d0 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Sat, 8 Feb 2025 12:36:33 -0500 Subject: [PATCH 10/11] Additional questions --- .../09_handling_input/index.md | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index f2bda03e..0959a88a 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -563,12 +563,12 @@ In the next chapter, we'll learn how to track previous input states to handle si ## Test Your Knowledge -1. Why do we store the result of GetState() in a variable instead of calling it multiple times? +1. Why do we store the result of `GetState` in a variable instead of calling it multiple times?
Question 1 Answer - > Storing the state in a variable is more efficient and ensures consistent input checking within a frame. Each GetState() call polls the device, which can impact performance if called repeatedly. + > Storing the state in a variable is more efficient and ensures consistent input checking within a frame. Each `GetState` call polls the device, which can impact performance if called repeatedly.

2. What's the main difference between how keyboard and mouse/gamepad button states are checked? @@ -576,7 +576,7 @@ In the next chapter, we'll learn how to track previous input states to handle si
Question 2 Answer - > Keyboard input uses IsKeyUp/IsKeyDown methods, while mouse and gamepad buttons return a ButtonState enum value (Pressed or Released). + > Keyboard input uses [**IsKeyUp**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyUp(Microsoft.Xna.Framework.Input.Keys))/[**IsKeyDown**](xref:Microsoft.Xna.Framework.Input.KeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys)) methods, while mouse and gamepad buttons return a [**ButtonState**](xref:Microsoft.Xna.Framework.Input.ButtonState) enum value (Pressed or Released).

3. When using thumbstick values for movement, why do we multiply the Y value by -1? @@ -593,4 +593,40 @@ In the next chapter, we'll learn how to track previous input states to handle si Question 4 Answer > Analog triggers provide values between 0.0f and 1.0f based on how far they're pressed, while digital triggers only report 0.0f (not pressed) or 1.0f (pressed). This affects how you handle trigger input in your game. -
\ No newline at end of file +
+ +5. What's the key difference between [**TouchPanel.GetState**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.GetState) and [**TouchPanel.ReadGesture**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.ReadGesture)? + +
+ Question 5 Answer + + > [**TouchPanel.GetState**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.GetState) returns information about current touch points on the screen, while [**TouchPanel.ReadGesture**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.ReadGesture) provides information about specific gesture patterns like taps, drags, and pinches that have been performed. +

+ +6. Why do we use a while loop with [**TouchPanel.IsGestureAvailable**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.IsGestureAvailable) when reading gestures? + +
+ Question 6 Answer + + > Quick gestures can generate multiple gesture events that are queued. Using a while loop with [**TouchPanel.IsGestureAvailable**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.IsGestureAvailable) ensures we process all queued gestures, as [**TouchPanel.ReadGesture**](xref:Microsoft.Xna.Framework.Input.Touch.TouchPanel.ReadGesture) only returns one gesture at a time. +

+ +7. How does touch input differ from mouse input in terms of handling multiple input points? + +
+ Question 7 Answer + + > Touch input can handle multiple simultaneous touch points through the [**TouchCollection**](Microsoft.Xna.Framework.Input.Touch.TouchCollection), while mouse input only tracks a single cursor position. This allows touch input to support features like multi-touch gestures that aren't possible with a mouse. +

+ +8. What are the different states a [**TouchLocation**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation) can have and what do they indicate? + +
+ Question 8 Answer + + > A [**TouchLocation**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocation) can have four states: + > - [**Pressed**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocationState): Initial contact with the screen + > - [**Moved**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocationState): Touch point moved while maintaining contact + > - [**Released**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocationState): Contact with the screen ended + > - [**Invalid**](xref:Microsoft.Xna.Framework.Input.Touch.TouchLocationState): Touch data is not valid or tracking was lost +

\ No newline at end of file From 592416773b5897421ad5d15ab3b2b30865772c23 Mon Sep 17 00:00:00 2001 From: Christopher Whitley Date: Sat, 8 Feb 2025 12:42:02 -0500 Subject: [PATCH 11/11] Remove notice about not talking about mobile since Simon requested to talk about touch input --- .../tutorials/building_2d_games/09_handling_input/index.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/articles/tutorials/building_2d_games/09_handling_input/index.md b/articles/tutorials/building_2d_games/09_handling_input/index.md index 0959a88a..dfb7ab63 100644 --- a/articles/tutorials/building_2d_games/09_handling_input/index.md +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -10,9 +10,6 @@ When you play a game, you need ways to control what's happening; using a keyboar - [**GamePad**](xref:Microsoft.Xna.Framework.Input.GamePad): Manages controller input like button presses and thumbstick movement. - [**TouchPanel**](xref:Microsoft.Xna.Framework.Input.TouchPanel): Manages touch input on devices with a touch panel such as mobile phones and tablets. -> [!NOTE] -> MonoGame also supports touch and accelerometer input for mobile devices, however this tutorial focuses on desktop input handling using keyboard, mouse, and gamepad controls. - Each of these input types has a `GetState` method that, when called, checks what is happening with that device at that moment. Think of it like taking a snapshot; when you call `GetState`, MonoGame looks at that exact moment to see which buttons are pressed, where the mouse is, or how the controller is being used. In this chapter you will, we will learn how to use each of these dedicated input classes to handle player input.