5
\$\begingroup\$

I'm working on picking up XNA and this was a confusing point for me.

KeyboardState keyState = Keyboard.GetState();
if (keyState.IsKeyDown(Keys.Left) || keyState.IsKeyDown(Keys.A))
{
    //Do stuff...
}

The book I'm using (Learning XNA 4.0, O'Rielly) says that this method accepts a bitwise OR series of keys, which I think should look like this...

KeyboardState keyState = Keyboard.GetState();
if (keyState.IsKeyDown(Keys.Left | Keys.A))
{
    //Do stuff...
}

But I can't get it work. I also tried using !IsKeyUp(... | ...) as it said that all keys had to be down for it to be true, but had no luck with that either. Ideas? Thanks.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Is it really worth it? Just use an extra keyState.IsKeyDown() with || \$\endgroup\$
    – indeed005
    Commented Sep 24, 2012 at 22:14
  • 1
    \$\begingroup\$ First of all your two blocks are not equivalent. "keyState.IsKeyDown(Keys.Left) && keyState.IsKeyDown(Keys.A)" for the top would make the blocks logically equivalent. Also could you describe what "But I can't get it work" means? Where is this code. Are threads involved? The second block it should be true iff A and Left are both pressed. Are you not experiencing this behavior? \$\endgroup\$ Commented Sep 24, 2012 at 22:15
  • \$\begingroup\$ I'm only trying to avoid extra code and method calls if I can - this seems like something that should be common with alternate keys in games? I know they're not equivalent, that is why I mentioned the IsKeyUp alternative. By "it doesn't work" I meant that I can't ever get it to be true - it doesn't matter if both keys are up, one key is down, or both keys is down. In one of those cases it should be true, but none of them were. \$\endgroup\$
    – jocull
    Commented Sep 24, 2012 at 22:49
  • 2
    \$\begingroup\$ a better thing to avoid than extra code is confusing code. The second block has confused you and has a higher probability of confusing others so in this case it should be avoided... Even if you get it to work as expected. \$\endgroup\$
    – Steve H
    Commented Sep 25, 2012 at 1:46
  • 2
    \$\begingroup\$ I have this book and just checked the section on Keyboard input. I saw absolutely nothing mentioning bitwise operators. However, there is a section about Gamepad input that claims you can use bitwise to pass multiple Buttons to the IsButtonDown method. \$\endgroup\$ Commented Sep 25, 2012 at 2:43

2 Answers 2

10
\$\begingroup\$

The XNA Keys struct is not marked with the Flags Attribute which means that it's not legal to | a load of keys together like that.

Keys keyCombo = Keys.Left | Keys.A;
Console.WriteLine(keyCombo);

Will print NumPad5, which is clearly not what you want here! You've never been able to do this in any version of XNA, I think the book is just wrong here :O

Copying from one of my own games, this is how I solved it:

#if WINDOWS
public static bool Any(this KeyboardState state, params Keys[] keys)
{
    return keys.Any(state.IsKeyDown);
}
#endif

And then you can just do:

KeyboardState k = Keyboard.GetState();

if (k.Any(Keys.Left, Keys.A))
    DoStuff();

However, as someone in the comments pointed out this method is bad practice on the xbox due to it allocating (params array allocation, and LINQ allocating an Enumerator). An alternative system for xbox is this:

Keys[] doStuffKeys; //Allocate an array of keys for this particular action

Initialize()
{
    doStuffKeyBinding = new[] { Keys.Left, Keys.A };
}

Update()
{
    KeyboardState k = Keyboard.GetState();

    if (k.Any(doStuffKeys)) //No allocations here!
        DoStuff();
}

The Any method needs modifying too:

#if XBOX
public static bool Any(this KeyboardState state, Keys[] keys)
{
    for (int i = 0; i < keys.Length; i++)
        if (state.IsKeyDown(keys[i]))
            return true;
    return false;
}
#endif

It's a while since I've done any work for the xbox, but this now shouldn't allocate.

\$\endgroup\$
9
  • \$\begingroup\$ Really like the use the extension method here. Thanks! I think that should be return keys.Any(x => state.IsKeyDown(x)); \$\endgroup\$
    – jocull
    Commented Sep 25, 2012 at 1:58
  • 1
    \$\begingroup\$ @jocull, I don't need x => state.IsKeyDown(x), the above compiles just fine :) \$\endgroup\$
    – Martin
    Commented Sep 25, 2012 at 4:40
  • 2
    \$\begingroup\$ +1 For extension methods and LINQ. And @jocull, That's right. It's like how events/delegates can be associated with a lambda expression, anonymous method, or a named method. \$\endgroup\$ Commented Sep 25, 2012 at 7:02
  • 2
    \$\begingroup\$ @Jocull, absolutely right. Any takes a Func<Keys, bool> and IsKeyDown is a Func<Keys, bool> so all is fine :D \$\endgroup\$
    – Martin
    Commented Sep 25, 2012 at 7:34
  • 3
    \$\begingroup\$ One minor note, if you are using XNA to develop on the xbox using params and linq will typically generate garbage, which is best avoided. It's not pretty but overloading the method with 1, 2, 3... parameters would get around this. \$\endgroup\$ Commented Sep 25, 2012 at 15:03
4
\$\begingroup\$

IsKeyDown takes an enum Key for its type. I don't see from the documentation that it can take multiple emums ORed together. Perhaps your book is wrong?

You could create your own method if you wanted the shortcut:

boolean anyOfTheseDown(KeyboardState keyState, params Keys[] keys) {
   boolean result = false;
   for (int i = 0; i < keys.Length; i++)
   {
       result |= keyState.IsKeyDown(keys[i]);
   }    
   return result;
}

Then use it like:

KeyboardState keyState = Keyboard.GetState();
if (anyOfTheseDown(keyState, Keys.Left, Keys.A))
{
    //Do stuff...
}

Code is untested.

\$\endgroup\$
3
  • \$\begingroup\$ I like this solution if bitwise is out of the question. Is the for loop considered unnecessary overhead? Would it be better to just write it twice with a traditional ||? Not trying to micro-optimize and scrutinize this too much, but genuinely curious about writing the best code I can. \$\endgroup\$
    – jocull
    Commented Sep 24, 2012 at 22:52
  • 2
    \$\begingroup\$ It depends, the method above will allow 1 or more keys to be checked. The for loop will not add any (noticeable) additional overhead for checking multiple key states. But you do appear to be pre-micro-optimizing :) If you worry this much about this level of detail, you'll never finish. \$\endgroup\$
    – House
    Commented Sep 24, 2012 at 22:58
  • \$\begingroup\$ @joculi For your case this seems the most convenient, doesn't it? Using enum flags would be more useful in checking for a group of keys being pressed exclusively, but making sure that each frame you build an "isDown" enum value of the keys that are down this frame to bitwise-compare to the requested key combination. But even that's still prematurely optimizing. \$\endgroup\$ Commented Sep 25, 2012 at 0:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .