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/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..cb6b268f --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/ps-controller-front.svg @@ -0,0 +1,3 @@ + + +xLeft ThumbstickRight ShoulderLeft ShoulderXBAYStartBackDPadRight Thumbstick 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..338ce85f --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/images/xbox-controller-front.svg @@ -0,0 +1,2 @@ + +Left ThumbstickRight ThumbstickDPadBackStartYABXLeft 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 new file mode 100644 index 00000000..dfb7ab63 --- /dev/null +++ b/articles/tutorials/building_2d_games/09_handling_input/index.md @@ -0,0 +1,629 @@ +--- +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 thumbstick movement. +- [**TouchPanel**](xref:Microsoft.Xna.Framework.Input.TouchPanel): Manages touch input on devices with a touch panel such as mobile phones and tablets. + +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 +KeyboardState keyboardState = Keyboard.GetState(); + +if(keyboardState.IsKeyDown(Keys.Space)) +{ + // The space key is down, so do something. +} +``` + +> [!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: + +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; + } + ``` + + > [!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: + + ```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 +MouseState mouseState = Mouse.GetState(); + +if(mouseState.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 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 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. | +| [**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. +> +> | 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: + +```cs +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if(gamePadState.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 +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if(gamePadState.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 +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +Vector2 leftStick = gamePadState.Thumbsticks.Left; +leftStick.Y *= -1.0f; + +sprite.Position += leftStick; +``` + +> [!IMPORTANT] +> 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 + +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 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: + +```cs +GamePadState gamePadState = GamePad.GetState(PlayerIndex.One); + +if(gamePadState.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! + +## 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: + +- 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. + +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**](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? + +
+ 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. +

+ +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 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: