4
\$\begingroup\$

In my game, I need to detect whether any buttons/keys are pressed at all before I try to process any input. This is easy enough for keys since the XNA library provides a GetPressedKeys method on the KeyboardState class:

private void ProcessKeyboard()
{
    keyboardState = Keyboard.GetState( PlayerIndex.One );

    if ( !keyboardState.GetPressedKeys().Any() )
    {
        DoAction( Actions.Idle );
        return;
    }

    foreach ( var binding in keyboardBindings )
    {
        if ( keyboardState.IsKeyDown( binding.Key ) )
        {
            DoAction( binding.Value );
        }
    }
}

However, it doesn't provide a corollary for the GamePadState class:

private void ProcessGamePad()
{
    gamePadState = GamePad.GetState( PlayerIndex.One );

    // detect no input?

    foreach ( var binding in gamePadBindings )
    {
        if ( gamePadState.IsButtonDown( binding.Key ) )
        {
            DoAction( binding.Value );
        }
    }
}

I tried whipping up my own extension method but, as I'm not really familiar with enums or arrays in C#, I'm afraid it's a little sloppy:

public static class GamePadStateExtensions
{
    public static Buttons[] GetPressedButtons( this GamePadState gamePadState )
    {
        // 25 is the number of items in Buttons
        var pressedButtons = new Buttons[25];

        foreach ( Buttons button in Enum.GetValues( typeof(Buttons) ) )
        {
            if ( gamePadState.IsButtonDown( button ) )
            {
                pressedButtons[pressedButtons.Count()] = button;
            }
        }

        return pressedButtons;
    }
}

Would I be better off doing something else with ProcessKeyboard and/or ProcessGamePad? How can I improve GetPressedButtons?

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Rather than using an array to return you should use a List<T> which can be easily appended to:

public static List<Buttons> GetPressedButtons( this GamePadState gamePadState )
{
    var pressedButtons = List<Buttons>();

    foreach ( Buttons button in Enum.GetValues( typeof(Buttons) ) )
    {
        if ( gamePadState.IsButtonDown( button ) )
        {
            pressedButtons.Add(button);
        }
    }

    return pressedButtons;
}

However this can be simplified still with the use of LINQ. The for loop basically selects all buttons which are pressed:

public static List<Buttons> GetPressedButtons(this GamePadState gamePadState)
{
    return Enum.GetValues(typeof(Buttons))
               .Cast<Buttons>()
               .Where(b => gamePadState.IsButtonDown(b))
               .ToList();
}

The Cast<T> is required because GetValue returns a non-generic Array.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is an interesting way of doing it. In my case, I mapped both XNA buttons and keys to my own Buttons flags enum, which I'd argue is more efficient than a list to store button state. \$\endgroup\$
    – Magus
    Feb 21, 2014 at 17:39
4
\$\begingroup\$

There is one problem you may face with that code,

I did a lot of input work with XNA and you may find that getting the "down" keys is not actually what you want. it is not the same thing as the pressed keys, that will fire constantly as you hold the key, triggering a new event each time.E.g if you press a to open a chat dialog and a again to close it, just pressing the button once may trigger like 6 actual presses and flicker the popup.

For getting the PRESSED buttons you would actually need to store the prior state of the gamepad and assert if it was not pressed last frame e.g

public static List<Buttons> GetPressedButtons(this GamePadState gamePadState,GamePadState previousState)
{
    return Enum.GetValues(typeof(Buttons))
               .Cast<Buttons>()
               .Where(b => gamePadState.IsButtonDown(b) && !previousState.IsButtonDown(b))
               .ToList();
}

As for getting the prior state that is simply a matter of having a prior state variable and at the end of your update cycle set previousState = currentState .

P.s I robbed @ChrisWue 's very clean snippet for my example.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Another way around this is to only collect changes (or only collect duplicates every n ticks) into some kind of collection and only observe on specific patterns, rather than tying input directly to the frame. It can be complicated, but it's somewhat more flexible - it can be extended to recognize patterns. \$\endgroup\$
    – Magus
    Feb 21, 2014 at 17:48
  • \$\begingroup\$ I had not thought that far ahead yet! I will surely need to do this. \$\endgroup\$ Feb 21, 2014 at 19:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.