Interaction & UI

Handling Input

Most MonoGame input is polling-based: each frame you ask the keyboard, mouse, or gamepad what its current state is, then update your game from that snapshot. This tutorial explains held input, single-press input, mouse clicks, scroll wheel deltas, and the basics of gamepad reading before combining keyboard and mouse in a complete runnable sample.

Download the runnable sample project

Use the verified ZIP bundle for this article to review the extracted example files, run the sample host, and repackage the sources without bin or obj folders.

Keyboard state

Read the keyboard during Update with Keyboard.GetState(). The returned KeyboardState represents which keys are down right now. Use IsKeyDown when an action should continue every frame a key is held, and IsKeyUp when you need to confirm the key is not pressed.

private Vector2 _position = new Vector2(100, 100);
private const float Speed = 240f;

protected override void Update(GameTime gameTime)
{
    KeyboardState keyboard = Keyboard.GetState();
    float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
    Vector2 direction = Vector2.Zero;

    if (keyboard.IsKeyDown(Keys.W) || keyboard.IsKeyDown(Keys.Up)) direction.Y -= 1f;
    if (keyboard.IsKeyDown(Keys.S) || keyboard.IsKeyDown(Keys.Down)) direction.Y += 1f;
    if (keyboard.IsKeyDown(Keys.A) || keyboard.IsKeyDown(Keys.Left)) direction.X -= 1f;
    if (keyboard.IsKeyDown(Keys.D) || keyboard.IsKeyDown(Keys.Right)) direction.X += 1f;

    if (direction != Vector2.Zero)
        direction.Normalize();

    _position += direction * Speed * dt;

    base.Update(gameTime);
}

💡 Normalising the direction vector prevents diagonal movement being faster than horizontal or vertical movement.

Pressed vs held keys

A held key is down this frame and can repeat every frame, which is ideal for movement. A single key press is an edge: the key is down this frame but was up last frame. To detect that edge, store both previous and current input states.

private KeyboardState _previousKeyboard;
private KeyboardState _currentKeyboard;
private bool _isJumping;

protected override void Update(GameTime gameTime)
{
    _previousKeyboard = _currentKeyboard;
    _currentKeyboard = Keyboard.GetState();

    bool spacePressed = _currentKeyboard.IsKeyDown(Keys.Space) &&
                        _previousKeyboard.IsKeyUp(Keys.Space);

    if (spacePressed && !_isJumping)
    {
        StartJump();
    }

    base.Update(gameTime);
}

⚠️ Warning: Do not check only IsKeyDown(Keys.Space) for a jump, menu selection, or attack that should happen once. Without the previous-state comparison it fires every frame while the key is held.

⚠️ Caution: Copy the previous state before reading the new current state. If you assign both after polling, both variables may describe the same frame and the edge test will never work.

Mouse state

Read the mouse with Mouse.GetState(). Position gives the cursor location in window coordinates, buttons compare with ButtonState.Pressed, and ScrollWheelValue is cumulative, so subtract the previous value to get a per-frame scroll delta.

private MouseState _previousMouse;
private MouseState _currentMouse;

protected override void Update(GameTime gameTime)
{
    _previousMouse = _currentMouse;
    _currentMouse = Mouse.GetState();

    bool leftClicked = _currentMouse.LeftButton == ButtonState.Pressed &&
                       _previousMouse.LeftButton == ButtonState.Released;

    Point cursor = _currentMouse.Position;
    int scrollDelta = _currentMouse.ScrollWheelValue - _previousMouse.ScrollWheelValue;

    if (leftClicked)
    {
        _position = cursor.ToVector2();
    }

    base.Update(gameTime);
}

Gamepad state

Read a controller with GamePad.GetState(PlayerIndex.One). Check IsConnected before using values, then read thumbsticks, buttons, and triggers from the returned GamePadState. MonoGame thumbsticks use game-style coordinates: pushing the left stick up gives a positive Y value, which is inverted relative to screen space where Y increases downward.

protected override void Update(GameTime gameTime)
{
    GamePadState pad = GamePad.GetState(PlayerIndex.One);

    if (pad.IsConnected)
    {
        Vector2 move = pad.ThumbSticks.Left;
        move.Y = -move.Y; // Convert stick up to negative screen-space Y.

        bool jumpPressed = pad.Buttons.A == ButtonState.Pressed;
        bool menuHeld = pad.Buttons.Start == ButtonState.Pressed;

        float accelerate = pad.Triggers.Right;
        float brake = pad.Triggers.Left;
    }

    base.Update(gameTime);
}

💡 For one-shot gamepad actions, use the same previous/current edge pattern as the keyboard: pressed this frame, released last frame.

Complete example: keyboard and mouse control

This complete Game1.cs generates a 1x1 pixel texture in code, stretches it into a box, moves it with WASD or arrow keys using delta time, jumps on a single Space press, and teleports the box to the cursor on a single left-click.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

public class Game1 : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch;
    private Texture2D _pixel;

    private KeyboardState _previousKeyboard;
    private KeyboardState _currentKeyboard;
    private MouseState _previousMouse;
    private MouseState _currentMouse;

    private Vector2 _position = new Vector2(120, 120);
    private Vector2 _velocity;
    private float _jumpTimer;

    private const int BoxSize = 48;
    private const float MoveSpeed = 260f;
    private const float JumpImpulse = -420f;
    private const float Gravity = 1100f;
    private const float GroundY = 320f;

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        _pixel = new Texture2D(GraphicsDevice, 1, 1);
        _pixel.SetData(new[] { Color.White });
    }

    protected override void Update(GameTime gameTime)
    {
        _previousKeyboard = _currentKeyboard;
        _previousMouse = _currentMouse;
        _currentKeyboard = Keyboard.GetState();
        _currentMouse = Mouse.GetState();

        if (_currentKeyboard.IsKeyDown(Keys.Escape))
            Exit();

        float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
        Vector2 direction = Vector2.Zero;

        if (_currentKeyboard.IsKeyDown(Keys.W) || _currentKeyboard.IsKeyDown(Keys.Up)) direction.Y -= 1f;
        if (_currentKeyboard.IsKeyDown(Keys.S) || _currentKeyboard.IsKeyDown(Keys.Down)) direction.Y += 1f;
        if (_currentKeyboard.IsKeyDown(Keys.A) || _currentKeyboard.IsKeyDown(Keys.Left)) direction.X -= 1f;
        if (_currentKeyboard.IsKeyDown(Keys.D) || _currentKeyboard.IsKeyDown(Keys.Right)) direction.X += 1f;

        if (direction != Vector2.Zero)
            direction.Normalize();

        _position += direction * MoveSpeed * dt;

        bool spacePressed = _currentKeyboard.IsKeyDown(Keys.Space) &&
                            _previousKeyboard.IsKeyUp(Keys.Space);
        bool onGround = _position.Y >= GroundY;

        if (spacePressed && onGround)
        {
            _velocity.Y = JumpImpulse;
            _jumpTimer = 0.2f;
        }

        _velocity.Y += Gravity * dt;
        _position.Y += _velocity.Y * dt;
        _jumpTimer = MathHelper.Max(0f, _jumpTimer - dt);

        if (_position.Y > GroundY)
        {
            _position.Y = GroundY;
            _velocity.Y = 0f;
        }

        bool leftClicked = _currentMouse.LeftButton == ButtonState.Pressed &&
                           _previousMouse.LeftButton == ButtonState.Released;

        if (leftClicked)
        {
            _position = _currentMouse.Position.ToVector2() - new Vector2(BoxSize / 2f);
            _velocity = Vector2.Zero;
        }

        ClampToWindow();
        base.Update(gameTime);
    }

    private void ClampToWindow()
    {
        Rectangle bounds = GraphicsDevice.Viewport.Bounds;
        _position.X = MathHelper.Clamp(_position.X, 0f, bounds.Width - BoxSize);
        _position.Y = MathHelper.Clamp(_position.Y, 0f, bounds.Height - BoxSize);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        Color boxColour = _jumpTimer > 0f ? Color.Gold : Color.OrangeRed;

        _spriteBatch.Begin();
        _spriteBatch.Draw(_pixel, new Rectangle((int)_position.X, (int)_position.Y, BoxSize, BoxSize), boxColour);
        _spriteBatch.End();

        base.Draw(gameTime);
    }
}

Run it with dotnet run. Hold movement keys to move, press Space once to jump, and left-click anywhere in the window to teleport the box.

dotnet build
dotnet run

Common pitfalls

Next steps

Need larger tap targets or higher contrast? Toggle enhanced spacing and high contrast in the header. Your preference is saved in session storage.

🎓 Test Your Knowledge

Check your understanding of the key concepts from this tutorial.

Which class lets you read the current state of the keyboard?

How do you detect a key being pressed for exactly one frame (not held)?

What does Mouse.GetState() return?

What namespace contains the keyboard and mouse input classes?