Detecting Keyboard Gestures Using Monogame

April 07, 2014

Recently, I got to the stage where I had just about finished one of my games, and suddenly realised that I had done nothing to deal with mouse gestures. Dealing with touch (which I have previously blogged about) can blind you to other input forms. Exactly how you deal with this will, of course, depend on what you’re trying to achieve.

Simple press and click - Touch

Let’s have a look at a piece of code to handle touch gestures:


private TouchCollection \_currentTouch;

private void ProcessTouchInput()
{
    if (keypad == null) return;

    \_currentTouch = TouchPanel.GetState();
    foreach( var touch in \_currentTouch)
    {
        if (touch.State == TouchLocationState.Released)
        {
            keysPressed += keypad.GetKeyAt(touch.Position);
        }
    }
}

Okay, so this was ripped right out of the middle of a game. Most of it doesn’t matter, but `keypad` represents a customised on-screen keypad. So, to find out what’s been pressed, we simply call TouchPanel.GetState() and iterate the returned collection.

If we find that the TouchLocationState to be released then it must have been touched (how can you release something that was never touched?).

It then builds up a string containing the keys.

… And with mouse

Okay, here’s the first thing; this statement:

If we find that the TouchLocationState to be released then it must have been touched (how can you release something that was never touched?).

Is no longer true. `Released` where a mouse state is concerned is simply an absence of being pressed!

So, we need to track what has happened, and when:


private DateTime? \_mousePressed;
private MouseState \_currentMouse;

private void ProcessMouseInput()
{
    if (keypad == null) return;
           
    \_currentMouse = Mouse.GetState();
    if (\_currentMouse.LeftButton == ButtonState.Pressed)
         \_mousePressed = DateTime.Now;

    if (\_currentMouse.LeftButton == ButtonState.Released
       && \_mousePressed != null)
    {
        \_mousePressed = null;
        keysPressed += keypad.GetKeyAt( new Vector2(\_currentMouse.X, \_currentMouse.Y));               
    }
}


Okay, so as far as mouse press is concerned, we have to roll our own. This isn’t particularly complicated code though.

What about gestures?

You can do gestures; it’s not straight-forward and, again, it’s not particularly well supported.

Here’s how I handle dragging:


MouseState mouseState = Mouse .GetState();
Vector2 mousePosition = new Vector2(mouseState.X, mouseState.Y);

// First, work out where and whether the drag has started
if (mouseState.LeftButton == ButtonState.Pressed)
{
    if (\_lastPressed == null || \_lastPressed < \_lastRelease)
        dragStart = null;

    // The current mouse location must be in a place where it 
    // is acceptable to start a drag
    if (CheckLocation(mousePosition, acceptableDragStart, 60))
    {
         if (dragStart == null)
         {
              dragStart = mousePosition;
         }
    }

    \_lastPressed = DateTime.Now;
}

// Now work out when the drag is released
if (mouseState.LeftButton == ButtonState.Released)
{
    // For some reason, ButtonState.Released is
    // always flagged unless the button is pressed
    if (\_lastPressed == null) return;
        \_lastPressed = null;

    // Set the last release here
    \_lastRelease = DateTime.Now;

    // Do whatever it is that you need for drag release
    DragRelease(mousePosition, dragStart, PlayerPosition);
}    



So the code is in two distinct sections; the first being to determine is, and where, the mouse was pressed, and to store that information; the latter to determine if and where it was released.

Double tap, hold, and other such gestures make this code more and more complex. It feels a lot like the main issue is the Released state.



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2022