Tales Framework for XNA: Input and Focus

Posted 10/18/2008 @ 10:00:00 PM by Joseph Molnar
Filed under: Programming , Tales Framework , Xbox 360 , XNA

In my last post I talked about controls and the control structure within the Tales Framework. In this post I will go over the input handling. The two most important ideas to understand are a) input gestures, which represent things like button presses, and b) how focus impacts input handling.

Input Gestures

When building a GameControl that takes input you need to bind a method to one or more input gestures that implement the IInputGesture interface.

The IInputGesture interface looks like this:

public interface IInputGesture {
    /// <summary>
    /// This method is used to check if we have input in a particular state.
    /// </summary>
    /// <param name="thePlayerIndex">The index of player that we are 
    /// checking input for.</param>
    /// <param name="theGameTime">The GameTime for the input update.</param>
    /// <returns>True if the expected input occurred.</returns>
    bool MatchGesture( PlayerIndex thePlayerIndex, GameTime theGameTime );
}

A gesture’s MatchGesture method simply checks some state, like a pressed button, for the specified player and if the state is true the method returns true.

For example, this is a simple gesture that detects if a button was literally just pressed:

public class ButtonDownGesture : IInputGesture {
    private readonly Buttons _buttons;

    /// <summary>
    /// Constructor taking the buttons in question.
    /// </summary>
    /// <param name="theButtons">The buttons to check to see if they
    /// were pressed.</param>
    public ButtonDownGesture( Buttons theButtons ) {
        _buttons = theButtons;
    }

    /// <summary>
    /// The buttons to check.
    /// </summary>
    public Buttons Buttons {
        get {
            return _buttons;
        }
    }

    /// <summary>
    /// This method checks to see if the configured buttons were
    /// just pressed.
    /// </summary>
    /// <param name="thePlayerIndex">The player index being checked.</param>
    /// <param name="theGameTime">The GameTime for the checking.</param>
    /// <returns>Returns true if the buttons were just pressed.</returns>
    public bool MatchGesture( 
        PlayerIndex thePlayerIndex, 
        GameTime theGameTime ) {
        
        bool returnValue = false;
        int index = ( int )thePlayerIndex;

        if( InputManager.CurrentGamePadStates[ index ].IsButtonDown( _buttons ) &&
            InputManager.PreviousGamePadStates[ index ].IsButtonUp( _buttons ) ) {
            returnValue = true;
        }

        return returnValue;
    }
}

The MatchGesture method uses the InputManager. The InputManager stores the current and previous states for gamepads and keyboards.

Developers can create their own gestures but the framework includes the critical gestures. There are simple gestures for detecting that a gamepad button or key was just pressed or just released, and there is support for more complicated gestures that only return true periodically while a user holds a button or key down.

Binding Gestures

So how do you use gestures in your controls or menus? GameControl has a protected method called BindGestures that takes a delegate and set of IInputGestures. By default the framework will automatically call the MatchGesture methods on the bound gestures and if one of the gestures indicates a match the delegate is called. Advanced framework users can also manually check gestures by using the InputManager.

This snippet of code from a sample spinner control shows the binding of gestures.

public class SampleSpinner : MenuItem {
    // <snip> 

    /// <summary>
    /// Initializes the left/right analog stick/d-pad 
    /// as the mechanism to select next/prev option.
    /// </summary>
    protected override void InitializeInput( ) {
        //base.InitializeInput( );
        BindInputGestures(
            this.HandlePrevValue,
            new IInputGesture[] {
                new ButtonDownGesture( Buttons.DPadLeft ),
                new ButtonDownGesture( Buttons.LeftThumbstickLeft )
            } );

        BindInputGestures(
            this.HandleNextValue,
            new IInputGesture[] {
                new ButtonDownGesture( Buttons.DPadRight ),
                new ButtonDownGesture( Buttons.LeftThumbstickRight )
            } );
    }   

    /// <summary>
    /// Handler for requesting the next option.
    /// </summary>
    protected void HandleNextValue( 
        GameControl theControl, 
        PlayerIndex thePlayer, 
        IInputGesture theGesture ) {
        // <snip> 
        // make it go to the next value
        // <snip> 
    }

    /// <summary>
    /// Handler for requesting the previous option.
    /// </summary>
    protected void HandlePrevValue( 
        GameControl theControl, 
        PlayerIndex thePlayer, 
        IInputGesture theGesture ) {
        // <snip> 
        // make it go to the previous value
        // <snip> 
    }
 
    // <snip> 
}

Input and Focus

Another important aspect of input, and making sure that your registered delegates are called, is understanding focus. The framework doesn’t call every single registered gesture for every single control, instead it checks the gestures for the controls that are in focus, and if a gesture isn’t matched it will traverse up through parent controls checking the registered gestures.

I said ‘checks the gestures for the controls that are in focus’. This implies more than one control can be in focus. This is true, however only one control can be in focus for each player. More details on this are discussed below in Advanced Focus.

To put a control in focus, you simply need to set its Focused property to true. Some of the high-level controls will automatically set the focus. For example, the ScreenManager will set the focus on Menus as they are made active while Menus will set the focus of MenuItems as a user moves between the menu options.

Advanced Focus

One of the advanced features for focus and input is the notion of FocusScope. While the default FocusScope will accept input from all players, you can create FocusScopes that control which players’ input will be accepted and processed. This means not only must a control be in focus, but a FocusScope must exist that allows input from the player. This is best explained with the following example.

It is common during game play that if a player hits the Start button the game will load a pause menu that pauses the game and only that same player can hit the Back button to close the pause menu. Input from other players is ignored.

This advanced ability can also be used to bind certain controls of a HUD to particular players in local multi-player games. If you want to use this ability I recommend you have a look at the FocusManager.

The above hopefully has given a good overview of input and focus handling. Additional posts regarding the framework will be coming soon.

Comments

Post a comment