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!