David Redding

Blog? What blog? Theres no blog to see here. Please move along
posts - 55, comments - 53, trackbacks - 31

My Links

News

!--Avatar-->
www.flickr.com
This is a Flickr badge showing public photos from dredding. Make your own badge here.

Twitter












Tag Cloud

Article Categories

Archives

Post Categories

.Net Resource Sites

.Net User Group Sites

Language Resource Sites

Mason Sites

My Bloggin' Buddies

XNA Key Repeat is like a Jack russell Frickin' Terrier

    Ok, so, we have to take a step away from the event driven architecture that were used to in "Normal" .net Apps.   For instance, you would expect a keyboard key press to be presented as such in your standard form.  But not in XNA...oh no.. event driven items are for sissies when it comes to writing for the XBox or XNA PC.  But who cares right? I mean, all we have to do is check if a button or thumbstick or whatever is down during the update method right?  Well that’s where it gets tricky. 

    From what I’ve seen, the update method fires once about ever 16 milliseconds.  And everything I’ve read shows the update method polling the different input (If you do it different, I’d love to see some alternatives).  Now, for allot of game inputs, that’s fine.  Anything you would essentially say "While this button is pushed: x+=1" to there is no trouble.  But what if you need to handle some kind of state changed on button press like "When this button is  pressed ask your wife for a beer".  I don't know about you, but if I ask my wife for a beer once every 16 milliseconds, I’m likely to just get whacked upside the head with one...every 16 milliseconds.

    The solution?  Keep track of when the button was pressed.  One method I’ve implemented goes something like this

ButtonState _prevstate;

protected void CheckGamepadInput(GameTime time)
{

if (_prevstate != ButtonState.Pressed)
{
      Beer b = GetBeerFromWife<SamAdams>();
      Me.Drink<Beer>(b);
}

if (GamePad.GetState(PlayerIndex.One).Buttons.Start == ButtonState.Pressed)
_currentPhase = eTurnPhases.RobotsMove;

}

GamePadState s = GamePad.GetState(PlayerIndex.One);
if(s.Buttons.A == ButtonState.Pressed ||
   s.Buttons.B == ButtonState.Pressed ||
   s.Buttons.X == ButtonState.Pressed ||
   s.Buttons.Y == ButtonState.Pressed)
{             
      _prevstate = ButtonState.Pressed;
}
else
{
     
_prevstate = ButtonState.Released;
}

    Well, that’s one way to do it for the game pad.  But you’re going to have the same trouble with the keyboard.  One suggestion would be to use the above method, but against the keys your using for input.  But what if you’re using like...2 billion key combinations, like maybe you have many many different beers available to you and you have these crazy key combos.  Well, in that situation we could (totally shooting from the hip here) do something like this.

long totalTime = 0;
int keyrepeateFrequency = 100;
List<KeyLastPressed> lastPressed = new List<KeyLastPressed>();

protected void CheckKeyboardInput(GameTime time)
{
      KeyboardState ks = Keyboard.GetState();
      Keys[] k = ks.GetPressedKeys();
      totalTime += time.ElapsedGameTime.Milliseconds;
      //flush the collection of combos that have served
      //their time and are ready for pushing again
      long difference = keyrepeateFrequency + 1;
      while (difference >= keyrepeateFrequency && lastPressed.Count>0)
      {
          difference = totalTime - lastPressed[0].timePressed;
          if (difference >= keyrepeateFrequency)
          lastPressed.Remove(lastPressed[0]);
      }
      //Now we have to check to see if the keys were
      //pressing have been pressed less than keyrepeateFrequence ago
      bool hasBeenPressedRecently = false;
      foreach (KeyLastPressed klp in lastPressed)
      {
         //could benifit from a better checking algorithim, like hashing
         //plus were not "really" checking for key combos
         if (klp.pressedKeys.Length == k.Length)
         {
             foreach (Keys keyin in k)
             {
                 foreach (Keys keyprev in klp.pressedKeys)
                 {
                     if (keyin == keyprev)
                     {
                         hasBeenPressedRecently = true;
                         break;
                     }
                 }
                 if (hasBeenPressedRecently)
                     break;
             }
         }
      }
      //so has it been pressed recently?
      if (!hasBeenPressedRecently && k.Length>0)
      {
      //nope it hasnt, so add it to our collection
      lastPressed.Add(new KeyLastPressed(k, totalTime));
      //Now do game key press shenanigans.
      //DoGameStuffWithKeys(k);
      }
           if (ks.IsKeyDown(Keys.Escape))
              this.Quit();
}
public struct KeyLastPressed
{
      public Keys[] pressedKeys;
      public long timePressed;
      public KeyLastPressed(Keys[] pk, long tp)
      {
         pressedKeys = pk;
         timePressed = tp;
      }
}

    So it's something to get you started.  Now, optimize the shit out of that and tell me what you come up with.  This code is very similar to what I'm using in a current little project I’m playing with, so if you, gentle reader, have a solution that you've developed, please share! 

    Remember, I’ll be speaking at the Day of .Net this year (the second one, on October 20th) on XNA, so if you like the code here, come get some more, if you hate it, come publicly ridicule me.  Either way, see you there!

 

Print | posted on Sunday, September 23, 2007 2:07 PM | Filed Under [ XNA ]


Feedback

# re: XNA Key Repeat is like a Jack russell Frickin' Terrier

On an aside. You can also do timer based key/button presses. It would be as simple as When was Anybutton pressed last, is the diff between current time and that time greater than the wait time? If so go ahead and allow another *press. 9/24/2007 2:07 AM | Dave

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 3 and 6 and type the answer here:

Powered by: