Blog

Input Handling in XNA Games – Part 2

May 12

Written by:
Thursday, May 12, 2011  RssIcon

In the last post, we introduced the (still empty) input service interface. Now it is time to add a few functions. This blog post describes

  • how to detect button clicks and double-clicks,
  • how to add key repetition for held down keys,
  • how to enable mouse centering for relative mouse input.

Detecting Clicks and Double-Clicks

Using the XNA classes Keyboard, Mouse, and GamePad, we can get the device state (e.g. KeyboardState) and can check if a key or button is currently pressed or released. But we cannot directly query whether a key or button was released in the last frame and pressed down in this frame. And we cannot simply query if a button click belongs to a double-click.

Therefore, the input service provides methods to check if a button is down, up, pressed, or released:

  • IsDown() checks if a button is currently pressed down.
  • IsUp() checks if a button is currently released.
  • IsPressed() checks if a button was pressed in the current frame. (The button was up in the previous frame, but is down in the current frame.)
  • IsReleased() checks if a button was released in the current frame. (The button was down in the previous frame, but is up in the current frame.)
  • IsDoubleClick() can be used to check for double-clicks.

The method parameter will be a button or a key, e.g.

  bool IsReleased(Keys key);

XNA provides enumerations for Keys and gamepad Buttons, but not for mouse buttons – so this enumeration is a useful addition:

  public enum MouseButtons
  {
    Left,
    Middle,
    Right,
    XButton1,
    XButton2,
  }

The implementation of IsDown and IsUp is easy: We simply check the button state in the current device state. For IsPressed and IsReleased, we also have to check the device state of the last frame. The device states are retrieved and stored in InputManager.Update().

To detect double-clicks, InputManager.Update() must check all keys and buttons remembers the last pressed key/button and when it was pressed. If the same button is pressed again within a certain time limit, then we have a double-click. (Note: A click that was part of a double-click cannot be part of a second double-click. That means, if a button is pressed 3 times, we have a double-click followed by a single-click – not 2 double-clicks.)

Key Repetition

Key repetition is useful for keys in text boxes and for buttons in game menus:

  • When the key B is pressed in a text editor, a B appears immediately. After a short duration, if the key is still held down, more Bs appear with a configurable frequency.
  • When the left stick is pressed to the right in a horizontal game menu, the next menu item is selected. After a short duration, if the stick is still held to the right, the selection jumps from menu item to menu item.

To support key repetition, the IsPressed() methods have a second parameter, e.g.

  bool IsPressed(Keys key, bool useKeyRepetition);

If the second parameter is true, IsPressed returns true when the key was pressed down in this frame or when the key was pressed down long enough for key repetition. Every component, which processes input, can decide whether key repetition should be used.

In InputManager.Update() we already remember the last pressed keys and buttons and the press-time to detect double-clicks. The same information can be used to decide when to create virtual key presses.

Mouse Centering

Using the XNA MouseState, it is easy to get the absolute mouse position (relative to the game window). In some games, we are only interested in relative mouse movements, for example in first-person-shooter games where the relative mouse change is used to rotate the view. We get the relative mouse position simply be subtracting the mouse position of the last frame.

But this approach is limited – namely by the screen limits. The mouse cursor cannot be moved out of the screen and once the mouse cursor has reached, for instance, the left screen border, the mouse X position will not change if the user tries to move the mouse more to the left. This is not acceptable in a first-person-shooter game. To avoid this, we must make sure that the mouse has enough room to move in all directions in each frame. Therefore, InputManager.Update() resets the mouse position in each frame. We can set the mouse position to the center of the game window, or simply to fixed “mouse center”:

  public void Update(float deltaTime)
  { 
    … 
   
if
(EnableMouseCentering)
      
Mouse.SetPosition((int)Settings.MouseCenter.X,
                         (
int)Settings.MouseCenter.Y);

 
}

And the input service computes the relative mouse position when we need it:

  public Vector2F MousePositionDeltaRaw
  {
   
get
    {
     
if
(EnableMouseCentering)
       
return new Vector2F
(_newMouseState.X - Settings.MouseCenter.X,
_newMouseState.Y - Settings.MouseCenter.Y);
     
else
return new Vector2F(_newMouseState.X - _previousMouseState.X,
_newMouseState.Y - _previousMouseState.Y);
    }
}

Next

In the next blog posts, we will discuss how we are

  • using logical players to let the user play with any connected controller,
  • regulating the order of input handling and preemption if several components handle device input,
  • allowing the player to re-configure the input.

The full input service with documentation and class diagrams is available with our DigitalRune Game UI library.


Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel